Added Fahrenheit support to the plot.
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-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
12 using System.Collections.Generic;
15 using System.Windows.Forms;
16 using OpenHardwareMonitor.Hardware;
19 using OxyPlot.WindowsForms;
21 using OpenHardwareMonitor.Collections;
23 namespace OpenHardwareMonitor.GUI {
24 public class PlotPanel : UserControl {
26 private readonly PersistentSettings settings;
27 private readonly UnitManager unitManager;
29 private readonly Plot plot;
30 private readonly PlotModel model;
31 private readonly TimeSpanAxis timeAxis = new TimeSpanAxis();
32 private readonly SortedDictionary<SensorType, LinearAxis> axes =
33 new SortedDictionary<SensorType, LinearAxis>();
35 private UserOption stackedAxes;
39 public PlotPanel(PersistentSettings settings, UnitManager unitManager) {
40 this.settings = settings;
41 this.unitManager = unitManager;
43 this.model = CreatePlotModel();
45 this.plot = new Plot();
46 this.plot.Dock = DockStyle.Fill;
47 this.plot.Model = model;
48 this.plot.BackColor = Color.White;
49 this.plot.ContextMenu = CreateMenu();
54 this.Controls.Add(plot);
55 this.ResumeLayout(true);
58 public void SetCurrentSettings() {
59 settings.SetValue("plotPanel.MinTimeSpan", (float)timeAxis.ViewMinimum);
60 settings.SetValue("plotPanel.MaxTimeSpan", (float)timeAxis.ViewMaximum);
62 foreach (var axis in axes.Values) {
63 settings.SetValue("plotPanel.Min" + axis.Key, (float)axis.ViewMinimum);
64 settings.SetValue("plotPanel.Max" + axis.Key, (float)axis.ViewMaximum);
68 private ContextMenu CreateMenu() {
69 ContextMenu menu = new ContextMenu();
71 MenuItem stackedAxesMenuItem = new MenuItem("Stacked Axes");
72 stackedAxes = new UserOption("stackedAxes", true,
73 stackedAxesMenuItem, settings);
74 stackedAxes.Changed += (sender, e) => {
78 menu.MenuItems.Add(stackedAxesMenuItem);
80 MenuItem timeWindow = new MenuItem("Time Window");
81 MenuItem[] timeWindowMenuItems =
82 { new MenuItem("Auto",
83 (s, e) => { timeAxis.Zoom(0, double.NaN); InvalidatePlot(); }),
85 (s, e) => { timeAxis.Zoom(0, 5 * 60); InvalidatePlot(); }),
86 new MenuItem("10 min",
87 (s, e) => { timeAxis.Zoom(0, 10 * 60); InvalidatePlot(); }),
88 new MenuItem("20 min",
89 (s, e) => { timeAxis.Zoom(0, 20 * 60); InvalidatePlot(); }),
90 new MenuItem("30 min",
91 (s, e) => { timeAxis.Zoom(0, 30 * 60); InvalidatePlot(); }),
92 new MenuItem("45 min",
93 (s, e) => { timeAxis.Zoom(0, 45 * 60); InvalidatePlot(); }),
95 (s, e) => { timeAxis.Zoom(0, 60 * 60); InvalidatePlot(); }),
97 (s, e) => { timeAxis.Zoom(0, 1.5 * 60 * 60); InvalidatePlot(); }),
99 (s, e) => { timeAxis.Zoom(0, 2 * 60 * 60); InvalidatePlot(); }),
101 (s, e) => { timeAxis.Zoom(0, 3 * 60 * 60); InvalidatePlot(); }),
103 (s, e) => { timeAxis.Zoom(0, 6 * 60 * 60); InvalidatePlot(); }),
105 (s, e) => { timeAxis.Zoom(0, 12 * 60 * 60); InvalidatePlot(); }),
107 (s, e) => { timeAxis.Zoom(0, 24 * 60 * 60); InvalidatePlot(); }) };
108 foreach (MenuItem mi in timeWindowMenuItems)
109 timeWindow.MenuItems.Add(mi);
110 menu.MenuItems.Add(timeWindow);
115 private PlotModel CreatePlotModel() {
117 timeAxis.Position = AxisPosition.Bottom;
118 timeAxis.MajorGridlineStyle = LineStyle.Solid;
119 timeAxis.MajorGridlineThickness = 1;
120 timeAxis.MajorGridlineColor = OxyColor.FromRgb(192, 192, 192);
121 timeAxis.MinorGridlineStyle = LineStyle.Solid;
122 timeAxis.MinorGridlineThickness = 1;
123 timeAxis.MinorGridlineColor = OxyColor.FromRgb(232, 232, 232);
124 timeAxis.StartPosition = 1;
125 timeAxis.EndPosition = 0;
126 timeAxis.MinimumPadding = 0;
127 timeAxis.MaximumPadding = 0;
128 timeAxis.AbsoluteMinimum = 0;
129 timeAxis.Minimum = 0;
130 timeAxis.AbsoluteMaximum = 24 * 60 * 60;
132 settings.GetValue("plotPanel.MinTimeSpan", 0.0f),
133 settings.GetValue("plotPanel.MaxTimeSpan", 10.0f * 60));
134 timeAxis.StringFormat = "h:mm";
136 var units = new Dictionary<SensorType, string>();
137 units.Add(SensorType.Voltage, "V");
138 units.Add(SensorType.Clock, "MHz");
139 units.Add(SensorType.Temperature, "°C");
140 units.Add(SensorType.Load, "%");
141 units.Add(SensorType.Fan, "RPM");
142 units.Add(SensorType.Flow, "L/h");
143 units.Add(SensorType.Control, "%");
144 units.Add(SensorType.Level, "%");
145 units.Add(SensorType.Factor, "1");
146 units.Add(SensorType.Power, "W");
147 units.Add(SensorType.Data, "GB");
149 foreach (SensorType type in Enum.GetValues(typeof(SensorType))) {
150 var axis = new LinearAxis();
151 axis.Position = AxisPosition.Left;
152 axis.MajorGridlineStyle = LineStyle.Solid;
153 axis.MajorGridlineThickness = 1;
154 axis.MajorGridlineColor = timeAxis.MajorGridlineColor;
155 axis.MinorGridlineStyle = LineStyle.Solid;
156 axis.MinorGridlineThickness = 1;
157 axis.MinorGridlineColor = timeAxis.MinorGridlineColor;
158 axis.AxislineStyle = LineStyle.Solid;
159 axis.Title = type.ToString();
160 axis.Key = type.ToString();
163 settings.GetValue("plotPanel.Min" + axis.Key, float.NaN),
164 settings.GetValue("plotPanel.Max" + axis.Key, float.NaN));
166 if (units.ContainsKey(type))
167 axis.Unit = units[type];
168 axes.Add(type, axis);
171 var model = new PlotModel();
172 model.Axes.Add(timeAxis);
173 foreach (var axis in axes.Values)
174 model.Axes.Add(axis);
175 model.PlotMargins = new OxyThickness(0);
176 model.IsLegendVisible = false;
181 public void SetSensors(List<ISensor> sensors,
182 IDictionary<ISensor, Color> colors) {
183 this.model.Series.Clear();
185 ListSet<SensorType> types = new ListSet<SensorType>();
187 foreach (ISensor sensor in sensors) {
188 var series = new LineSeries();
189 if (sensor.SensorType == SensorType.Temperature) {
190 series.ItemsSource = sensor.Values.Select(value => new DataPoint {
191 X = (now - value.Time).TotalSeconds,
192 Y = unitManager.TemperatureUnit == TemperatureUnit.Celsius ?
193 value.Value : UnitManager.CelsiusToFahrenheit(value.Value).Value
196 series.ItemsSource = sensor.Values.Select(value => new DataPoint {
197 X = (now - value.Time).TotalSeconds, Y = value.Value
200 series.Color = colors[sensor].ToOxyColor();
201 series.StrokeThickness = 1;
202 series.YAxisKey = axes[sensor.SensorType].Key;
203 series.Title = sensor.Hardware.Name + " " + sensor.Name;
204 this.model.Series.Add(series);
206 types.Add(sensor.SensorType);
209 foreach (var pair in axes.Reverse()) {
210 var axis = pair.Value;
212 axis.IsAxisVisible = types.Contains(type);
215 UpdateAxesPosition();
219 private void UpdateAxesPosition() {
220 if (stackedAxes.Value) {
221 var count = axes.Values.Count(axis => axis.IsAxisVisible);
223 foreach (var pair in axes.Reverse()) {
224 var axis = pair.Value;
226 axis.StartPosition = start;
227 var delta = axis.IsAxisVisible ? 1.0 / count : 0;
229 axis.EndPosition = start;
230 axis.PositionTier = 0;
231 axis.MajorGridlineStyle = LineStyle.Solid;
232 axis.MinorGridlineStyle = LineStyle.Solid;
236 foreach (var pair in axes.Reverse()) {
237 var axis = pair.Value;
239 if (axis.IsAxisVisible) {
240 axis.StartPosition = 0;
241 axis.EndPosition = 1;
242 axis.PositionTier = tier;
245 axis.StartPosition = 0;
246 axis.EndPosition = 0;
247 axis.PositionTier = 0;
249 axis.MajorGridlineStyle = LineStyle.None;
250 axis.MinorGridlineStyle = LineStyle.None;
256 public void InvalidatePlot() {
257 this.now = DateTime.UtcNow;
259 foreach (var pair in axes) {
260 var axis = pair.Value;
262 if (type == SensorType.Temperature)
263 axis.Unit = unitManager.TemperatureUnit == TemperatureUnit.Celsius ?
267 this.plot.InvalidatePlot(true);