GUI/PlotPanel.cs
author moel.mich
Tue, 14 Feb 2012 23:07:55 +0000
changeset 340 600962f8a298
parent 314 d19c6b4d625e
child 344 3145aadca3d2
permissions -rw-r--r--
Added a new sensor type "Factor" for dimensionless values (and similar) that are not to be shown as percent ("Level" type). Changed the write amplification sensor to use the new "Factor" sensor type. Added the temperature SMART attribute for Sandforce SSDs as hidden sensor (as it may show fake results on some hardware).
moel@158
     1
/*
moel@1
     2
  
moel@1
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@1
     4
moel@1
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@1
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@1
     7
  the License. You may obtain a copy of the License at
moel@1
     8
 
moel@1
     9
  http://www.mozilla.org/MPL/
moel@1
    10
moel@1
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@1
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@1
    13
  for the specific language governing rights and limitations under the License.
moel@1
    14
moel@1
    15
  The Original Code is the Open Hardware Monitor code.
moel@1
    16
moel@1
    17
  The Initial Developer of the Original Code is 
moel@1
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@298
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2011
moel@1
    20
  the Initial Developer. All Rights Reserved.
moel@1
    21
moel@1
    22
  Contributor(s):
moel@1
    23
moel@1
    24
  Alternatively, the contents of this file may be used under the terms of
moel@1
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@1
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@1
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@1
    28
  of those above. If you wish to allow use of your version of this file only
moel@1
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@1
    30
  use your version of this file under the terms of the MPL, indicate your
moel@1
    31
  decision by deleting the provisions above and replace them with the notice
moel@1
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@1
    33
  the provisions above, a recipient may use your version of this file under
moel@1
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@1
    35
 
moel@1
    36
*/
moel@1
    37
moel@1
    38
using System;
moel@1
    39
using System.Collections.Generic;
moel@1
    40
using System.ComponentModel;
moel@1
    41
using System.Drawing;
moel@1
    42
using System.Drawing.Drawing2D;
moel@1
    43
using System.Windows.Forms;
moel@1
    44
using OpenHardwareMonitor.Hardware;
moel@1
    45
moel@1
    46
namespace OpenHardwareMonitor.GUI {
moel@158
    47
  public class PlotPanel : UserControl {
moel@1
    48
moel@326
    49
    private PersistentSettings settings;
moel@326
    50
moel@1
    51
    private DateTime now;
moel@1
    52
    private List<ISensor> clocks = new List<ISensor>();
moel@1
    53
    private List<ISensor> temperatures = new List<ISensor>();
moel@1
    54
    private List<ISensor> fans = new List<ISensor>();
moel@1
    55
    private IDictionary<ISensor, Color> colors;
moel@1
    56
moel@1
    57
    private StringFormat centerlower;
moel@1
    58
    private StringFormat centerleft;
moel@1
    59
    private StringFormat lowerleft;
moel@1
    60
    private Brush lightBrush;
moel@1
    61
    private Pen lightPen;
moel@1
    62
moel@326
    63
    private UserRadioGroup timeWindowRadioGroup;
moel@326
    64
moel@326
    65
    public PlotPanel(PersistentSettings settings) {
moel@326
    66
      this.settings = settings;
moel@326
    67
moel@1
    68
      this.SetStyle(ControlStyles.DoubleBuffer |
moel@1
    69
        ControlStyles.UserPaint |
moel@158
    70
        ControlStyles.AllPaintingInWmPaint |
moel@1
    71
        ControlStyles.ResizeRedraw, true);
moel@1
    72
      this.UpdateStyles();
moel@1
    73
moel@326
    74
      CreateContextMenu();
moel@326
    75
moel@1
    76
      centerlower = new StringFormat();
moel@1
    77
      centerlower.Alignment = StringAlignment.Center;
moel@1
    78
      centerlower.LineAlignment = StringAlignment.Near;
moel@1
    79
moel@1
    80
      centerleft = new StringFormat();
moel@1
    81
      centerleft.Alignment = StringAlignment.Far;
moel@1
    82
      centerleft.LineAlignment = StringAlignment.Center;
moel@1
    83
moel@1
    84
      lowerleft = new StringFormat();
moel@1
    85
      lowerleft.Alignment = StringAlignment.Far;
moel@1
    86
      lowerleft.LineAlignment = StringAlignment.Near;
moel@1
    87
moel@1
    88
      lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245));
moel@1
    89
      lightPen = new Pen(Color.FromArgb(200, 200, 200));
moel@1
    90
    }
moel@1
    91
moel@326
    92
    private void CreateContextMenu() {
moel@326
    93
      MenuItem timeWindow = new MenuItem("Time Scale");
moel@326
    94
      
moel@326
    95
      MenuItem[] timeWindowMenuItems = 
moel@326
    96
        { new MenuItem("Auto"), 
moel@326
    97
          new MenuItem("5 min"),
moel@326
    98
          new MenuItem("10 min"),
moel@326
    99
          new MenuItem("20 min"),
moel@326
   100
          new MenuItem("30 min"),
moel@326
   101
          new MenuItem("45 min"),
moel@326
   102
          new MenuItem("1 h"),
moel@326
   103
          new MenuItem("1.5 h"),
moel@326
   104
          new MenuItem("2 h"),
moel@326
   105
          new MenuItem("3 h"),
moel@326
   106
          new MenuItem("6 h"),
moel@326
   107
          new MenuItem("12 h"),
moel@326
   108
          new MenuItem("24 h") };
moel@326
   109
moel@326
   110
      foreach (MenuItem mi in timeWindowMenuItems)
moel@326
   111
        timeWindow.MenuItems.Add(mi);
moel@326
   112
moel@326
   113
      timeWindowRadioGroup = new UserRadioGroup("timeWindow", 0, 
moel@326
   114
        timeWindowMenuItems, settings);
moel@326
   115
moel@326
   116
      this.ContextMenu = new ContextMenu();
moel@326
   117
      this.ContextMenu.MenuItems.Add(timeWindow);
moel@326
   118
    }
moel@326
   119
moel@1
   120
    private List<float> GetTemperatureGrid() {
moel@1
   121
moel@1
   122
      float? minTempNullable = null;
moel@1
   123
      float? maxTempNullable = null;
moel@1
   124
      foreach (ISensor sensor in temperatures) {
moel@159
   125
        IEnumerable<SensorValue> values = sensor.Values;
moel@159
   126
        foreach (SensorValue value in values) {
moel@298
   127
          if (!float.IsNaN(value.Value)) {
moel@298
   128
            if (!minTempNullable.HasValue || minTempNullable > value.Value)
moel@298
   129
              minTempNullable = value.Value;
moel@298
   130
            if (!maxTempNullable.HasValue || maxTempNullable < value.Value)
moel@298
   131
              maxTempNullable = value.Value;
moel@298
   132
          }
moel@1
   133
        }
moel@1
   134
      }
moel@1
   135
      if (!minTempNullable.HasValue) {
moel@1
   136
        minTempNullable = 20;
moel@1
   137
        maxTempNullable = 30;
moel@1
   138
      }
moel@1
   139
moel@1
   140
      float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10;
moel@1
   141
      float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10;
moel@158
   142
      if (maxTemp == minTemp)
moel@158
   143
        maxTemp += 10;
moel@158
   144
moel@1
   145
      int countTempMax = 4;
moel@1
   146
      float deltaTemp = maxTemp - minTemp;
moel@1
   147
      int countTemp = (int)Math.Round(deltaTemp / 2);
moel@1
   148
      if (countTemp > countTempMax)
moel@1
   149
        countTemp = (int)Math.Round(deltaTemp / 5);
moel@1
   150
      if (countTemp > countTempMax)
moel@1
   151
        countTemp = (int)Math.Round(deltaTemp / 10);
moel@1
   152
      if (countTemp > countTempMax)
moel@1
   153
        countTemp = (int)Math.Round(deltaTemp / 20);
moel@1
   154
moel@1
   155
      List<float> grid = new List<float>(countTemp + 1);
moel@1
   156
      for (int i = 0; i <= countTemp; i++) {
moel@1
   157
        grid.Add(minTemp + i * deltaTemp / countTemp);
moel@1
   158
      }
moel@1
   159
      return grid;
moel@1
   160
    }
moel@1
   161
moel@1
   162
    private List<float> GetTimeGrid() {
moel@1
   163
moel@326
   164
      float maxTime;
moel@326
   165
      if (timeWindowRadioGroup.Value == 0) { // Auto
moel@326
   166
        maxTime = 5;
moel@326
   167
        if (temperatures.Count > 0) {
moel@326
   168
          IEnumerator<SensorValue> enumerator =
moel@326
   169
            temperatures[0].Values.GetEnumerator();
moel@326
   170
          if (enumerator.MoveNext()) {
moel@326
   171
            maxTime = (float)(now - enumerator.Current.Time).TotalMinutes;
moel@326
   172
          }
moel@1
   173
        }
moel@326
   174
      } else {
moel@326
   175
        float[] maxTimes = 
moel@326
   176
          { 5, 10, 20, 30, 45, 60, 90, 120, 180, 360, 720, 1440 };
moel@326
   177
moel@326
   178
        maxTime = maxTimes[timeWindowRadioGroup.Value - 1];
moel@1
   179
      }
moel@1
   180
moel@1
   181
      int countTime = 10;
moel@1
   182
      float deltaTime = 5;
moel@326
   183
      while (deltaTime + 1 <= maxTime && deltaTime < 10)
moel@1
   184
        deltaTime += 1;
moel@326
   185
      while (deltaTime + 2 <= maxTime && deltaTime < 30)
moel@1
   186
        deltaTime += 2;
moel@326
   187
      while (deltaTime + 5 <= maxTime && deltaTime < 100)
moel@1
   188
        deltaTime += 5;
moel@326
   189
      while (deltaTime + 50 <= maxTime && deltaTime < 1000)
moel@298
   190
        deltaTime += 50;
moel@326
   191
      while (deltaTime + 100 <= maxTime && deltaTime < 10000)
moel@298
   192
        deltaTime += 100;
moel@1
   193
moel@1
   194
      List<float> grid = new List<float>(countTime + 1);
moel@1
   195
      for (int i = 0; i <= countTime; i++) {
moel@1
   196
        grid.Add(i * deltaTime / countTime);
moel@1
   197
      }
moel@1
   198
      return grid;
moel@1
   199
    }
moel@1
   200
moel@1
   201
    protected override void OnPaint(PaintEventArgs e) {
moel@314
   202
      now = DateTime.UtcNow - new TimeSpan(0, 0, 4);
moel@1
   203
moel@1
   204
      List<float> timeGrid = GetTimeGrid();
moel@1
   205
      List<float> tempGrid = GetTemperatureGrid();
moel@1
   206
moel@1
   207
      Graphics g = e.Graphics;
moel@1
   208
moel@1
   209
      RectangleF r =
moel@1
   210
        new RectangleF(0, 0, Bounds.Width, Bounds.Height);
moel@1
   211
moel@1
   212
      float ml = 40;
moel@1
   213
      float mr = 15;
moel@1
   214
      float x0 = r.X + ml;
moel@1
   215
      float w = r.Width - ml - mr;
moel@1
   216
moel@1
   217
      float mt = 15;
moel@1
   218
      float mb = 28;
moel@1
   219
      float y0 = r.Y + mt;
moel@1
   220
      float h = r.Height - mt - mb;
moel@1
   221
moel@1
   222
      float leftScaleSpace = 5;
moel@1
   223
      float bottomScaleSpace = 5;
moel@158
   224
moel@1
   225
      g.Clear(Color.White);
moel@1
   226
moel@1
   227
      if (w > 0 && h > 0) {
moel@1
   228
        g.FillRectangle(lightBrush, x0, y0, w, h);
moel@1
   229
moel@1
   230
        g.SmoothingMode = SmoothingMode.HighQuality;
moel@1
   231
        for (int i = 0; i < timeGrid.Count; i++) {
moel@1
   232
          float x = x0 + i * w / (timeGrid.Count - 1);
moel@1
   233
          g.DrawLine(lightPen, x, y0, x, y0 + h);
moel@1
   234
        }
moel@1
   235
moel@1
   236
        for (int i = 0; i < tempGrid.Count; i++) {
moel@1
   237
          float y = y0 + i * h / (tempGrid.Count - 1);
moel@1
   238
          g.DrawLine(lightPen, x0, y, x0 + w, y);
moel@1
   239
        }
moel@1
   240
moel@1
   241
        float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0];
moel@1
   242
        float deltaTime = timeGrid[timeGrid.Count - 1];
moel@1
   243
        foreach (ISensor sensor in temperatures) {
moel@1
   244
          using (Pen pen = new Pen(colors[sensor])) {
moel@159
   245
            IEnumerable<SensorValue> values = sensor.Values;
moel@1
   246
            PointF last = new PointF();
moel@1
   247
            bool first = true;
moel@298
   248
            foreach (SensorValue v in values) {
moel@298
   249
              if (!float.IsNaN(v.Value)) {
moel@298
   250
                PointF point = new PointF(
moel@298
   251
                    x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime,
moel@298
   252
                    y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp);
moel@298
   253
                if (!first) 
moel@298
   254
                  g.DrawLine(pen, last, point);                
moel@298
   255
                last = point;
moel@298
   256
                first = false;
moel@298
   257
              } else {
moel@298
   258
                first = true;
moel@298
   259
              }
moel@1
   260
            }
moel@1
   261
          }
moel@1
   262
        }
moel@1
   263
moel@1
   264
        g.SmoothingMode = SmoothingMode.None;
moel@1
   265
        g.FillRectangle(Brushes.White, 0, 0, x0, r.Height);
moel@158
   266
        g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w,
moel@1
   267
          r.Height);
moel@1
   268
moel@1
   269
        for (int i = 1; i < timeGrid.Count; i++) {
moel@158
   270
          float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1);
moel@158
   271
          g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x,
moel@1
   272
            y0 + h + bottomScaleSpace, centerlower);
moel@1
   273
        }
moel@1
   274
moel@1
   275
        for (int i = 0; i < tempGrid.Count - 1; i++) {
moel@1
   276
          float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1);
moel@158
   277
          g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black,
moel@1
   278
            x0 - leftScaleSpace, y, centerleft);
moel@1
   279
        }
moel@1
   280
moel@1
   281
        g.SmoothingMode = SmoothingMode.HighQuality;
moel@1
   282
        g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0,
moel@158
   283
          lowerleft);
moel@1
   284
        g.DrawString("[min]", Font, Brushes.Black, x0 + w,
moel@158
   285
          y0 + h + bottomScaleSpace, lowerleft);
moel@1
   286
      }
moel@1
   287
    }
moel@1
   288
moel@158
   289
    public void SetSensors(List<ISensor> sensors,
moel@158
   290
      IDictionary<ISensor, Color> colors) {
moel@1
   291
      this.colors = colors;
moel@1
   292
      List<ISensor> clocks = new List<ISensor>();
moel@1
   293
      List<ISensor> temperatures = new List<ISensor>();
moel@1
   294
      List<ISensor> fans = new List<ISensor>();
moel@1
   295
      foreach (ISensor sensor in sensors)
moel@1
   296
        switch (sensor.SensorType) {
moel@1
   297
          case SensorType.Clock: clocks.Add(sensor); break;
moel@1
   298
          case SensorType.Temperature: temperatures.Add(sensor); break;
moel@1
   299
          case SensorType.Fan: fans.Add(sensor); break;
moel@1
   300
        }
moel@1
   301
      this.clocks = clocks;
moel@1
   302
      this.temperatures = temperatures;
moel@1
   303
      this.fans = fans;
moel@1
   304
      Invalidate();
moel@1
   305
    }
moel@1
   306
moel@1
   307
  }
moel@1
   308
}