GUI/PlotPanel.cs
author moel.mich
Sun, 09 Jun 2013 17:44:05 +0000
changeset 400 f4e2e3e69651
parent 398 ac9b3b647906
permissions -rw-r--r--
Added Fahrenheit support to the plot.
     1 /*
     2  
     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/.
     6  
     7   Copyright (C) 2009-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Collections.Generic;
    13 using System.Drawing;
    14 using System.Linq;
    15 using System.Windows.Forms;
    16 using OpenHardwareMonitor.Hardware;
    17 using OxyPlot;
    18 using OxyPlot.Axes;
    19 using OxyPlot.WindowsForms;
    20 using OxyPlot.Series;
    21 using OpenHardwareMonitor.Collections;
    22 
    23 namespace OpenHardwareMonitor.GUI {
    24   public class PlotPanel : UserControl {
    25 
    26     private readonly PersistentSettings settings;
    27     private readonly UnitManager unitManager;
    28 
    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>();
    34 
    35     private UserOption stackedAxes;
    36 
    37     private DateTime now;
    38 
    39     public PlotPanel(PersistentSettings settings, UnitManager unitManager) {
    40       this.settings = settings;
    41       this.unitManager = unitManager;
    42 
    43       this.model = CreatePlotModel();
    44 
    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();
    50 
    51       UpdateAxesPosition();
    52 
    53       this.SuspendLayout();
    54       this.Controls.Add(plot);
    55       this.ResumeLayout(true);
    56     }
    57 
    58     public void SetCurrentSettings() {
    59       settings.SetValue("plotPanel.MinTimeSpan", (float)timeAxis.ViewMinimum);
    60       settings.SetValue("plotPanel.MaxTimeSpan", (float)timeAxis.ViewMaximum);
    61 
    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);
    65       }
    66     }
    67 
    68     private ContextMenu CreateMenu() {
    69       ContextMenu menu = new ContextMenu();
    70 
    71       MenuItem stackedAxesMenuItem = new MenuItem("Stacked Axes");
    72       stackedAxes = new UserOption("stackedAxes", true,
    73         stackedAxesMenuItem, settings);
    74       stackedAxes.Changed += (sender, e) => {
    75         UpdateAxesPosition();
    76         InvalidatePlot();
    77       };
    78       menu.MenuItems.Add(stackedAxesMenuItem);
    79 
    80       MenuItem timeWindow = new MenuItem("Time Window");
    81       MenuItem[] timeWindowMenuItems =
    82         { new MenuItem("Auto", 
    83             (s, e) => { timeAxis.Zoom(0, double.NaN); InvalidatePlot(); }),
    84           new MenuItem("5 min", 
    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(); }),
    94           new MenuItem("1 h", 
    95             (s, e) => { timeAxis.Zoom(0, 60 * 60); InvalidatePlot(); }),
    96           new MenuItem("1.5 h", 
    97             (s, e) => { timeAxis.Zoom(0, 1.5 * 60 * 60); InvalidatePlot(); }),
    98           new MenuItem("2 h", 
    99             (s, e) => { timeAxis.Zoom(0, 2 * 60 * 60); InvalidatePlot(); }),
   100           new MenuItem("3 h", 
   101             (s, e) => { timeAxis.Zoom(0, 3 * 60 * 60); InvalidatePlot(); }),
   102           new MenuItem("6 h", 
   103             (s, e) => { timeAxis.Zoom(0, 6 * 60 * 60); InvalidatePlot(); }),
   104           new MenuItem("12 h", 
   105             (s, e) => { timeAxis.Zoom(0, 12 * 60 * 60); InvalidatePlot(); }),
   106           new MenuItem("24 h", 
   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);
   111 
   112       return menu;
   113     }
   114 
   115     private PlotModel CreatePlotModel() {
   116 
   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;
   131       timeAxis.Zoom(
   132         settings.GetValue("plotPanel.MinTimeSpan", 0.0f),
   133         settings.GetValue("plotPanel.MaxTimeSpan", 10.0f * 60));
   134       timeAxis.StringFormat = "h:mm";
   135 
   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");
   148 
   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();
   161 
   162         axis.Zoom(
   163           settings.GetValue("plotPanel.Min" + axis.Key, float.NaN),
   164           settings.GetValue("plotPanel.Max" + axis.Key, float.NaN));
   165 
   166         if (units.ContainsKey(type))
   167           axis.Unit = units[type];
   168         axes.Add(type, axis);
   169       }
   170 
   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;
   177 
   178       return model;
   179     }
   180 
   181     public void SetSensors(List<ISensor> sensors,
   182       IDictionary<ISensor, Color> colors) {
   183       this.model.Series.Clear();
   184 
   185       ListSet<SensorType> types = new ListSet<SensorType>();
   186 
   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
   194           });
   195         } else {
   196           series.ItemsSource = sensor.Values.Select(value => new DataPoint {
   197             X = (now - value.Time).TotalSeconds, Y = value.Value
   198           });
   199         }
   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);
   205 
   206         types.Add(sensor.SensorType);
   207       }
   208 
   209       foreach (var pair in axes.Reverse()) {
   210         var axis = pair.Value;
   211         var type = pair.Key;
   212         axis.IsAxisVisible = types.Contains(type);
   213       } 
   214 
   215       UpdateAxesPosition();
   216       InvalidatePlot();
   217     }
   218 
   219     private void UpdateAxesPosition() {
   220       if (stackedAxes.Value) {
   221         var count = axes.Values.Count(axis => axis.IsAxisVisible);
   222         var start = 0.0;
   223         foreach (var pair in axes.Reverse()) {
   224           var axis = pair.Value;
   225           var type = pair.Key;
   226           axis.StartPosition = start;
   227           var delta = axis.IsAxisVisible ? 1.0 / count : 0;
   228           start += delta;
   229           axis.EndPosition = start;
   230           axis.PositionTier = 0;
   231           axis.MajorGridlineStyle = LineStyle.Solid;
   232           axis.MinorGridlineStyle = LineStyle.Solid;   
   233         }
   234       } else {
   235         var tier = 0;
   236         foreach (var pair in axes.Reverse()) {
   237           var axis = pair.Value;
   238           var type = pair.Key;
   239           if (axis.IsAxisVisible) {
   240             axis.StartPosition = 0;
   241             axis.EndPosition = 1;
   242             axis.PositionTier = tier;
   243             tier++;
   244           } else {
   245             axis.StartPosition = 0;
   246             axis.EndPosition = 0;
   247             axis.PositionTier = 0;
   248           }
   249           axis.MajorGridlineStyle = LineStyle.None;
   250           axis.MinorGridlineStyle = LineStyle.None;          
   251         }
   252       }
   253 
   254     }
   255 
   256     public void InvalidatePlot() {
   257       this.now = DateTime.UtcNow;
   258 
   259       foreach (var pair in axes) {
   260         var axis = pair.Value;
   261         var type = pair.Key;
   262         if (type == SensorType.Temperature)
   263           axis.Unit = unitManager.TemperatureUnit == TemperatureUnit.Celsius ?
   264           "°C" : "°F";
   265       }
   266 
   267       this.plot.InvalidatePlot(true);
   268     }
   269 
   270   }
   271 }