Improved the data compression for storing the recorded sensor values in the config file.
3 This Source Code Form is subject to the terms of the Mozilla Public
4 License, v. 2.0. If a copy of the MPL was not distributed with this
5 file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 Copyright (C) 2009-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Collections.Generic;
13 using System.ComponentModel;
15 using System.Drawing.Drawing2D;
16 using System.Windows.Forms;
17 using OpenHardwareMonitor.Hardware;
19 namespace OpenHardwareMonitor.GUI {
20 public class PlotPanel : UserControl {
22 private PersistentSettings settings;
25 private List<ISensor> clocks = new List<ISensor>();
26 private List<ISensor> temperatures = new List<ISensor>();
27 private List<ISensor> fans = new List<ISensor>();
28 private IDictionary<ISensor, Color> colors;
30 private StringFormat centerlower;
31 private StringFormat centerleft;
32 private StringFormat lowerleft;
33 private Brush lightBrush;
36 private UserRadioGroup timeWindowRadioGroup;
38 public PlotPanel(PersistentSettings settings) {
39 this.settings = settings;
41 this.SetStyle(ControlStyles.DoubleBuffer |
42 ControlStyles.UserPaint |
43 ControlStyles.AllPaintingInWmPaint |
44 ControlStyles.ResizeRedraw, true);
49 centerlower = new StringFormat();
50 centerlower.Alignment = StringAlignment.Center;
51 centerlower.LineAlignment = StringAlignment.Near;
53 centerleft = new StringFormat();
54 centerleft.Alignment = StringAlignment.Far;
55 centerleft.LineAlignment = StringAlignment.Center;
57 lowerleft = new StringFormat();
58 lowerleft.Alignment = StringAlignment.Far;
59 lowerleft.LineAlignment = StringAlignment.Near;
61 lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245));
62 lightPen = new Pen(Color.FromArgb(200, 200, 200));
65 private void CreateContextMenu() {
66 MenuItem timeWindow = new MenuItem("Time Scale");
68 MenuItem[] timeWindowMenuItems =
69 { new MenuItem("Auto"),
70 new MenuItem("5 min"),
71 new MenuItem("10 min"),
72 new MenuItem("20 min"),
73 new MenuItem("30 min"),
74 new MenuItem("45 min"),
76 new MenuItem("1.5 h"),
81 new MenuItem("24 h") };
83 foreach (MenuItem mi in timeWindowMenuItems)
84 timeWindow.MenuItems.Add(mi);
86 timeWindowRadioGroup = new UserRadioGroup("timeWindow", 0,
87 timeWindowMenuItems, settings);
89 this.ContextMenu = new ContextMenu();
90 this.ContextMenu.MenuItems.Add(timeWindow);
93 private List<float> GetTemperatureGrid() {
95 float? minTempNullable = null;
96 float? maxTempNullable = null;
97 foreach (ISensor sensor in temperatures) {
98 IEnumerable<SensorValue> values = sensor.Values;
99 foreach (SensorValue value in values) {
100 if (!float.IsNaN(value.Value)) {
101 if (!minTempNullable.HasValue || minTempNullable > value.Value)
102 minTempNullable = value.Value;
103 if (!maxTempNullable.HasValue || maxTempNullable < value.Value)
104 maxTempNullable = value.Value;
108 if (!minTempNullable.HasValue) {
109 minTempNullable = 20;
110 maxTempNullable = 30;
113 float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10;
114 float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10;
115 if (maxTemp == minTemp)
118 int countTempMax = 4;
119 float deltaTemp = maxTemp - minTemp;
120 int countTemp = (int)Math.Round(deltaTemp / 2);
121 if (countTemp > countTempMax)
122 countTemp = (int)Math.Round(deltaTemp / 5);
123 if (countTemp > countTempMax)
124 countTemp = (int)Math.Round(deltaTemp / 10);
125 if (countTemp > countTempMax)
126 countTemp = (int)Math.Round(deltaTemp / 20);
128 List<float> grid = new List<float>(countTemp + 1);
129 for (int i = 0; i <= countTemp; i++) {
130 grid.Add(minTemp + i * deltaTemp / countTemp);
135 private List<float> GetTimeGrid() {
138 if (timeWindowRadioGroup.Value == 0) { // Auto
140 if (temperatures.Count > 0) {
141 IEnumerator<SensorValue> enumerator =
142 temperatures[0].Values.GetEnumerator();
143 if (enumerator.MoveNext()) {
144 maxTime = (float)(now - enumerator.Current.Time).TotalMinutes;
149 { 5, 10, 20, 30, 45, 60, 90, 120, 180, 360, 720, 1440 };
151 maxTime = maxTimes[timeWindowRadioGroup.Value - 1];
156 while (deltaTime + 1 <= maxTime && deltaTime < 10)
158 while (deltaTime + 2 <= maxTime && deltaTime < 30)
160 while (deltaTime + 5 <= maxTime && deltaTime < 100)
162 while (deltaTime + 50 <= maxTime && deltaTime < 1000)
164 while (deltaTime + 100 <= maxTime && deltaTime < 10000)
167 List<float> grid = new List<float>(countTime + 1);
168 for (int i = 0; i <= countTime; i++) {
169 grid.Add(i * deltaTime / countTime);
174 protected override void OnPaint(PaintEventArgs e) {
175 now = DateTime.UtcNow - new TimeSpan(0, 0, 4);
177 List<float> timeGrid = GetTimeGrid();
178 List<float> tempGrid = GetTemperatureGrid();
180 Graphics g = e.Graphics;
183 new RectangleF(0, 0, Bounds.Width, Bounds.Height);
188 float w = r.Width - ml - mr;
193 float h = r.Height - mt - mb;
195 float leftScaleSpace = 5;
196 float bottomScaleSpace = 5;
198 g.Clear(Color.White);
200 if (w > 0 && h > 0) {
201 g.FillRectangle(lightBrush, x0, y0, w, h);
203 g.SmoothingMode = SmoothingMode.HighQuality;
204 for (int i = 0; i < timeGrid.Count; i++) {
205 float x = x0 + i * w / (timeGrid.Count - 1);
206 g.DrawLine(lightPen, x, y0, x, y0 + h);
209 for (int i = 0; i < tempGrid.Count; i++) {
210 float y = y0 + i * h / (tempGrid.Count - 1);
211 g.DrawLine(lightPen, x0, y, x0 + w, y);
214 float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0];
215 float deltaTime = timeGrid[timeGrid.Count - 1];
216 foreach (ISensor sensor in temperatures) {
217 using (Pen pen = new Pen(colors[sensor])) {
218 IEnumerable<SensorValue> values = sensor.Values;
219 PointF last = new PointF();
221 foreach (SensorValue v in values) {
222 if (!float.IsNaN(v.Value)) {
223 PointF point = new PointF(
224 x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime,
225 y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp);
227 g.DrawLine(pen, last, point);
237 g.SmoothingMode = SmoothingMode.None;
238 g.FillRectangle(Brushes.White, 0, 0, x0, r.Height);
239 g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w,
242 for (int i = 1; i < timeGrid.Count; i++) {
243 float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1);
244 g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x,
245 y0 + h + bottomScaleSpace, centerlower);
248 for (int i = 0; i < tempGrid.Count - 1; i++) {
249 float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1);
250 g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black,
251 x0 - leftScaleSpace, y, centerleft);
254 g.SmoothingMode = SmoothingMode.HighQuality;
255 g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0,
257 g.DrawString("[min]", Font, Brushes.Black, x0 + w,
258 y0 + h + bottomScaleSpace, lowerleft);
262 public void SetSensors(List<ISensor> sensors,
263 IDictionary<ISensor, Color> colors) {
264 this.colors = colors;
265 List<ISensor> clocks = new List<ISensor>();
266 List<ISensor> temperatures = new List<ISensor>();
267 List<ISensor> fans = new List<ISensor>();
268 foreach (ISensor sensor in sensors)
269 switch (sensor.SensorType) {
270 case SensorType.Clock: clocks.Add(sensor); break;
271 case SensorType.Temperature: temperatures.Add(sensor); break;
272 case SensorType.Fan: fans.Add(sensor); break;
274 this.clocks = clocks;
275 this.temperatures = temperatures;