GUI/PlotPanel.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
parent 298 96263190189a
child 326 a41745e3828d
permissions -rw-r--r--
Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
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@1
    49
    private DateTime now;
moel@1
    50
    private List<ISensor> clocks = new List<ISensor>();
moel@1
    51
    private List<ISensor> temperatures = new List<ISensor>();
moel@1
    52
    private List<ISensor> fans = new List<ISensor>();
moel@1
    53
    private IDictionary<ISensor, Color> colors;
moel@1
    54
moel@1
    55
    private StringFormat centerlower;
moel@1
    56
    private StringFormat centerleft;
moel@1
    57
    private StringFormat lowerleft;
moel@1
    58
    private Brush lightBrush;
moel@1
    59
    private Pen lightPen;
moel@1
    60
moel@1
    61
    public PlotPanel() {
moel@1
    62
      this.SetStyle(ControlStyles.DoubleBuffer |
moel@1
    63
        ControlStyles.UserPaint |
moel@158
    64
        ControlStyles.AllPaintingInWmPaint |
moel@1
    65
        ControlStyles.ResizeRedraw, true);
moel@1
    66
      this.UpdateStyles();
moel@1
    67
moel@1
    68
      centerlower = new StringFormat();
moel@1
    69
      centerlower.Alignment = StringAlignment.Center;
moel@1
    70
      centerlower.LineAlignment = StringAlignment.Near;
moel@1
    71
moel@1
    72
      centerleft = new StringFormat();
moel@1
    73
      centerleft.Alignment = StringAlignment.Far;
moel@1
    74
      centerleft.LineAlignment = StringAlignment.Center;
moel@1
    75
moel@1
    76
      lowerleft = new StringFormat();
moel@1
    77
      lowerleft.Alignment = StringAlignment.Far;
moel@1
    78
      lowerleft.LineAlignment = StringAlignment.Near;
moel@1
    79
moel@1
    80
      lightBrush = new SolidBrush(Color.FromArgb(245, 245, 245));
moel@1
    81
      lightPen = new Pen(Color.FromArgb(200, 200, 200));
moel@1
    82
    }
moel@1
    83
moel@1
    84
    private List<float> GetTemperatureGrid() {
moel@1
    85
moel@1
    86
      float? minTempNullable = null;
moel@1
    87
      float? maxTempNullable = null;
moel@1
    88
      foreach (ISensor sensor in temperatures) {
moel@159
    89
        IEnumerable<SensorValue> values = sensor.Values;
moel@159
    90
        foreach (SensorValue value in values) {
moel@298
    91
          if (!float.IsNaN(value.Value)) {
moel@298
    92
            if (!minTempNullable.HasValue || minTempNullable > value.Value)
moel@298
    93
              minTempNullable = value.Value;
moel@298
    94
            if (!maxTempNullable.HasValue || maxTempNullable < value.Value)
moel@298
    95
              maxTempNullable = value.Value;
moel@298
    96
          }
moel@1
    97
        }
moel@1
    98
      }
moel@1
    99
      if (!minTempNullable.HasValue) {
moel@1
   100
        minTempNullable = 20;
moel@1
   101
        maxTempNullable = 30;
moel@1
   102
      }
moel@1
   103
moel@1
   104
      float maxTemp = (float)Math.Ceiling(maxTempNullable.Value / 10) * 10;
moel@1
   105
      float minTemp = (float)Math.Floor(minTempNullable.Value / 10) * 10;
moel@158
   106
      if (maxTemp == minTemp)
moel@158
   107
        maxTemp += 10;
moel@158
   108
moel@1
   109
      int countTempMax = 4;
moel@1
   110
      float deltaTemp = maxTemp - minTemp;
moel@1
   111
      int countTemp = (int)Math.Round(deltaTemp / 2);
moel@1
   112
      if (countTemp > countTempMax)
moel@1
   113
        countTemp = (int)Math.Round(deltaTemp / 5);
moel@1
   114
      if (countTemp > countTempMax)
moel@1
   115
        countTemp = (int)Math.Round(deltaTemp / 10);
moel@1
   116
      if (countTemp > countTempMax)
moel@1
   117
        countTemp = (int)Math.Round(deltaTemp / 20);
moel@1
   118
moel@1
   119
      List<float> grid = new List<float>(countTemp + 1);
moel@1
   120
      for (int i = 0; i <= countTemp; i++) {
moel@1
   121
        grid.Add(minTemp + i * deltaTemp / countTemp);
moel@1
   122
      }
moel@1
   123
      return grid;
moel@1
   124
    }
moel@1
   125
moel@1
   126
    private List<float> GetTimeGrid() {
moel@1
   127
moel@158
   128
      float maxTime = 5;
moel@1
   129
      if (temperatures.Count > 0) {
moel@159
   130
        IEnumerator<SensorValue> enumerator =
moel@159
   131
          temperatures[0].Values.GetEnumerator();
moel@1
   132
        if (enumerator.MoveNext()) {
moel@1
   133
          maxTime = (float)(now - enumerator.Current.Time).TotalMinutes;
moel@1
   134
        }
moel@1
   135
      }
moel@1
   136
moel@1
   137
      int countTime = 10;
moel@1
   138
      float deltaTime = 5;
moel@1
   139
      while (deltaTime + 1 < maxTime && deltaTime < 10)
moel@1
   140
        deltaTime += 1;
moel@1
   141
      while (deltaTime + 2 < maxTime && deltaTime < 30)
moel@1
   142
        deltaTime += 2;
moel@1
   143
      while (deltaTime + 5 < maxTime && deltaTime < 100)
moel@1
   144
        deltaTime += 5;
moel@298
   145
      while (deltaTime + 50 < maxTime && deltaTime < 1000)
moel@298
   146
        deltaTime += 50;
moel@298
   147
      while (deltaTime + 100 < maxTime && deltaTime < 10000)
moel@298
   148
        deltaTime += 100;
moel@1
   149
moel@1
   150
      List<float> grid = new List<float>(countTime + 1);
moel@1
   151
      for (int i = 0; i <= countTime; i++) {
moel@1
   152
        grid.Add(i * deltaTime / countTime);
moel@1
   153
      }
moel@1
   154
      return grid;
moel@1
   155
    }
moel@1
   156
moel@1
   157
    protected override void OnPaint(PaintEventArgs e) {
moel@314
   158
      now = DateTime.UtcNow - new TimeSpan(0, 0, 4);
moel@1
   159
moel@1
   160
      List<float> timeGrid = GetTimeGrid();
moel@1
   161
      List<float> tempGrid = GetTemperatureGrid();
moel@1
   162
moel@1
   163
      Graphics g = e.Graphics;
moel@1
   164
moel@1
   165
      RectangleF r =
moel@1
   166
        new RectangleF(0, 0, Bounds.Width, Bounds.Height);
moel@1
   167
moel@1
   168
      float ml = 40;
moel@1
   169
      float mr = 15;
moel@1
   170
      float x0 = r.X + ml;
moel@1
   171
      float w = r.Width - ml - mr;
moel@1
   172
moel@1
   173
      float mt = 15;
moel@1
   174
      float mb = 28;
moel@1
   175
      float y0 = r.Y + mt;
moel@1
   176
      float h = r.Height - mt - mb;
moel@1
   177
moel@1
   178
      float leftScaleSpace = 5;
moel@1
   179
      float bottomScaleSpace = 5;
moel@158
   180
moel@1
   181
      g.Clear(Color.White);
moel@1
   182
moel@1
   183
      if (w > 0 && h > 0) {
moel@1
   184
        g.FillRectangle(lightBrush, x0, y0, w, h);
moel@1
   185
moel@1
   186
        g.SmoothingMode = SmoothingMode.HighQuality;
moel@1
   187
        for (int i = 0; i < timeGrid.Count; i++) {
moel@1
   188
          float x = x0 + i * w / (timeGrid.Count - 1);
moel@1
   189
          g.DrawLine(lightPen, x, y0, x, y0 + h);
moel@1
   190
        }
moel@1
   191
moel@1
   192
        for (int i = 0; i < tempGrid.Count; i++) {
moel@1
   193
          float y = y0 + i * h / (tempGrid.Count - 1);
moel@1
   194
          g.DrawLine(lightPen, x0, y, x0 + w, y);
moel@1
   195
        }
moel@1
   196
moel@1
   197
        float deltaTemp = tempGrid[tempGrid.Count - 1] - tempGrid[0];
moel@1
   198
        float deltaTime = timeGrid[timeGrid.Count - 1];
moel@1
   199
        foreach (ISensor sensor in temperatures) {
moel@1
   200
          using (Pen pen = new Pen(colors[sensor])) {
moel@159
   201
            IEnumerable<SensorValue> values = sensor.Values;
moel@1
   202
            PointF last = new PointF();
moel@1
   203
            bool first = true;
moel@298
   204
            foreach (SensorValue v in values) {
moel@298
   205
              if (!float.IsNaN(v.Value)) {
moel@298
   206
                PointF point = new PointF(
moel@298
   207
                    x0 + w - w * (float)(now - v.Time).TotalMinutes / deltaTime,
moel@298
   208
                    y0 + h - h * (v.Value - tempGrid[0]) / deltaTemp);
moel@298
   209
                if (!first) 
moel@298
   210
                  g.DrawLine(pen, last, point);                
moel@298
   211
                last = point;
moel@298
   212
                first = false;
moel@298
   213
              } else {
moel@298
   214
                first = true;
moel@298
   215
              }
moel@1
   216
            }
moel@1
   217
          }
moel@1
   218
        }
moel@1
   219
moel@1
   220
        g.SmoothingMode = SmoothingMode.None;
moel@1
   221
        g.FillRectangle(Brushes.White, 0, 0, x0, r.Height);
moel@158
   222
        g.FillRectangle(Brushes.White, x0 + w + 1, 0, r.Width - x0 - w,
moel@1
   223
          r.Height);
moel@1
   224
moel@1
   225
        for (int i = 1; i < timeGrid.Count; i++) {
moel@158
   226
          float x = x0 + (timeGrid.Count - 1 - i) * w / (timeGrid.Count - 1);
moel@158
   227
          g.DrawString(timeGrid[i].ToString(), Font, Brushes.Black, x,
moel@1
   228
            y0 + h + bottomScaleSpace, centerlower);
moel@1
   229
        }
moel@1
   230
moel@1
   231
        for (int i = 0; i < tempGrid.Count - 1; i++) {
moel@1
   232
          float y = y0 + (tempGrid.Count - 1 - i) * h / (tempGrid.Count - 1);
moel@158
   233
          g.DrawString(tempGrid[i].ToString(), Font, Brushes.Black,
moel@1
   234
            x0 - leftScaleSpace, y, centerleft);
moel@1
   235
        }
moel@1
   236
moel@1
   237
        g.SmoothingMode = SmoothingMode.HighQuality;
moel@1
   238
        g.DrawString("[°C]", Font, Brushes.Black, x0 - leftScaleSpace, y0,
moel@158
   239
          lowerleft);
moel@1
   240
        g.DrawString("[min]", Font, Brushes.Black, x0 + w,
moel@158
   241
          y0 + h + bottomScaleSpace, lowerleft);
moel@1
   242
      }
moel@1
   243
    }
moel@1
   244
moel@158
   245
    public void SetSensors(List<ISensor> sensors,
moel@158
   246
      IDictionary<ISensor, Color> colors) {
moel@1
   247
      this.colors = colors;
moel@1
   248
      List<ISensor> clocks = new List<ISensor>();
moel@1
   249
      List<ISensor> temperatures = new List<ISensor>();
moel@1
   250
      List<ISensor> fans = new List<ISensor>();
moel@1
   251
      foreach (ISensor sensor in sensors)
moel@1
   252
        switch (sensor.SensorType) {
moel@1
   253
          case SensorType.Clock: clocks.Add(sensor); break;
moel@1
   254
          case SensorType.Temperature: temperatures.Add(sensor); break;
moel@1
   255
          case SensorType.Fan: fans.Add(sensor); break;
moel@1
   256
        }
moel@1
   257
      this.clocks = clocks;
moel@1
   258
      this.temperatures = temperatures;
moel@1
   259
      this.fans = fans;
moel@1
   260
      Invalidate();
moel@1
   261
    }
moel@1
   262
moel@1
   263
  }
moel@1
   264
}