Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Thu, 11 Nov 2010 21:22:24 +0000
changeset 241 52007c404f32
parent 182 4801e9eaf979
child 260 5a6ac1d1bc0e
permissions -rw-r--r--
Fixed a problem, where the MainForm location and size was lost when the application is started minimized and exited without ever showing the form. This caused MainForm_Load to be never called (location and size was not loaded), but the default size and location were still saved. The new implementation only saves the location and size when one of the two is changed.
moel@171
     1
/*
moel@171
     2
  
moel@171
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@171
     4
moel@171
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@171
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@171
     7
  the License. You may obtain a copy of the License at
moel@171
     8
 
moel@171
     9
  http://www.mozilla.org/MPL/
moel@171
    10
moel@171
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@171
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@171
    13
  for the specific language governing rights and limitations under the License.
moel@171
    14
moel@171
    15
  The Original Code is the Open Hardware Monitor code.
moel@171
    16
moel@171
    17
  The Initial Developer of the Original Code is 
moel@171
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@171
    19
  Portions created by the Initial Developer are Copyright (C) 2010
moel@171
    20
  the Initial Developer. All Rights Reserved.
moel@171
    21
moel@171
    22
  Contributor(s):
moel@171
    23
moel@171
    24
  Alternatively, the contents of this file may be used under the terms of
moel@171
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@171
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@171
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@171
    28
  of those above. If you wish to allow use of your version of this file only
moel@171
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@171
    30
  use your version of this file under the terms of the MPL, indicate your
moel@171
    31
  decision by deleting the provisions above and replace them with the notice
moel@171
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@171
    33
  the provisions above, a recipient may use your version of this file under
moel@171
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@171
    35
 
moel@171
    36
*/
moel@171
    37
moel@171
    38
using System;
moel@182
    39
using System.Globalization;
moel@171
    40
using System.IO;
moel@171
    41
using System.IO.Ports;
moel@171
    42
using System.Text;
moel@171
    43
using System.Text.RegularExpressions;
moel@171
    44
using System.Threading;
moel@171
    45
moel@171
    46
namespace OpenHardwareMonitor.Hardware.Heatmaster {
moel@182
    47
  internal class Heatmaster : Hardware, IDisposable {
moel@171
    48
moel@195
    49
    private readonly string portName;
moel@171
    50
    private SerialPort serialPort;
moel@171
    51
moel@195
    52
    private readonly int hardwareRevision;
moel@195
    53
    private readonly int firmwareRevision;
moel@195
    54
    private readonly int firmwareCRC;
moel@171
    55
moel@195
    56
    private readonly Sensor[] fans;
moel@195
    57
    private readonly Sensor[] controls;
moel@195
    58
    private readonly Sensor[] temperatures;
moel@195
    59
    private readonly Sensor[] flows;
moel@195
    60
    private readonly Sensor[] relays;
moel@171
    61
    
moel@195
    62
    private readonly bool available;
moel@171
    63
moel@195
    64
    private readonly StringBuilder buffer = new StringBuilder();
moel@172
    65
moel@171
    66
    private string ReadLine(int timeout) {
moel@171
    67
      int i = 0;
moel@171
    68
      StringBuilder builder = new StringBuilder();
moel@171
    69
      while (i <= timeout) {
moel@171
    70
        while (serialPort.BytesToRead > 0) {
moel@171
    71
          byte b = (byte)serialPort.ReadByte();
moel@171
    72
          switch (b) {
moel@171
    73
            case 0xAA: return ((char)b).ToString();
moel@171
    74
            case 0x0D: return builder.ToString();
moel@171
    75
            default: builder.Append((char)b); break;
moel@171
    76
          }
moel@171
    77
        }
moel@171
    78
        i++;
moel@171
    79
        Thread.Sleep(1);
moel@171
    80
      }
moel@171
    81
      throw new TimeoutException();
moel@171
    82
    }
moel@171
    83
moel@171
    84
    private string ReadField(int device, char field) {
moel@171
    85
      serialPort.WriteLine("[0:" + device + "]R" + field);
moel@171
    86
      for (int i = 0; i < 5; i++) {
moel@172
    87
        string s = ReadLine(200);
moel@182
    88
        Match match = Regex.Match(s, @"-\[0:" + 
moel@182
    89
          device.ToString(CultureInfo.InvariantCulture) + @"\]R" +
moel@182
    90
          Regex.Escape(field.ToString(CultureInfo.InvariantCulture)) + ":(.*)");
moel@171
    91
        if (match.Success) 
moel@171
    92
          return match.Groups[1].Value;
moel@171
    93
      }
moel@171
    94
      return null;
moel@171
    95
    }
moel@171
    96
moel@195
    97
    protected string ReadString(int device, char field) {
moel@171
    98
      string s = ReadField(device, field);
moel@171
    99
      if (s != null && s[0] == '"' && s[s.Length - 1] == '"')
moel@171
   100
        return s.Substring(1, s.Length - 2);
moel@171
   101
      else
moel@171
   102
        return null;
moel@171
   103
    }
moel@171
   104
moel@195
   105
    protected int ReadInteger(int device, char field) {
moel@171
   106
      string s = ReadField(device, field);      
moel@171
   107
      int i;
moel@171
   108
      if (int.TryParse(s, out i))
moel@171
   109
        return i;
moel@171
   110
      else
moel@171
   111
        return 0;
moel@171
   112
    }
moel@171
   113
moel@171
   114
    private bool WriteField(int device, char field, string value) {
moel@171
   115
      serialPort.WriteLine("[0:" + device + "]W" + field + ":" + value);
moel@171
   116
      for (int i = 0; i < 5; i++) {
moel@172
   117
        string s = ReadLine(200);
moel@182
   118
        Match match = Regex.Match(s, @"-\[0:" + 
moel@182
   119
          device.ToString(CultureInfo.InvariantCulture) + @"\]W" + 
moel@182
   120
          Regex.Escape(field.ToString(CultureInfo.InvariantCulture)) +
moel@182
   121
          ":" + value);
moel@171
   122
        if (match.Success)
moel@171
   123
          return true;
moel@171
   124
      }
moel@171
   125
      return false;
moel@171
   126
    }
moel@171
   127
moel@195
   128
    protected bool WriteInteger(int device, char field, int value) {
moel@182
   129
      return WriteField(device, field, 
moel@182
   130
        value.ToString(CultureInfo.InvariantCulture));
moel@171
   131
    }
moel@171
   132
moel@195
   133
    protected bool WriteString(int device, char field, string value) {
moel@171
   134
      return WriteField(device, field, '"' + value + '"');
moel@171
   135
    }
moel@171
   136
moel@171
   137
    public Heatmaster(string portName, ISettings settings) {
moel@171
   138
moel@171
   139
      this.portName = portName;
moel@171
   140
      try {
moel@171
   141
        serialPort = new SerialPort(portName, 38400, Parity.None, 8,
moel@171
   142
          StopBits.One);
moel@171
   143
        serialPort.Open();
moel@171
   144
        serialPort.NewLine = ((char)0x0D).ToString();
moel@171
   145
        
moel@171
   146
        hardwareRevision = ReadInteger(0, 'H');
moel@171
   147
        firmwareRevision = ReadInteger(0, 'V');
moel@171
   148
        firmwareCRC = ReadInteger(0, 'C');
moel@171
   149
moel@173
   150
        int fanCount = Math.Min(ReadInteger(32, '?'), 4);
moel@173
   151
        int temperatureCount = Math.Min(ReadInteger(48, '?'), 6);
moel@173
   152
        int flowCount = Math.Min(ReadInteger(64, '?'), 1);
moel@173
   153
        int relayCount =  Math.Min(ReadInteger(80, '?'), 1);
moel@171
   154
moel@171
   155
        fans = new Sensor[fanCount];
moel@171
   156
        controls = new Sensor[fanCount];
moel@171
   157
        for (int i = 0; i < fanCount; i++) {
moel@171
   158
          int device = 33 + i;
moel@171
   159
          string name = ReadString(device, 'C');
moel@171
   160
          fans[i] = new Sensor(name, device, SensorType.Fan, this, settings);          
moel@171
   161
          fans[i].Value = ReadInteger(device, 'R');
moel@171
   162
          ActivateSensor(fans[i]);
moel@171
   163
          controls[i] =
moel@171
   164
            new Sensor(name, device, SensorType.Control, this, settings);
moel@171
   165
          controls[i].Value = (100 / 255.0f) * ReadInteger(device, 'P');
moel@171
   166
          ActivateSensor(controls[i]);
moel@195
   167
        }       
moel@171
   168
moel@171
   169
        temperatures = new Sensor[temperatureCount];
moel@171
   170
        for (int i = 0; i < temperatureCount; i++) {
moel@171
   171
          int device = 49 + i;
moel@171
   172
          string name = ReadString(device, 'C');
moel@171
   173
          temperatures[i] =
moel@171
   174
            new Sensor(name, device, SensorType.Temperature, this, settings);
moel@171
   175
          int value = ReadInteger(device, 'T');
moel@171
   176
          temperatures[i].Value = 0.1f * value;
moel@171
   177
          if (value != -32768)
moel@171
   178
            ActivateSensor(temperatures[i]);
moel@171
   179
        }
moel@171
   180
moel@171
   181
        flows = new Sensor[flowCount];
moel@171
   182
        for (int i = 0; i < flowCount; i++) {
moel@171
   183
          int device = 65 + i;
moel@171
   184
          string name = ReadString(device, 'C');
moel@171
   185
          flows[i] = new Sensor(name, device, SensorType.Flow, this, settings);
moel@171
   186
          flows[i].Value = 0.1f * ReadInteger(device, 'L');
moel@171
   187
          ActivateSensor(flows[i]);
moel@171
   188
        }
moel@171
   189
moel@171
   190
        relays = new Sensor[relayCount];
moel@171
   191
        for (int i = 0; i < relayCount; i++) {
moel@171
   192
          int device = 81 + i;
moel@171
   193
          string name = ReadString(device, 'C');
moel@171
   194
          relays[i] = 
moel@171
   195
            new Sensor(name, device, SensorType.Control, this, settings);
moel@171
   196
          relays[i].Value = 100 * ReadInteger(device, 'S');
moel@171
   197
          ActivateSensor(relays[i]);
moel@171
   198
        }
moel@171
   199
moel@171
   200
        // set the update rate to 2 Hz
moel@171
   201
        WriteInteger(0, 'L', 2);
moel@171
   202
        
moel@171
   203
        available = true;
moel@171
   204
moel@171
   205
      } catch (IOException) { } catch (TimeoutException) { }      
moel@171
   206
    }
moel@171
   207
moel@171
   208
    public override HardwareType HardwareType {
moel@171
   209
      get { return HardwareType.Heatmaster; }
moel@171
   210
    }
moel@171
   211
moel@171
   212
    public override Identifier Identifier {
moel@171
   213
      get {
moel@171
   214
        return new Identifier("heatmaster",
moel@195
   215
          serialPort.PortName.TrimStart(new [] {'/'}).ToLowerInvariant());
moel@171
   216
      }
moel@171
   217
    }
moel@171
   218
moel@171
   219
    public override string Name {
moel@171
   220
      get { return "Heatmaster"; }
moel@171
   221
    }
moel@171
   222
moel@172
   223
    private void ProcessUpdateLine(string line) {
moel@172
   224
      Match match = Regex.Match(line, @">\[0:(\d+)\]([0-9:\|-]+)");
moel@172
   225
      if (match.Success) {
moel@172
   226
        int device;
moel@172
   227
        if (int.TryParse(match.Groups[1].Value, out device)) {
moel@172
   228
          foreach (string s in match.Groups[2].Value.Split('|')) {
moel@172
   229
            string[] strings = s.Split(':');
moel@172
   230
            int[] ints = new int[strings.Length];
moel@172
   231
            for (int i = 0; i < ints.Length; i++)
moel@182
   232
              ints[i] = int.Parse(strings[i], CultureInfo.InvariantCulture);
moel@172
   233
            switch (device) {
moel@172
   234
              case 32:
moel@172
   235
                if (ints.Length == 3 && ints[0] <= fans.Length) {
moel@172
   236
                  fans[ints[0] - 1].Value = ints[1];
moel@172
   237
                  controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
moel@172
   238
                }
moel@172
   239
                break;
moel@172
   240
              case 48:
moel@172
   241
                if (ints.Length == 2 && ints[0] <= temperatures.Length)
moel@172
   242
                  temperatures[ints[0] - 1].Value = 0.1f * ints[1];
moel@172
   243
                break;
moel@172
   244
              case 64:
moel@172
   245
                if (ints.Length == 3 && ints[0] <= flows.Length)
moel@172
   246
                  flows[ints[0] - 1].Value = 0.1f * ints[1];
moel@172
   247
                break;
moel@172
   248
              case 80:
moel@172
   249
                if (ints.Length == 2 && ints[0] <= relays.Length)
moel@172
   250
                  relays[ints[0] - 1].Value = 100 * ints[1];
moel@172
   251
                break;
moel@172
   252
            }
moel@172
   253
          }
moel@172
   254
        }
moel@172
   255
      }
moel@172
   256
    }
moel@172
   257
moel@171
   258
    public override void Update() {
moel@171
   259
      if (!available)
moel@171
   260
        return;
moel@171
   261
moel@171
   262
      while (serialPort.BytesToRead > 0) {
moel@172
   263
        byte b = (byte)serialPort.ReadByte();
moel@172
   264
        if (b == 0x0D) {
moel@172
   265
          ProcessUpdateLine(buffer.ToString());
moel@172
   266
          buffer.Length = 0;
moel@172
   267
        } else {
moel@172
   268
          buffer.Append((char)b);
moel@171
   269
        }
moel@171
   270
      }
moel@171
   271
    }
moel@171
   272
moel@171
   273
    public override string GetReport() {
moel@171
   274
      StringBuilder r = new StringBuilder();
moel@171
   275
moel@171
   276
      r.AppendLine("Heatmaster");
moel@171
   277
      r.AppendLine();
moel@171
   278
      r.Append("Port: ");
moel@171
   279
      r.AppendLine(portName);
moel@171
   280
      r.Append("Hardware Revision: ");
moel@182
   281
      r.AppendLine(hardwareRevision.ToString(CultureInfo.InvariantCulture));
moel@171
   282
      r.Append("Firmware Revision: ");
moel@182
   283
      r.AppendLine(firmwareRevision.ToString(CultureInfo.InvariantCulture));
moel@171
   284
      r.Append("Firmware CRC: ");
moel@182
   285
      r.AppendLine(firmwareCRC.ToString(CultureInfo.InvariantCulture));
moel@171
   286
      r.AppendLine();
moel@171
   287
moel@171
   288
      return r.ToString();
moel@171
   289
    }
moel@171
   290
moel@171
   291
    public void Close() {
moel@171
   292
      serialPort.Close();
moel@182
   293
      serialPort.Dispose();
moel@182
   294
      serialPort = null;
moel@182
   295
    }
moel@182
   296
moel@182
   297
    public void Dispose() {
moel@182
   298
      if (serialPort != null) {
moel@182
   299
        serialPort.Dispose();
moel@182
   300
        serialPort = null;
moel@182
   301
      }
moel@171
   302
    }
moel@171
   303
  }
moel@171
   304
}