Added an option to disable stacked axes in 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 PersistentSettings settings;
28 private readonly Plot plot;
29 private readonly PlotModel model;
30 private readonly TimeSpanAxis timeAxis = new TimeSpanAxis();
31 private readonly SortedDictionary<SensorType, LinearAxis> axes =
32 new SortedDictionary<SensorType, LinearAxis>();
34 private UserOption stackedAxes;
38 public PlotPanel(PersistentSettings settings) {
39 this.settings = settings;
40 this.model = CreatePlotModel();
42 this.plot = new Plot();
43 this.plot.Dock = DockStyle.Fill;
44 this.plot.Model = model;
45 this.plot.BackColor = Color.White;
46 this.plot.ContextMenu = CreateMenu();
51 this.Controls.Add(plot);
52 this.ResumeLayout(true);
55 public void SetCurrentSettings() {
56 settings.SetValue("plotPanel.MinTimeSpan", (float)timeAxis.ViewMinimum);
57 settings.SetValue("plotPanel.MaxTimeSpan", (float)timeAxis.ViewMaximum);
59 foreach (var axis in axes.Values) {
60 settings.SetValue("plotPanel.Min" + axis.Key, (float)axis.ViewMinimum);
61 settings.SetValue("plotPanel.Max" + axis.Key, (float)axis.ViewMaximum);
65 private ContextMenu CreateMenu() {
66 ContextMenu menu = new ContextMenu();
68 MenuItem stackedAxesMenuItem = new MenuItem("Stacked Axes");
69 stackedAxes = new UserOption("stackedAxes", true,
70 stackedAxesMenuItem, settings);
71 stackedAxes.Changed += (sender, e) => {
75 menu.MenuItems.Add(stackedAxesMenuItem);
77 MenuItem timeWindow = new MenuItem("Time Window");
78 MenuItem[] timeWindowMenuItems =
79 { new MenuItem("Auto",
80 (s, e) => { timeAxis.Zoom(0, double.NaN); InvalidatePlot(); }),
82 (s, e) => { timeAxis.Zoom(0, 5 * 60); InvalidatePlot(); }),
83 new MenuItem("10 min",
84 (s, e) => { timeAxis.Zoom(0, 10 * 60); InvalidatePlot(); }),
85 new MenuItem("20 min",
86 (s, e) => { timeAxis.Zoom(0, 20 * 60); InvalidatePlot(); }),
87 new MenuItem("30 min",
88 (s, e) => { timeAxis.Zoom(0, 30 * 60); InvalidatePlot(); }),
89 new MenuItem("45 min",
90 (s, e) => { timeAxis.Zoom(0, 45 * 60); InvalidatePlot(); }),
92 (s, e) => { timeAxis.Zoom(0, 60 * 60); InvalidatePlot(); }),
94 (s, e) => { timeAxis.Zoom(0, 1.5 * 60 * 60); InvalidatePlot(); }),
96 (s, e) => { timeAxis.Zoom(0, 2 * 60 * 60); InvalidatePlot(); }),
98 (s, e) => { timeAxis.Zoom(0, 3 * 60 * 60); InvalidatePlot(); }),
100 (s, e) => { timeAxis.Zoom(0, 6 * 60 * 60); InvalidatePlot(); }),
102 (s, e) => { timeAxis.Zoom(0, 12 * 60 * 60); InvalidatePlot(); }),
104 (s, e) => { timeAxis.Zoom(0, 24 * 60 * 60); InvalidatePlot(); }) };
105 foreach (MenuItem mi in timeWindowMenuItems)
106 timeWindow.MenuItems.Add(mi);
107 menu.MenuItems.Add(timeWindow);
112 private PlotModel CreatePlotModel() {
114 timeAxis.Position = AxisPosition.Bottom;
115 timeAxis.MajorGridlineStyle = LineStyle.Solid;
116 timeAxis.MajorGridlineThickness = 1;
117 timeAxis.MajorGridlineColor = OxyColor.FromRgb(192, 192, 192);
118 timeAxis.MinorGridlineStyle = LineStyle.Solid;
119 timeAxis.MinorGridlineThickness = 1;
120 timeAxis.MinorGridlineColor = OxyColor.FromRgb(232, 232, 232);
121 timeAxis.StartPosition = 1;
122 timeAxis.EndPosition = 0;
123 timeAxis.MinimumPadding = 0;
124 timeAxis.MaximumPadding = 0;
125 timeAxis.AbsoluteMinimum = 0;
126 timeAxis.Minimum = 0;
127 timeAxis.AbsoluteMaximum = 24 * 60 * 60;
129 settings.GetValue("plotPanel.MinTimeSpan", 0.0f),
130 settings.GetValue("plotPanel.MaxTimeSpan", 10.0f * 60));
131 timeAxis.StringFormat = "h:mm";
133 var units = new Dictionary<SensorType, string>();
134 units.Add(SensorType.Voltage, "V");
135 units.Add(SensorType.Clock, "MHz");
136 units.Add(SensorType.Temperature, "°C");
137 units.Add(SensorType.Load, "%");
138 units.Add(SensorType.Fan, "RPM");
139 units.Add(SensorType.Flow, "L/h");
140 units.Add(SensorType.Control, "%");
141 units.Add(SensorType.Level, "%");
142 units.Add(SensorType.Factor, "1");
143 units.Add(SensorType.Power, "W");
144 units.Add(SensorType.Data, "GB");
146 foreach (SensorType type in Enum.GetValues(typeof(SensorType))) {
147 var axis = new LinearAxis();
148 axis.Position = AxisPosition.Left;
149 axis.MajorGridlineStyle = LineStyle.Solid;
150 axis.MajorGridlineThickness = 1;
151 axis.MajorGridlineColor = timeAxis.MajorGridlineColor;
152 axis.MinorGridlineStyle = LineStyle.Solid;
153 axis.MinorGridlineThickness = 1;
154 axis.MinorGridlineColor = timeAxis.MinorGridlineColor;
155 axis.AxislineStyle = LineStyle.Solid;
156 axis.Title = type.ToString();
157 axis.Key = type.ToString();
160 settings.GetValue("plotPanel.Min" + axis.Key, float.NaN),
161 settings.GetValue("plotPanel.Max" + axis.Key, float.NaN));
163 if (units.ContainsKey(type))
164 axis.Unit = units[type];
165 axes.Add(type, axis);
168 var model = new PlotModel();
169 model.Axes.Add(timeAxis);
170 foreach (var axis in axes.Values)
171 model.Axes.Add(axis);
172 model.PlotMargins = new OxyThickness(0);
173 model.IsLegendVisible = false;
178 public void SetSensors(List<ISensor> sensors,
179 IDictionary<ISensor, Color> colors) {
180 this.model.Series.Clear();
182 ListSet<SensorType> types = new ListSet<SensorType>();
184 foreach (ISensor sensor in sensors) {
185 var series = new LineSeries();
186 series.ItemsSource = sensor.Values.Select(value => new DataPoint {
187 X = (now - value.Time).TotalSeconds, Y = value.Value
189 series.Color = colors[sensor].ToOxyColor();
190 series.StrokeThickness = 1;
191 series.YAxisKey = axes[sensor.SensorType].Key;
192 series.Title = sensor.Hardware.Name + " " + sensor.Name;
193 this.model.Series.Add(series);
195 types.Add(sensor.SensorType);
198 foreach (var pair in axes.Reverse()) {
199 var axis = pair.Value;
201 axis.IsAxisVisible = types.Contains(type);
204 UpdateAxesPosition();
208 private void UpdateAxesPosition() {
209 if (stackedAxes.Value) {
210 var count = axes.Values.Count(axis => axis.IsAxisVisible);
212 foreach (var pair in axes.Reverse()) {
213 var axis = pair.Value;
215 axis.StartPosition = start;
216 var delta = axis.IsAxisVisible ? 1.0 / count : 0;
218 axis.EndPosition = start;
219 axis.PositionTier = 0;
220 axis.MajorGridlineStyle = LineStyle.Solid;
221 axis.MinorGridlineStyle = LineStyle.Solid;
225 foreach (var pair in axes.Reverse()) {
226 var axis = pair.Value;
228 axis.StartPosition = 0;
229 axis.EndPosition = 1;
230 axis.PositionTier = tier;
231 if (axis.IsAxisVisible)
233 axis.MajorGridlineStyle = LineStyle.None;
234 axis.MinorGridlineStyle = LineStyle.None;
239 public void InvalidatePlot() {
240 this.now = DateTime.UtcNow;
241 this.plot.InvalidatePlot(true);