GUI/PlotPanel.cs
author sl
Sun, 03 Feb 2013 18:01:50 +0100
changeset 391 ca4c0e7ae75d
parent 326 a41745e3828d
permissions -rw-r--r--
Converted project to VisualStudio 2012.
Adding SoundGraphDisplay and SensorFrontView classes.
They were respectively based on SystemTray and SensorNotifyIcon.
SoundGraphDisplay is now able to load iMONDisplay.dll providing it lives on your PATH.
Adding option to sensor context menu for adding it into FrontView.
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@344
     7
  Copyright (C) 2009-2011 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.ComponentModel;
moel@1
    14
using System.Drawing;
moel@1
    15
using System.Drawing.Drawing2D;
moel@1
    16
using System.Windows.Forms;
moel@1
    17
using OpenHardwareMonitor.Hardware;
moel@1
    18
moel@1
    19
namespace OpenHardwareMonitor.GUI {
moel@158
    20
  public class PlotPanel : UserControl {
moel@1
    21
moel@326
    22
    private PersistentSettings settings;
moel@326
    23
moel@1
    24
    private DateTime now;
moel@1
    25
    private List<ISensor> clocks = new List<ISensor>();
moel@1
    26
    private List<ISensor> temperatures = new List<ISensor>();
moel@1
    27
    private List<ISensor> fans = new List<ISensor>();
moel@1
    28
    private IDictionary<ISensor, Color> colors;
moel@1
    29
moel@1
    30
    private StringFormat centerlower;
moel@1
    31
    private StringFormat centerleft;
moel@1
    32
    private StringFormat lowerleft;
moel@1
    33
    private Brush lightBrush;
moel@1
    34
    private Pen lightPen;
moel@1
    35
moel@326
    36
    private UserRadioGroup timeWindowRadioGroup;
moel@326
    37
moel@326
    38
    public PlotPanel(PersistentSettings settings) {
moel@326
    39
      this.settings = settings;
moel@326
    40
moel@1
    41
      this.SetStyle(ControlStyles.DoubleBuffer |
moel@1
    42
        ControlStyles.UserPaint |
moel@158
    43
        ControlStyles.AllPaintingInWmPaint |
moel@1
    44
        ControlStyles.ResizeRedraw, true);
moel@1
    45
      this.UpdateStyles();
moel@1
    46
moel@326
    47
      CreateContextMenu();
moel@326
    48
moel@1
    49
      centerlower = new StringFormat();
moel@1
    50
      centerlower.Alignment = StringAlignment.Center;
moel@1
    51
      centerlower.LineAlignment = StringAlignment.Near;
moel@1
    52
moel@1
    53
      centerleft = new StringFormat();
moel@1
    54
      centerleft.Alignment = StringAlignment.Far;
moel@1
    55
      centerleft.LineAlignment = StringAlignment.Center;
moel@1
    56
moel@1
    57
      lowerleft = new StringFormat();
moel@1
    58
      lowerleft.Alignment = StringAlignment.Far;
moel@1
    59
      lowerleft.LineAlignment = StringAlignment.Near;
moel@1
    60
moel@1
    61
      lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245));
moel@1
    62
      lightPen = new Pen(Color.FromArgb(200, 200, 200));
moel@1
    63
    }
moel@1
    64
moel@326
    65
    private void CreateContextMenu() {
moel@326
    66
      MenuItem timeWindow = new MenuItem("Time Scale");
moel@326
    67
      
moel@326
    68
      MenuItem[] timeWindowMenuItems = 
moel@326
    69
        { new MenuItem("Auto"), 
moel@326
    70
          new MenuItem("5 min"),
moel@326
    71
          new MenuItem("10 min"),
moel@326
    72
          new MenuItem("20 min"),
moel@326
    73
          new MenuItem("30 min"),
moel@326
    74
          new MenuItem("45 min"),
moel@326
    75
          new MenuItem("1 h"),
moel@326
    76
          new MenuItem("1.5 h"),
moel@326
    77
          new MenuItem("2 h"),
moel@326
    78
          new MenuItem("3 h"),
moel@326
    79
          new MenuItem("6 h"),
moel@326
    80
          new MenuItem("12 h"),
moel@326
    81
          new MenuItem("24 h") };
moel@326
    82
moel@326
    83
      foreach (MenuItem mi in timeWindowMenuItems)
moel@326
    84
        timeWindow.MenuItems.Add(mi);
moel@326
    85
moel@326
    86
      timeWindowRadioGroup = new UserRadioGroup("timeWindow", 0, 
moel@326
    87
        timeWindowMenuItems, settings);
moel@326
    88
moel@326
    89
      this.ContextMenu = new ContextMenu();
moel@326
    90
      this.ContextMenu.MenuItems.Add(timeWindow);
moel@326
    91
    }
moel@326
    92
moel@1
    93
    private List<float> GetTemperatureGrid() {
moel@1
    94
moel@1
    95
      float? minTempNullable = null;
moel@1
    96
      float? maxTempNullable = null;
moel@1
    97
      foreach (ISensor sensor in temperatures) {
moel@159
    98
        IEnumerable<SensorValue> values = sensor.Values;
moel@159
    99
        foreach (SensorValue value in values) {
moel@298
   100
          if (!float.IsNaN(value.Value)) {
moel@298
   101
            if (!minTempNullable.HasValue || minTempNullable > value.Value)
moel@298
   102
              minTempNullable = value.Value;
moel@298
   103
            if (!maxTempNullable.HasValue || maxTempNullable < value.Value)
moel@298
   104
              maxTempNullable = value.Value;
moel@298
   105
          }
moel@1
   106
        }
moel@1
   107
      }
moel@1
   108
      if (!minTempNullable.HasValue) {
moel@1
   109
        minTempNullable = 20;
moel@1
   110
        maxTempNullable = 30;
moel@1
   111
      }
moel@1
   112
moel@1
   113
      float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10;
moel@1
   114
      float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10;
moel@158
   115
      if (maxTemp == minTemp)
moel@158
   116
        maxTemp += 10;
moel@158
   117
moel@1
   118
      int countTempMax = 4;
moel@1
   119
      float deltaTemp = maxTemp - minTemp;
moel@1
   120
      int countTemp = (int)Math.Round(deltaTemp / 2);
moel@1
   121
      if (countTemp > countTempMax)
moel@1
   122
        countTemp = (int)Math.Round(deltaTemp / 5);
moel@1
   123
      if (countTemp > countTempMax)
moel@1
   124
        countTemp = (int)Math.Round(deltaTemp / 10);
moel@1
   125
      if (countTemp > countTempMax)
moel@1
   126
        countTemp = (int)Math.Round(deltaTemp / 20);
moel@1
   127
moel@1
   128
      List<float> grid = new List<float>(countTemp + 1);
moel@1
   129
      for (int i = 0; i <= countTemp; i++) {
moel@1
   130
        grid.Add(minTemp + i * deltaTemp / countTemp);
moel@1
   131
      }
moel@1
   132
      return grid;
moel@1
   133
    }
moel@1
   134
moel@1
   135
    private List<float> GetTimeGrid() {
moel@1
   136
moel@326
   137
      float maxTime;
moel@326
   138
      if (timeWindowRadioGroup.Value == 0) { // Auto
moel@326
   139
        maxTime = 5;
moel@326
   140
        if (temperatures.Count > 0) {
moel@326
   141
          IEnumerator<SensorValue> enumerator =
moel@326
   142
            temperatures[0].Values.GetEnumerator();
moel@326
   143
          if (enumerator.MoveNext()) {
moel@326
   144
            maxTime = (float)(now - enumerator.Current.Time).TotalMinutes;
moel@326
   145
          }
moel@1
   146
        }
moel@326
   147
      } else {
moel@326
   148
        float[] maxTimes = 
moel@326
   149
          { 5, 10, 20, 30, 45, 60, 90, 120, 180, 360, 720, 1440 };
moel@326
   150
moel@326
   151
        maxTime = maxTimes[timeWindowRadioGroup.Value - 1];
moel@1
   152
      }
moel@1
   153
moel@1
   154
      int countTime = 10;
moel@1
   155
      float deltaTime = 5;
moel@326
   156
      while (deltaTime + 1 <= maxTime && deltaTime < 10)
moel@1
   157
        deltaTime += 1;
moel@326
   158
      while (deltaTime + 2 <= maxTime && deltaTime < 30)
moel@1
   159
        deltaTime += 2;
moel@326
   160
      while (deltaTime + 5 <= maxTime && deltaTime < 100)
moel@1
   161
        deltaTime += 5;
moel@326
   162
      while (deltaTime + 50 <= maxTime && deltaTime < 1000)
moel@298
   163
        deltaTime += 50;
moel@326
   164
      while (deltaTime + 100 <= maxTime && deltaTime < 10000)
moel@298
   165
        deltaTime += 100;
moel@1
   166
moel@1
   167
      List<float> grid = new List<float>(countTime + 1);
moel@1
   168
      for (int i = 0; i <= countTime; i++) {
moel@1
   169
        grid.Add(i * deltaTime / countTime);
moel@1
   170
      }
moel@1
   171
      return grid;
moel@1
   172
    }
moel@1
   173
moel@1
   174
    protected override void OnPaint(PaintEventArgs e) {
moel@314
   175
      now = DateTime.UtcNow - new TimeSpan(0, 0, 4);
moel@1
   176
moel@1
   177
      List<float> timeGrid = GetTimeGrid();
moel@1
   178
      List<float> tempGrid = GetTemperatureGrid();
moel@1
   179
moel@1
   180
      Graphics g = e.Graphics;
moel@1
   181
moel@1
   182
      RectangleF r =
moel@1
   183
        new RectangleF(0, 0, Bounds.Width, Bounds.Height);
moel@1
   184
moel@1
   185
      float ml = 40;
moel@1
   186
      float mr = 15;
moel@1
   187
      float x0 = r.X + ml;
moel@1
   188
      float w = r.Width - ml - mr;
moel@1
   189
moel@1
   190
      float mt = 15;
moel@1
   191
      float mb = 28;
moel@1
   192
      float y0 = r.Y + mt;
moel@1
   193
      float h = r.Height - mt - mb;
moel@1
   194
moel@1
   195
      float leftScaleSpace = 5;
moel@1
   196
      float bottomScaleSpace = 5;
moel@158
   197
moel@1
   198
      g.Clear(Color.White);
moel@1
   199
moel@1
   200
      if (w > 0 && h > 0) {
moel@1
   201
        g.FillRectangle(lightBrush, x0, y0, w, h);
moel@1
   202
moel@1
   203
        g.SmoothingMode = SmoothingMode.HighQuality;
moel@1
   204
        for (int i = 0; i < timeGrid.Count; i++) {
moel@1
   205
          float x = x0 + i * w / (timeGrid.Count - 1);
moel@1
   206
          g.DrawLine(lightPen, x, y0, x, y0 + h);
moel@1
   207
        }
moel@1
   208
moel@1
   209
        for (int i = 0; i < tempGrid.Count; i++) {
moel@1
   210
          float y = y0 + i * h / (tempGrid.Count - 1);
moel@1
   211
          g.DrawLine(lightPen, x0, y, x0 + w, y);
moel@1
   212
        }
moel@1
   213
moel@1
   214
        float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0];
moel@1
   215
        float deltaTime = timeGrid[timeGrid.Count - 1];
moel@1
   216
        foreach (ISensor sensor in temperatures) {
moel@1
   217
          using (Pen pen = new Pen(colors[sensor])) {
moel@159
   218
            IEnumerable<SensorValue> values = sensor.Values;
moel@1
   219
            PointF last = new PointF();
moel@1
   220
            bool first = true;
moel@298
   221
            foreach (SensorValue v in values) {
moel@298
   222
              if (!float.IsNaN(v.Value)) {
moel@298
   223
                PointF point = new PointF(
moel@298
   224
                    x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime,
moel@298
   225
                    y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp);
moel@298
   226
                if (!first) 
moel@298
   227
                  g.DrawLine(pen, last, point);                
moel@298
   228
                last = point;
moel@298
   229
                first = false;
moel@298
   230
              } else {
moel@298
   231
                first = true;
moel@298
   232
              }
moel@1
   233
            }
moel@1
   234
          }
moel@1
   235
        }
moel@1
   236
moel@1
   237
        g.SmoothingMode = SmoothingMode.None;
moel@1
   238
        g.FillRectangle(Brushes.White, 0, 0, x0, r.Height);
moel@158
   239
        g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w,
moel@1
   240
          r.Height);
moel@1
   241
moel@1
   242
        for (int i = 1; i < timeGrid.Count; i++) {
moel@158
   243
          float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1);
moel@158
   244
          g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x,
moel@1
   245
            y0 + h + bottomScaleSpace, centerlower);
moel@1
   246
        }
moel@1
   247
moel@1
   248
        for (int i = 0; i < tempGrid.Count - 1; i++) {
moel@1
   249
          float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1);
moel@158
   250
          g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black,
moel@1
   251
            x0 - leftScaleSpace, y, centerleft);
moel@1
   252
        }
moel@1
   253
moel@1
   254
        g.SmoothingMode = SmoothingMode.HighQuality;
moel@1
   255
        g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0,
moel@158
   256
          lowerleft);
moel@1
   257
        g.DrawString("[min]", Font, Brushes.Black, x0 + w,
moel@158
   258
          y0 + h + bottomScaleSpace, lowerleft);
moel@1
   259
      }
moel@1
   260
    }
moel@1
   261
moel@158
   262
    public void SetSensors(List<ISensor> sensors,
moel@158
   263
      IDictionary<ISensor, Color> colors) {
moel@1
   264
      this.colors = colors;
moel@1
   265
      List<ISensor> clocks = new List<ISensor>();
moel@1
   266
      List<ISensor> temperatures = new List<ISensor>();
moel@1
   267
      List<ISensor> fans = new List<ISensor>();
moel@1
   268
      foreach (ISensor sensor in sensors)
moel@1
   269
        switch (sensor.SensorType) {
moel@1
   270
          case SensorType.Clock: clocks.Add(sensor); break;
moel@1
   271
          case SensorType.Temperature: temperatures.Add(sensor); break;
moel@1
   272
          case SensorType.Fan: fans.Add(sensor); break;
moel@1
   273
        }
moel@1
   274
      this.clocks = clocks;
moel@1
   275
      this.temperatures = temperatures;
moel@1
   276
      this.fans = fans;
moel@1
   277
      Invalidate();
moel@1
   278
    }
moel@1
   279
moel@1
   280
  }
moel@1
   281
}