GUI/PlotPanel.cs
author moel.mich
Sun, 21 Jul 2013 21:03:31 +0000
changeset 416 f117373bd190
parent 398 ac9b3b647906
permissions -rw-r--r--
Refactored the SuperIOHardware class.
moel@158
     1
/*
moel@1
     2
 
moel@344
     3
  This Source Code Form is subject to the terms of the Mozilla Public
moel@344
     4
  License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344
     5
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@1
     6
 
moel@395
     7
  Copyright (C) 2009-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344
     8
	
moel@1
     9
*/
moel@1
    10
moel@1
    11
using System;
moel@1
    12
using System.Collections.Generic;
moel@1
    13
using System.Drawing;
moel@395
    14
using System.Linq;
moel@1
    15
using System.Windows.Forms;
moel@1
    16
using OpenHardwareMonitor.Hardware;
moel@395
    17
using OxyPlot;
moel@395
    18
using OxyPlot.Axes;
moel@395
    19
using OxyPlot.WindowsForms;
moel@395
    20
using OxyPlot.Series;
moel@395
    21
using OpenHardwareMonitor.Collections;
moel@1
    22
moel@1
    23
namespace OpenHardwareMonitor.GUI {
moel@158
    24
  public class PlotPanel : UserControl {
moel@1
    25
moel@400
    26
    private readonly PersistentSettings settings;
moel@400
    27
    private readonly UnitManager unitManager;
moel@326
    28
moel@395
    29
    private readonly Plot plot;
moel@395
    30
    private readonly PlotModel model;
moel@395
    31
    private readonly TimeSpanAxis timeAxis = new TimeSpanAxis();
moel@395
    32
    private readonly SortedDictionary<SensorType, LinearAxis> axes =
moel@395
    33
      new SortedDictionary<SensorType, LinearAxis>();
moel@395
    34
moel@397
    35
    private UserOption stackedAxes;
moel@397
    36
moel@1
    37
    private DateTime now;
moel@326
    38
moel@400
    39
    public PlotPanel(PersistentSettings settings, UnitManager unitManager) {
moel@326
    40
      this.settings = settings;
moel@400
    41
      this.unitManager = unitManager;
moel@400
    42
moel@395
    43
      this.model = CreatePlotModel();
moel@326
    44
moel@395
    45
      this.plot = new Plot();
moel@395
    46
      this.plot.Dock = DockStyle.Fill;
moel@395
    47
      this.plot.Model = model;
moel@395
    48
      this.plot.BackColor = Color.White;
moel@397
    49
      this.plot.ContextMenu = CreateMenu();
moel@397
    50
moel@397
    51
      UpdateAxesPosition();
moel@1
    52
moel@395
    53
      this.SuspendLayout();
moel@395
    54
      this.Controls.Add(plot);
moel@395
    55
      this.ResumeLayout(true);
moel@1
    56
    }
moel@1
    57
moel@395
    58
    public void SetCurrentSettings() {
moel@395
    59
      settings.SetValue("plotPanel.MinTimeSpan", (float)timeAxis.ViewMinimum);
moel@395
    60
      settings.SetValue("plotPanel.MaxTimeSpan", (float)timeAxis.ViewMaximum);
moel@395
    61
moel@395
    62
      foreach (var axis in axes.Values) {
moel@395
    63
        settings.SetValue("plotPanel.Min" + axis.Key, (float)axis.ViewMinimum);
moel@395
    64
        settings.SetValue("plotPanel.Max" + axis.Key, (float)axis.ViewMaximum);
moel@395
    65
      }
moel@395
    66
    }
moel@395
    67
moel@397
    68
    private ContextMenu CreateMenu() {
moel@397
    69
      ContextMenu menu = new ContextMenu();
moel@397
    70
moel@397
    71
      MenuItem stackedAxesMenuItem = new MenuItem("Stacked Axes");
moel@397
    72
      stackedAxes = new UserOption("stackedAxes", true,
moel@397
    73
        stackedAxesMenuItem, settings);
moel@397
    74
      stackedAxes.Changed += (sender, e) => {
moel@397
    75
        UpdateAxesPosition();
moel@397
    76
        InvalidatePlot();
moel@397
    77
      };
moel@397
    78
      menu.MenuItems.Add(stackedAxesMenuItem);
moel@397
    79
moel@395
    80
      MenuItem timeWindow = new MenuItem("Time Window");
moel@395
    81
      MenuItem[] timeWindowMenuItems =
moel@395
    82
        { new MenuItem("Auto", 
moel@395
    83
            (s, e) => { timeAxis.Zoom(0, double.NaN); InvalidatePlot(); }),
moel@395
    84
          new MenuItem("5 min", 
moel@395
    85
            (s, e) => { timeAxis.Zoom(0, 5 * 60); InvalidatePlot(); }),
moel@395
    86
          new MenuItem("10 min", 
moel@395
    87
            (s, e) => { timeAxis.Zoom(0, 10 * 60); InvalidatePlot(); }),
moel@395
    88
          new MenuItem("20 min", 
moel@395
    89
            (s, e) => { timeAxis.Zoom(0, 20 * 60); InvalidatePlot(); }),
moel@395
    90
          new MenuItem("30 min", 
moel@395
    91
            (s, e) => { timeAxis.Zoom(0, 30 * 60); InvalidatePlot(); }),
moel@395
    92
          new MenuItem("45 min", 
moel@395
    93
            (s, e) => { timeAxis.Zoom(0, 45 * 60); InvalidatePlot(); }),
moel@395
    94
          new MenuItem("1 h", 
moel@395
    95
            (s, e) => { timeAxis.Zoom(0, 60 * 60); InvalidatePlot(); }),
moel@395
    96
          new MenuItem("1.5 h", 
moel@395
    97
            (s, e) => { timeAxis.Zoom(0, 1.5 * 60 * 60); InvalidatePlot(); }),
moel@395
    98
          new MenuItem("2 h", 
moel@395
    99
            (s, e) => { timeAxis.Zoom(0, 2 * 60 * 60); InvalidatePlot(); }),
moel@395
   100
          new MenuItem("3 h", 
moel@395
   101
            (s, e) => { timeAxis.Zoom(0, 3 * 60 * 60); InvalidatePlot(); }),
moel@395
   102
          new MenuItem("6 h", 
moel@395
   103
            (s, e) => { timeAxis.Zoom(0, 6 * 60 * 60); InvalidatePlot(); }),
moel@395
   104
          new MenuItem("12 h", 
moel@395
   105
            (s, e) => { timeAxis.Zoom(0, 12 * 60 * 60); InvalidatePlot(); }),
moel@395
   106
          new MenuItem("24 h", 
moel@395
   107
            (s, e) => { timeAxis.Zoom(0, 24 * 60 * 60); InvalidatePlot(); }) };
moel@326
   108
      foreach (MenuItem mi in timeWindowMenuItems)
moel@326
   109
        timeWindow.MenuItems.Add(mi);
moel@397
   110
      menu.MenuItems.Add(timeWindow);
moel@326
   111
moel@397
   112
      return menu;
moel@326
   113
    }
moel@326
   114
moel@395
   115
    private PlotModel CreatePlotModel() {
moel@1
   116
moel@395
   117
      timeAxis.Position = AxisPosition.Bottom;
moel@395
   118
      timeAxis.MajorGridlineStyle = LineStyle.Solid;
moel@395
   119
      timeAxis.MajorGridlineThickness = 1;
moel@395
   120
      timeAxis.MajorGridlineColor = OxyColor.FromRgb(192, 192, 192);
moel@395
   121
      timeAxis.MinorGridlineStyle = LineStyle.Solid;
moel@395
   122
      timeAxis.MinorGridlineThickness = 1;
moel@395
   123
      timeAxis.MinorGridlineColor = OxyColor.FromRgb(232, 232, 232);
moel@395
   124
      timeAxis.StartPosition = 1;
moel@395
   125
      timeAxis.EndPosition = 0;
moel@395
   126
      timeAxis.MinimumPadding = 0;
moel@395
   127
      timeAxis.MaximumPadding = 0;
moel@395
   128
      timeAxis.AbsoluteMinimum = 0;
moel@395
   129
      timeAxis.Minimum = 0;
moel@395
   130
      timeAxis.AbsoluteMaximum = 24 * 60 * 60;
moel@395
   131
      timeAxis.Zoom(
moel@395
   132
        settings.GetValue("plotPanel.MinTimeSpan", 0.0f),
moel@395
   133
        settings.GetValue("plotPanel.MaxTimeSpan", 10.0f * 60));
moel@395
   134
      timeAxis.StringFormat = "h:mm";
moel@395
   135
moel@395
   136
      var units = new Dictionary<SensorType, string>();
moel@395
   137
      units.Add(SensorType.Voltage, "V");
moel@395
   138
      units.Add(SensorType.Clock, "MHz");
moel@395
   139
      units.Add(SensorType.Temperature, "°C");
moel@395
   140
      units.Add(SensorType.Load, "%");
moel@395
   141
      units.Add(SensorType.Fan, "RPM");
moel@395
   142
      units.Add(SensorType.Flow, "L/h");
moel@395
   143
      units.Add(SensorType.Control, "%");
moel@395
   144
      units.Add(SensorType.Level, "%");
moel@395
   145
      units.Add(SensorType.Factor, "1");
moel@395
   146
      units.Add(SensorType.Power, "W");
moel@395
   147
      units.Add(SensorType.Data, "GB");
moel@395
   148
moel@395
   149
      foreach (SensorType type in Enum.GetValues(typeof(SensorType))) {
moel@395
   150
        var axis = new LinearAxis();
moel@395
   151
        axis.Position = AxisPosition.Left;
moel@395
   152
        axis.MajorGridlineStyle = LineStyle.Solid;
moel@395
   153
        axis.MajorGridlineThickness = 1;
moel@395
   154
        axis.MajorGridlineColor = timeAxis.MajorGridlineColor;
moel@395
   155
        axis.MinorGridlineStyle = LineStyle.Solid;
moel@395
   156
        axis.MinorGridlineThickness = 1;
moel@395
   157
        axis.MinorGridlineColor = timeAxis.MinorGridlineColor;
moel@397
   158
        axis.AxislineStyle = LineStyle.Solid;
moel@395
   159
        axis.Title = type.ToString();
moel@395
   160
        axis.Key = type.ToString();
moel@395
   161
moel@395
   162
        axis.Zoom(
moel@395
   163
          settings.GetValue("plotPanel.Min" + axis.Key, float.NaN),
moel@395
   164
          settings.GetValue("plotPanel.Max" + axis.Key, float.NaN));
moel@395
   165
moel@395
   166
        if (units.ContainsKey(type))
moel@395
   167
          axis.Unit = units[type];
moel@395
   168
        axes.Add(type, axis);
moel@1
   169
      }
moel@1
   170
moel@395
   171
      var model = new PlotModel();
moel@395
   172
      model.Axes.Add(timeAxis);
moel@395
   173
      foreach (var axis in axes.Values)
moel@395
   174
        model.Axes.Add(axis);
moel@395
   175
      model.PlotMargins = new OxyThickness(0);
moel@395
   176
      model.IsLegendVisible = false;
moel@158
   177
moel@395
   178
      return model;
moel@1
   179
    }
moel@1
   180
moel@158
   181
    public void SetSensors(List<ISensor> sensors,
moel@158
   182
      IDictionary<ISensor, Color> colors) {
moel@395
   183
      this.model.Series.Clear();
moel@395
   184
moel@395
   185
      ListSet<SensorType> types = new ListSet<SensorType>();
moel@395
   186
moel@395
   187
      foreach (ISensor sensor in sensors) {
moel@395
   188
        var series = new LineSeries();
moel@400
   189
        if (sensor.SensorType == SensorType.Temperature) {
moel@400
   190
          series.ItemsSource = sensor.Values.Select(value => new DataPoint {
moel@400
   191
            X = (now - value.Time).TotalSeconds,
moel@400
   192
            Y = unitManager.TemperatureUnit == TemperatureUnit.Celsius ? 
moel@400
   193
              value.Value : UnitManager.CelsiusToFahrenheit(value.Value).Value
moel@400
   194
          });
moel@400
   195
        } else {
moel@400
   196
          series.ItemsSource = sensor.Values.Select(value => new DataPoint {
moel@400
   197
            X = (now - value.Time).TotalSeconds, Y = value.Value
moel@400
   198
          });
moel@400
   199
        }
moel@395
   200
        series.Color = colors[sensor].ToOxyColor();
moel@395
   201
        series.StrokeThickness = 1;
moel@395
   202
        series.YAxisKey = axes[sensor.SensorType].Key;
moel@395
   203
        series.Title = sensor.Hardware.Name + " " + sensor.Name;
moel@395
   204
        this.model.Series.Add(series);
moel@395
   205
moel@395
   206
        types.Add(sensor.SensorType);
moel@395
   207
      }
moel@395
   208
moel@395
   209
      foreach (var pair in axes.Reverse()) {
moel@395
   210
        var axis = pair.Value;
moel@395
   211
        var type = pair.Key;
moel@400
   212
        axis.IsAxisVisible = types.Contains(type);
moel@397
   213
      } 
moel@397
   214
moel@397
   215
      UpdateAxesPosition();
moel@397
   216
      InvalidatePlot();
moel@397
   217
    }
moel@397
   218
moel@397
   219
    private void UpdateAxesPosition() {
moel@397
   220
      if (stackedAxes.Value) {
moel@397
   221
        var count = axes.Values.Count(axis => axis.IsAxisVisible);
moel@397
   222
        var start = 0.0;
moel@397
   223
        foreach (var pair in axes.Reverse()) {
moel@397
   224
          var axis = pair.Value;
moel@397
   225
          var type = pair.Key;
moel@397
   226
          axis.StartPosition = start;
moel@397
   227
          var delta = axis.IsAxisVisible ? 1.0 / count : 0;
moel@397
   228
          start += delta;
moel@397
   229
          axis.EndPosition = start;
moel@397
   230
          axis.PositionTier = 0;
moel@397
   231
          axis.MajorGridlineStyle = LineStyle.Solid;
moel@397
   232
          axis.MinorGridlineStyle = LineStyle.Solid;   
moel@397
   233
        }
moel@397
   234
      } else {
moel@397
   235
        var tier = 0;
moel@397
   236
        foreach (var pair in axes.Reverse()) {
moel@397
   237
          var axis = pair.Value;
moel@397
   238
          var type = pair.Key;
moel@400
   239
          if (axis.IsAxisVisible) {
moel@400
   240
            axis.StartPosition = 0;
moel@400
   241
            axis.EndPosition = 1;
moel@400
   242
            axis.PositionTier = tier;
moel@397
   243
            tier++;
moel@400
   244
          } else {
moel@400
   245
            axis.StartPosition = 0;
moel@400
   246
            axis.EndPosition = 0;
moel@400
   247
            axis.PositionTier = 0;
moel@400
   248
          }
moel@397
   249
          axis.MajorGridlineStyle = LineStyle.None;
moel@397
   250
          axis.MinorGridlineStyle = LineStyle.None;          
moel@397
   251
        }
moel@395
   252
      }
moel@398
   253
moel@395
   254
    }
moel@395
   255
moel@395
   256
    public void InvalidatePlot() {
moel@395
   257
      this.now = DateTime.UtcNow;
moel@400
   258
moel@400
   259
      foreach (var pair in axes) {
moel@400
   260
        var axis = pair.Value;
moel@400
   261
        var type = pair.Key;
moel@400
   262
        if (type == SensorType.Temperature)
moel@400
   263
          axis.Unit = unitManager.TemperatureUnit == TemperatureUnit.Celsius ?
moel@400
   264
          "°C" : "°F";
moel@400
   265
      }
moel@400
   266
moel@395
   267
      this.plot.InvalidatePlot(true);
moel@1
   268
    }
moel@1
   269
moel@1
   270
  }
moel@1
   271
}