# HG changeset patch # User moel.mich # Date 1370794139 0 # Node ID d1f25b504845faad4c98b626ed332cd386370fa6 # Parent 19848d079a33d4a4f2d2c2fbd2d38da74b462404 Added the new OxyPlot based plot implementation. diff -r 19848d079a33 -r d1f25b504845 Collections/ListSet.cs --- a/Collections/ListSet.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/Collections/ListSet.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,7 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2010 Michael Möller + Copyright (C) 2009-2013 Michael Möller */ @@ -48,5 +48,11 @@ IEnumerator IEnumerable.GetEnumerator() { return list.GetEnumerator(); } + + public int Count { + get { + return list.Count; + } + } } } diff -r 19848d079a33 -r d1f25b504845 GUI/AboutBox.Designer.cs --- a/GUI/AboutBox.Designer.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/GUI/AboutBox.Designer.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,7 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2012 Michael Möller + Copyright (C) 2009-2013 Michael Möller */ @@ -84,7 +84,7 @@ this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(273, 13); this.label2.TabIndex = 3; - this.label2.Text = "Copyright © 2009-2012 Michael Möller and contributers. "; + this.label2.Text = "Copyright © 2009-2013 Michael Möller and contributers. "; // // label3 // diff -r 19848d079a33 -r d1f25b504845 GUI/MainForm.cs --- a/GUI/MainForm.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/GUI/MainForm.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,7 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2012 Michael Möller + Copyright (C) 2009-2013 Michael Möller Copyright (C) 2010 Paul Werelds Copyright (C) 2012 Prince Samuel @@ -459,8 +459,7 @@ int colorIndex = 0; foreach (TreeNodeAdv node in treeView.AllNodes) { SensorNode sensorNode = node.Tag as SensorNode; - if (sensorNode != null && - sensorNode.Sensor.SensorType == SensorType.Temperature) { + if (sensorNode != null) { if (sensorNode.Plot) { colors.Add(sensorNode.Sensor, plotColorPalette[colorIndex % plotColorPalette.Length]); @@ -484,9 +483,7 @@ private void nodeCheckBox_IsVisibleValueNeeded(object sender, NodeControlValueEventArgs e) { SensorNode node = e.Node.Tag as SensorNode; - e.Value = (node != null) && - (node.Sensor.SensorType == SensorType.Temperature) && - plotMenuItem.Checked; + e.Value = (node != null) && plotMenuItem.Checked; } private void exitClick(object sender, EventArgs e) { @@ -496,7 +493,7 @@ private void timer_Tick(object sender, EventArgs e) { computer.Accept(updateVisitor); treeView.Invalidate(); - plotPanel.Invalidate(); + plotPanel.InvalidatePlot(); systemTray.Redraw(); if (gadget != null) gadget.Redraw(); @@ -506,6 +503,7 @@ } private void SaveConfiguration() { + plotPanel.SetCurrentSettings(); foreach (TreeColumn column in treeView.Columns) settings.SetValue("treeView.Columns." + column.Header + ".Width", column.Width); diff -r 19848d079a33 -r d1f25b504845 GUI/PlotPanel.cs --- a/GUI/PlotPanel.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/GUI/PlotPanel.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,277 +4,200 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2011 Michael Möller + Copyright (C) 2009-2013 Michael Möller */ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; -using System.Drawing.Drawing2D; +using System.Linq; using System.Windows.Forms; using OpenHardwareMonitor.Hardware; +using OxyPlot; +using OxyPlot.Axes; +using OxyPlot.WindowsForms; +using OxyPlot.Series; +using OpenHardwareMonitor.Collections; namespace OpenHardwareMonitor.GUI { public class PlotPanel : UserControl { private PersistentSettings settings; + private readonly Plot plot; + private readonly PlotModel model; + private readonly TimeSpanAxis timeAxis = new TimeSpanAxis(); + private readonly SortedDictionary axes = + new SortedDictionary(); + private DateTime now; - private List clocks = new List(); - private List temperatures = new List(); - private List fans = new List(); - private IDictionary colors; - - private StringFormat centerlower; - private StringFormat centerleft; - private StringFormat lowerleft; - private Brush lightBrush; - private Pen lightPen; - - private UserRadioGroup timeWindowRadioGroup; public PlotPanel(PersistentSettings settings) { this.settings = settings; + this.model = CreatePlotModel(); - this.SetStyle(ControlStyles.DoubleBuffer | - ControlStyles.UserPaint | - ControlStyles.AllPaintingInWmPaint | - ControlStyles.ResizeRedraw, true); - this.UpdateStyles(); + this.plot = new Plot(); + this.plot.Dock = DockStyle.Fill; + this.plot.Model = model; + this.plot.BackColor = Color.White; + this.plot.ContextMenu = new ContextMenu(); + this.plot.ContextMenu.MenuItems.Add(CreateMenu()); - CreateContextMenu(); - - centerlower = new StringFormat(); - centerlower.Alignment = StringAlignment.Center; - centerlower.LineAlignment = StringAlignment.Near; - - centerleft = new StringFormat(); - centerleft.Alignment = StringAlignment.Far; - centerleft.LineAlignment = StringAlignment.Center; - - lowerleft = new StringFormat(); - lowerleft.Alignment = StringAlignment.Far; - lowerleft.LineAlignment = StringAlignment.Near; - - lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245)); - lightPen = new Pen(Color.FromArgb(200, 200, 200)); + this.SuspendLayout(); + this.Controls.Add(plot); + this.ResumeLayout(true); } - private void CreateContextMenu() { - MenuItem timeWindow = new MenuItem("Time Scale"); - - MenuItem[] timeWindowMenuItems = - { new MenuItem("Auto"), - new MenuItem("5 min"), - new MenuItem("10 min"), - new MenuItem("20 min"), - new MenuItem("30 min"), - new MenuItem("45 min"), - new MenuItem("1 h"), - new MenuItem("1.5 h"), - new MenuItem("2 h"), - new MenuItem("3 h"), - new MenuItem("6 h"), - new MenuItem("12 h"), - new MenuItem("24 h") }; + public void SetCurrentSettings() { + settings.SetValue("plotPanel.MinTimeSpan", (float)timeAxis.ViewMinimum); + settings.SetValue("plotPanel.MaxTimeSpan", (float)timeAxis.ViewMaximum); + + foreach (var axis in axes.Values) { + settings.SetValue("plotPanel.Min" + axis.Key, (float)axis.ViewMinimum); + settings.SetValue("plotPanel.Max" + axis.Key, (float)axis.ViewMaximum); + } + } + + private MenuItem CreateMenu() { + MenuItem timeWindow = new MenuItem("Time Window"); + + MenuItem[] timeWindowMenuItems = + { new MenuItem("Auto", + (s, e) => { timeAxis.Zoom(0, double.NaN); InvalidatePlot(); }), + new MenuItem("5 min", + (s, e) => { timeAxis.Zoom(0, 5 * 60); InvalidatePlot(); }), + new MenuItem("10 min", + (s, e) => { timeAxis.Zoom(0, 10 * 60); InvalidatePlot(); }), + new MenuItem("20 min", + (s, e) => { timeAxis.Zoom(0, 20 * 60); InvalidatePlot(); }), + new MenuItem("30 min", + (s, e) => { timeAxis.Zoom(0, 30 * 60); InvalidatePlot(); }), + new MenuItem("45 min", + (s, e) => { timeAxis.Zoom(0, 45 * 60); InvalidatePlot(); }), + new MenuItem("1 h", + (s, e) => { timeAxis.Zoom(0, 60 * 60); InvalidatePlot(); }), + new MenuItem("1.5 h", + (s, e) => { timeAxis.Zoom(0, 1.5 * 60 * 60); InvalidatePlot(); }), + new MenuItem("2 h", + (s, e) => { timeAxis.Zoom(0, 2 * 60 * 60); InvalidatePlot(); }), + new MenuItem("3 h", + (s, e) => { timeAxis.Zoom(0, 3 * 60 * 60); InvalidatePlot(); }), + new MenuItem("6 h", + (s, e) => { timeAxis.Zoom(0, 6 * 60 * 60); InvalidatePlot(); }), + new MenuItem("12 h", + (s, e) => { timeAxis.Zoom(0, 12 * 60 * 60); InvalidatePlot(); }), + new MenuItem("24 h", + (s, e) => { timeAxis.Zoom(0, 24 * 60 * 60); InvalidatePlot(); }) }; foreach (MenuItem mi in timeWindowMenuItems) timeWindow.MenuItems.Add(mi); - timeWindowRadioGroup = new UserRadioGroup("timeWindow", 0, - timeWindowMenuItems, settings); - - this.ContextMenu = new ContextMenu(); - this.ContextMenu.MenuItems.Add(timeWindow); + return timeWindow; } - private List GetTemperatureGrid() { + private PlotModel CreatePlotModel() { - float? minTempNullable = null; - float? maxTempNullable = null; - foreach (ISensor sensor in temperatures) { - IEnumerable values = sensor.Values; - foreach (SensorValue value in values) { - if (!float.IsNaN(value.Value)) { - if (!minTempNullable.HasValue || minTempNullable > value.Value) - minTempNullable = value.Value; - if (!maxTempNullable.HasValue || maxTempNullable < value.Value) - maxTempNullable = value.Value; - } - } - } - if (!minTempNullable.HasValue) { - minTempNullable = 20; - maxTempNullable = 30; + timeAxis.Position = AxisPosition.Bottom; + timeAxis.MajorGridlineStyle = LineStyle.Solid; + timeAxis.MajorGridlineThickness = 1; + timeAxis.MajorGridlineColor = OxyColor.FromRgb(192, 192, 192); + timeAxis.MinorGridlineStyle = LineStyle.Solid; + timeAxis.MinorGridlineThickness = 1; + timeAxis.MinorGridlineColor = OxyColor.FromRgb(232, 232, 232); + timeAxis.StartPosition = 1; + timeAxis.EndPosition = 0; + timeAxis.MinimumPadding = 0; + timeAxis.MaximumPadding = 0; + timeAxis.AbsoluteMinimum = 0; + timeAxis.Minimum = 0; + timeAxis.AbsoluteMaximum = 24 * 60 * 60; + timeAxis.Zoom( + settings.GetValue("plotPanel.MinTimeSpan", 0.0f), + settings.GetValue("plotPanel.MaxTimeSpan", 10.0f * 60)); + timeAxis.StringFormat = "h:mm"; + + var units = new Dictionary(); + units.Add(SensorType.Voltage, "V"); + units.Add(SensorType.Clock, "MHz"); + units.Add(SensorType.Temperature, "°C"); + units.Add(SensorType.Load, "%"); + units.Add(SensorType.Fan, "RPM"); + units.Add(SensorType.Flow, "L/h"); + units.Add(SensorType.Control, "%"); + units.Add(SensorType.Level, "%"); + units.Add(SensorType.Factor, "1"); + units.Add(SensorType.Power, "W"); + units.Add(SensorType.Data, "GB"); + + foreach (SensorType type in Enum.GetValues(typeof(SensorType))) { + var axis = new LinearAxis(); + axis.Position = AxisPosition.Left; + axis.MajorGridlineStyle = LineStyle.Solid; + axis.MajorGridlineThickness = 1; + axis.MajorGridlineColor = timeAxis.MajorGridlineColor; + axis.MinorGridlineStyle = LineStyle.Solid; + axis.MinorGridlineThickness = 1; + axis.MinorGridlineColor = timeAxis.MinorGridlineColor; + axis.Title = type.ToString(); + axis.Key = type.ToString(); + + axis.Zoom( + settings.GetValue("plotPanel.Min" + axis.Key, float.NaN), + settings.GetValue("plotPanel.Max" + axis.Key, float.NaN)); + + if (units.ContainsKey(type)) + axis.Unit = units[type]; + axes.Add(type, axis); } - float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10; - float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10; - if (maxTemp == minTemp) - maxTemp += 10; + var model = new PlotModel(); + model.Axes.Add(timeAxis); + foreach (var axis in axes.Values) + model.Axes.Add(axis); + model.PlotMargins = new OxyThickness(0); + model.IsLegendVisible = false; - int countTempMax = 4; - float deltaTemp = maxTemp - minTemp; - int countTemp = (int)Math.Round(deltaTemp / 2); - if (countTemp > countTempMax) - countTemp = (int)Math.Round(deltaTemp / 5); - if (countTemp > countTempMax) - countTemp = (int)Math.Round(deltaTemp / 10); - if (countTemp > countTempMax) - countTemp = (int)Math.Round(deltaTemp / 20); - - List grid = new List(countTemp + 1); - for (int i = 0; i <= countTemp; i++) { - grid.Add(minTemp + i * deltaTemp / countTemp); - } - return grid; - } - - private List GetTimeGrid() { - - float maxTime; - if (timeWindowRadioGroup.Value == 0) { // Auto - maxTime = 5; - if (temperatures.Count > 0) { - IEnumerator enumerator = - temperatures[0].Values.GetEnumerator(); - if (enumerator.MoveNext()) { - maxTime = (float)(now - enumerator.Current.Time).TotalMinutes; - } - } - } else { - float[] maxTimes = - { 5, 10, 20, 30, 45, 60, 90, 120, 180, 360, 720, 1440 }; - - maxTime = maxTimes[timeWindowRadioGroup.Value - 1]; - } - - int countTime = 10; - float deltaTime = 5; - while (deltaTime + 1 <= maxTime && deltaTime < 10) - deltaTime += 1; - while (deltaTime + 2 <= maxTime && deltaTime < 30) - deltaTime += 2; - while (deltaTime + 5 <= maxTime && deltaTime < 100) - deltaTime += 5; - while (deltaTime + 50 <= maxTime && deltaTime < 1000) - deltaTime += 50; - while (deltaTime + 100 <= maxTime && deltaTime < 10000) - deltaTime += 100; - - List grid = new List(countTime + 1); - for (int i = 0; i <= countTime; i++) { - grid.Add(i * deltaTime / countTime); - } - return grid; - } - - protected override void OnPaint(PaintEventArgs e) { - now = DateTime.UtcNow - new TimeSpan(0, 0, 4); - - List timeGrid = GetTimeGrid(); - List tempGrid = GetTemperatureGrid(); - - Graphics g = e.Graphics; - - RectangleF r = - new RectangleF(0, 0, Bounds.Width, Bounds.Height); - - float ml = 40; - float mr = 15; - float x0 = r.X + ml; - float w = r.Width - ml - mr; - - float mt = 15; - float mb = 28; - float y0 = r.Y + mt; - float h = r.Height - mt - mb; - - float leftScaleSpace = 5; - float bottomScaleSpace = 5; - - g.Clear(Color.White); - - if (w > 0 && h > 0) { - g.FillRectangle(lightBrush, x0, y0, w, h); - - g.SmoothingMode = SmoothingMode.HighQuality; - for (int i = 0; i < timeGrid.Count; i++) { - float x = x0 + i * w / (timeGrid.Count - 1); - g.DrawLine(lightPen, x, y0, x, y0 + h); - } - - for (int i = 0; i < tempGrid.Count; i++) { - float y = y0 + i * h / (tempGrid.Count - 1); - g.DrawLine(lightPen, x0, y, x0 + w, y); - } - - float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0]; - float deltaTime = timeGrid[timeGrid.Count - 1]; - foreach (ISensor sensor in temperatures) { - using (Pen pen = new Pen(colors[sensor])) { - IEnumerable values = sensor.Values; - PointF last = new PointF(); - bool first = true; - foreach (SensorValue v in values) { - if (!float.IsNaN(v.Value)) { - PointF point = new PointF( - x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime, - y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp); - if (!first) - g.DrawLine(pen, last, point); - last = point; - first = false; - } else { - first = true; - } - } - } - } - - g.SmoothingMode = SmoothingMode.None; - g.FillRectangle(Brushes.White, 0, 0, x0, r.Height); - g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w, - r.Height); - - for (int i = 1; i < timeGrid.Count; i++) { - float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1); - g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x, - y0 + h + bottomScaleSpace, centerlower); - } - - for (int i = 0; i < tempGrid.Count - 1; i++) { - float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1); - g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black, - x0 - leftScaleSpace, y, centerleft); - } - - g.SmoothingMode = SmoothingMode.HighQuality; - g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0, - lowerleft); - g.DrawString("[min]", Font, Brushes.Black, x0 + w, - y0 + h + bottomScaleSpace, lowerleft); - } + return model; } public void SetSensors(List sensors, IDictionary colors) { - this.colors = colors; - List clocks = new List(); - List temperatures = new List(); - List fans = new List(); - foreach (ISensor sensor in sensors) - switch (sensor.SensorType) { - case SensorType.Clock: clocks.Add(sensor); break; - case SensorType.Temperature: temperatures.Add(sensor); break; - case SensorType.Fan: fans.Add(sensor); break; - } - this.clocks = clocks; - this.temperatures = temperatures; - this.fans = fans; - Invalidate(); + this.model.Series.Clear(); + + ListSet types = new ListSet(); + + foreach (ISensor sensor in sensors) { + var series = new LineSeries(); + series.ItemsSource = sensor.Values.Select(value => new DataPoint { + X = (now - value.Time).TotalSeconds, Y = value.Value + }); + series.Color = colors[sensor].ToOxyColor(); + series.StrokeThickness = 1; + series.YAxisKey = axes[sensor.SensorType].Key; + series.Title = sensor.Hardware.Name + " " + sensor.Name; + this.model.Series.Add(series); + + types.Add(sensor.SensorType); + } + + var start = 0.0; + foreach (var pair in axes.Reverse()) { + var axis = pair.Value; + var type = pair.Key; + axis.StartPosition = start; + axis.IsAxisVisible = types.Contains(type); + var delta = axis.IsAxisVisible ? 1.0 / types.Count : 0; + start += delta; + axis.EndPosition = start; + } + + InvalidatePlot(); + } + + public void InvalidatePlot() { + this.now = DateTime.UtcNow; + this.plot.InvalidatePlot(true); } } diff -r 19848d079a33 -r d1f25b504845 OpenHardwareMonitor.csproj --- a/OpenHardwareMonitor.csproj Sat Jun 08 17:15:09 2013 +0000 +++ b/OpenHardwareMonitor.csproj Sun Jun 09 16:08:59 2013 +0000 @@ -58,6 +58,12 @@ AllRules.ruleset + + External\OxyPlot.dll + + + External\OxyPlot.WindowsForms.dll + diff -r 19848d079a33 -r d1f25b504845 Properties/AssemblyInfo.cs --- a/Properties/AssemblyInfo.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/Properties/AssemblyInfo.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,7 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2012 Michael Möller + Copyright (C) 2009-2013 Michael Möller */ @@ -18,7 +18,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Open Hardware Monitor")] -[assembly: AssemblyCopyright("Copyright © 2009-2012 Michael Möller")] +[assembly: AssemblyCopyright("Copyright © 2009-2013 Michael Möller")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff -r 19848d079a33 -r d1f25b504845 Properties/AssemblyLibInfo.cs --- a/Properties/AssemblyLibInfo.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/Properties/AssemblyLibInfo.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,7 +4,7 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2012 Michael Möller + Copyright (C) 2009-2013 Michael Möller */ @@ -17,7 +17,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Open Hardware Monitor Library")] -[assembly: AssemblyCopyright("Copyright © 2009-2012 Michael Möller")] +[assembly: AssemblyCopyright("Copyright © 2009-2013 Michael Möller")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff -r 19848d079a33 -r d1f25b504845 Properties/AssemblyVersion.cs --- a/Properties/AssemblyVersion.cs Sat Jun 08 17:15:09 2013 +0000 +++ b/Properties/AssemblyVersion.cs Sun Jun 09 16:08:59 2013 +0000 @@ -4,11 +4,11 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - Copyright (C) 2009-2012 Michael Möller + Copyright (C) 2009-2013 Michael Möller */ using System.Reflection; -[assembly: AssemblyVersion("0.5.1.10")] -[assembly: AssemblyInformationalVersion("0.5.1.10 Alpha")] \ No newline at end of file +[assembly: AssemblyVersion("0.5.1.13")] +[assembly: AssemblyInformationalVersion("0.5.1.13 Alpha")] \ No newline at end of file