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