moel@158: /* moel@1: moel@344: This Source Code Form is subject to the terms of the Mozilla Public moel@344: License, v. 2.0. If a copy of the MPL was not distributed with this moel@344: file, You can obtain one at http://mozilla.org/MPL/2.0/. moel@1: moel@344: Copyright (C) 2009-2011 Michael Möller moel@344: moel@1: */ moel@1: moel@1: using System; moel@1: using System.Collections.Generic; moel@1: using System.ComponentModel; moel@1: using System.Drawing; moel@1: using System.Drawing.Drawing2D; moel@1: using System.Windows.Forms; moel@1: using OpenHardwareMonitor.Hardware; moel@1: moel@1: namespace OpenHardwareMonitor.GUI { moel@158: public class PlotPanel : UserControl { moel@1: moel@326: private PersistentSettings settings; moel@326: moel@1: private DateTime now; moel@1: private List clocks = new List(); moel@1: private List temperatures = new List(); moel@1: private List fans = new List(); moel@1: private IDictionary colors; moel@1: moel@1: private StringFormat centerlower; moel@1: private StringFormat centerleft; moel@1: private StringFormat lowerleft; moel@1: private Brush lightBrush; moel@1: private Pen lightPen; moel@1: moel@326: private UserRadioGroup timeWindowRadioGroup; moel@326: moel@326: public PlotPanel(PersistentSettings settings) { moel@326: this.settings = settings; moel@326: moel@1: this.SetStyle(ControlStyles.DoubleBuffer | moel@1: ControlStyles.UserPaint | moel@158: ControlStyles.AllPaintingInWmPaint | moel@1: ControlStyles.ResizeRedraw, true); moel@1: this.UpdateStyles(); moel@1: moel@326: CreateContextMenu(); moel@326: moel@1: centerlower = new StringFormat(); moel@1: centerlower.Alignment = StringAlignment.Center; moel@1: centerlower.LineAlignment = StringAlignment.Near; moel@1: moel@1: centerleft = new StringFormat(); moel@1: centerleft.Alignment = StringAlignment.Far; moel@1: centerleft.LineAlignment = StringAlignment.Center; moel@1: moel@1: lowerleft = new StringFormat(); moel@1: lowerleft.Alignment = StringAlignment.Far; moel@1: lowerleft.LineAlignment = StringAlignment.Near; moel@1: moel@1: lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245)); moel@1: lightPen = new Pen(Color.FromArgb(200, 200, 200)); moel@1: } moel@1: moel@326: private void CreateContextMenu() { moel@326: MenuItem timeWindow = new MenuItem("Time Scale"); moel@326: moel@326: MenuItem[] timeWindowMenuItems = moel@326: { new MenuItem("Auto"), moel@326: new MenuItem("5 min"), moel@326: new MenuItem("10 min"), moel@326: new MenuItem("20 min"), moel@326: new MenuItem("30 min"), moel@326: new MenuItem("45 min"), moel@326: new MenuItem("1 h"), moel@326: new MenuItem("1.5 h"), moel@326: new MenuItem("2 h"), moel@326: new MenuItem("3 h"), moel@326: new MenuItem("6 h"), moel@326: new MenuItem("12 h"), moel@326: new MenuItem("24 h") }; moel@326: moel@326: foreach (MenuItem mi in timeWindowMenuItems) moel@326: timeWindow.MenuItems.Add(mi); moel@326: moel@326: timeWindowRadioGroup = new UserRadioGroup("timeWindow", 0, moel@326: timeWindowMenuItems, settings); moel@326: moel@326: this.ContextMenu = new ContextMenu(); moel@326: this.ContextMenu.MenuItems.Add(timeWindow); moel@326: } moel@326: moel@1: private List GetTemperatureGrid() { moel@1: moel@1: float? minTempNullable = null; moel@1: float? maxTempNullable = null; moel@1: foreach (ISensor sensor in temperatures) { moel@159: IEnumerable values = sensor.Values; moel@159: foreach (SensorValue value in values) { moel@298: if (!float.IsNaN(value.Value)) { moel@298: if (!minTempNullable.HasValue || minTempNullable > value.Value) moel@298: minTempNullable = value.Value; moel@298: if (!maxTempNullable.HasValue || maxTempNullable < value.Value) moel@298: maxTempNullable = value.Value; moel@298: } moel@1: } moel@1: } moel@1: if (!minTempNullable.HasValue) { moel@1: minTempNullable = 20; moel@1: maxTempNullable = 30; moel@1: } moel@1: moel@1: float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10; moel@1: float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10; moel@158: if (maxTemp == minTemp) moel@158: maxTemp += 10; moel@158: moel@1: int countTempMax = 4; moel@1: float deltaTemp = maxTemp - minTemp; moel@1: int countTemp = (int)Math.Round(deltaTemp / 2); moel@1: if (countTemp > countTempMax) moel@1: countTemp = (int)Math.Round(deltaTemp / 5); moel@1: if (countTemp > countTempMax) moel@1: countTemp = (int)Math.Round(deltaTemp / 10); moel@1: if (countTemp > countTempMax) moel@1: countTemp = (int)Math.Round(deltaTemp / 20); moel@1: moel@1: List grid = new List(countTemp + 1); moel@1: for (int i = 0; i <= countTemp; i++) { moel@1: grid.Add(minTemp + i * deltaTemp / countTemp); moel@1: } moel@1: return grid; moel@1: } moel@1: moel@1: private List GetTimeGrid() { moel@1: moel@326: float maxTime; moel@326: if (timeWindowRadioGroup.Value == 0) { // Auto moel@326: maxTime = 5; moel@326: if (temperatures.Count > 0) { moel@326: IEnumerator enumerator = moel@326: temperatures[0].Values.GetEnumerator(); moel@326: if (enumerator.MoveNext()) { moel@326: maxTime = (float)(now - enumerator.Current.Time).TotalMinutes; moel@326: } moel@1: } moel@326: } else { moel@326: float[] maxTimes = moel@326: { 5, 10, 20, 30, 45, 60, 90, 120, 180, 360, 720, 1440 }; moel@326: moel@326: maxTime = maxTimes[timeWindowRadioGroup.Value - 1]; moel@1: } moel@1: moel@1: int countTime = 10; moel@1: float deltaTime = 5; moel@326: while (deltaTime + 1 <= maxTime && deltaTime < 10) moel@1: deltaTime += 1; moel@326: while (deltaTime + 2 <= maxTime && deltaTime < 30) moel@1: deltaTime += 2; moel@326: while (deltaTime + 5 <= maxTime && deltaTime < 100) moel@1: deltaTime += 5; moel@326: while (deltaTime + 50 <= maxTime && deltaTime < 1000) moel@298: deltaTime += 50; moel@326: while (deltaTime + 100 <= maxTime && deltaTime < 10000) moel@298: deltaTime += 100; moel@1: moel@1: List grid = new List(countTime + 1); moel@1: for (int i = 0; i <= countTime; i++) { moel@1: grid.Add(i * deltaTime / countTime); moel@1: } moel@1: return grid; moel@1: } moel@1: moel@1: protected override void OnPaint(PaintEventArgs e) { moel@314: now = DateTime.UtcNow - new TimeSpan(0, 0, 4); moel@1: moel@1: List timeGrid = GetTimeGrid(); moel@1: List tempGrid = GetTemperatureGrid(); moel@1: moel@1: Graphics g = e.Graphics; moel@1: moel@1: RectangleF r = moel@1: new RectangleF(0, 0, Bounds.Width, Bounds.Height); moel@1: moel@1: float ml = 40; moel@1: float mr = 15; moel@1: float x0 = r.X + ml; moel@1: float w = r.Width - ml - mr; moel@1: moel@1: float mt = 15; moel@1: float mb = 28; moel@1: float y0 = r.Y + mt; moel@1: float h = r.Height - mt - mb; moel@1: moel@1: float leftScaleSpace = 5; moel@1: float bottomScaleSpace = 5; moel@158: moel@1: g.Clear(Color.White); moel@1: moel@1: if (w > 0 && h > 0) { moel@1: g.FillRectangle(lightBrush, x0, y0, w, h); moel@1: moel@1: g.SmoothingMode = SmoothingMode.HighQuality; moel@1: for (int i = 0; i < timeGrid.Count; i++) { moel@1: float x = x0 + i * w / (timeGrid.Count - 1); moel@1: g.DrawLine(lightPen, x, y0, x, y0 + h); moel@1: } moel@1: moel@1: for (int i = 0; i < tempGrid.Count; i++) { moel@1: float y = y0 + i * h / (tempGrid.Count - 1); moel@1: g.DrawLine(lightPen, x0, y, x0 + w, y); moel@1: } moel@1: moel@1: float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0]; moel@1: float deltaTime = timeGrid[timeGrid.Count - 1]; moel@1: foreach (ISensor sensor in temperatures) { moel@1: using (Pen pen = new Pen(colors[sensor])) { moel@159: IEnumerable values = sensor.Values; moel@1: PointF last = new PointF(); moel@1: bool first = true; moel@298: foreach (SensorValue v in values) { moel@298: if (!float.IsNaN(v.Value)) { moel@298: PointF point = new PointF( moel@298: x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime, moel@298: y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp); moel@298: if (!first) moel@298: g.DrawLine(pen, last, point); moel@298: last = point; moel@298: first = false; moel@298: } else { moel@298: first = true; moel@298: } moel@1: } moel@1: } moel@1: } moel@1: moel@1: g.SmoothingMode = SmoothingMode.None; moel@1: g.FillRectangle(Brushes.White, 0, 0, x0, r.Height); moel@158: g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w, moel@1: r.Height); moel@1: moel@1: for (int i = 1; i < timeGrid.Count; i++) { moel@158: float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1); moel@158: g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x, moel@1: y0 + h + bottomScaleSpace, centerlower); moel@1: } moel@1: moel@1: for (int i = 0; i < tempGrid.Count - 1; i++) { moel@1: float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1); moel@158: g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black, moel@1: x0 - leftScaleSpace, y, centerleft); moel@1: } moel@1: moel@1: g.SmoothingMode = SmoothingMode.HighQuality; moel@1: g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0, moel@158: lowerleft); moel@1: g.DrawString("[min]", Font, Brushes.Black, x0 + w, moel@158: y0 + h + bottomScaleSpace, lowerleft); moel@1: } moel@1: } moel@1: moel@158: public void SetSensors(List sensors, moel@158: IDictionary colors) { moel@1: this.colors = colors; moel@1: List clocks = new List(); moel@1: List temperatures = new List(); moel@1: List fans = new List(); moel@1: foreach (ISensor sensor in sensors) moel@1: switch (sensor.SensorType) { moel@1: case SensorType.Clock: clocks.Add(sensor); break; moel@1: case SensorType.Temperature: temperatures.Add(sensor); break; moel@1: case SensorType.Fan: fans.Add(sensor); break; moel@1: } moel@1: this.clocks = clocks; moel@1: this.temperatures = temperatures; moel@1: this.fans = fans; moel@1: Invalidate(); moel@1: } moel@1: moel@1: } moel@1: }