Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Sun, 27 May 2012 14:23:31 +0000
changeset 344 3145aadca3d2
parent 298 96263190189a
permissions -rw-r--r--
Changed the license to the Mozilla Public License 2.0 and update the licensing information.
moel@171
     1
/*
moel@171
     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@171
     6
 
moel@344
     7
  Copyright (C) 2010-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344
     8
	
moel@171
     9
*/
moel@171
    10
moel@171
    11
using System;
moel@182
    12
using System.Globalization;
moel@171
    13
using System.IO;
moel@171
    14
using System.IO.Ports;
moel@171
    15
using System.Text;
moel@171
    16
using System.Text.RegularExpressions;
moel@171
    17
using System.Threading;
moel@171
    18
moel@171
    19
namespace OpenHardwareMonitor.Hardware.Heatmaster {
moel@182
    20
  internal class Heatmaster : Hardware, IDisposable {
moel@171
    21
moel@195
    22
    private readonly string portName;
moel@171
    23
    private SerialPort serialPort;
moel@171
    24
moel@195
    25
    private readonly int hardwareRevision;
moel@195
    26
    private readonly int firmwareRevision;
moel@195
    27
    private readonly int firmwareCRC;
moel@171
    28
moel@195
    29
    private readonly Sensor[] fans;
moel@195
    30
    private readonly Sensor[] controls;
moel@195
    31
    private readonly Sensor[] temperatures;
moel@195
    32
    private readonly Sensor[] flows;
moel@195
    33
    private readonly Sensor[] relays;
moel@171
    34
    
moel@195
    35
    private readonly bool available;
moel@171
    36
moel@195
    37
    private readonly StringBuilder buffer = new StringBuilder();
moel@172
    38
moel@171
    39
    private string ReadLine(int timeout) {
moel@171
    40
      int i = 0;
moel@171
    41
      StringBuilder builder = new StringBuilder();
moel@171
    42
      while (i <= timeout) {
moel@171
    43
        while (serialPort.BytesToRead > 0) {
moel@171
    44
          byte b = (byte)serialPort.ReadByte();
moel@171
    45
          switch (b) {
moel@171
    46
            case 0xAA: return ((char)b).ToString();
moel@171
    47
            case 0x0D: return builder.ToString();
moel@171
    48
            default: builder.Append((char)b); break;
moel@171
    49
          }
moel@171
    50
        }
moel@171
    51
        i++;
moel@171
    52
        Thread.Sleep(1);
moel@171
    53
      }
moel@171
    54
      throw new TimeoutException();
moel@171
    55
    }
moel@171
    56
moel@171
    57
    private string ReadField(int device, char field) {
moel@171
    58
      serialPort.WriteLine("[0:" + device + "]R" + field);
moel@171
    59
      for (int i = 0; i < 5; i++) {
moel@172
    60
        string s = ReadLine(200);
moel@182
    61
        Match match = Regex.Match(s, @"-\[0:" + 
moel@182
    62
          device.ToString(CultureInfo.InvariantCulture) + @"\]R" +
moel@182
    63
          Regex.Escape(field.ToString(CultureInfo.InvariantCulture)) + ":(.*)");
moel@171
    64
        if (match.Success) 
moel@171
    65
          return match.Groups[1].Value;
moel@171
    66
      }
moel@171
    67
      return null;
moel@171
    68
    }
moel@171
    69
moel@195
    70
    protected string ReadString(int device, char field) {
moel@171
    71
      string s = ReadField(device, field);
moel@171
    72
      if (s != null && s[0] == '"' && s[s.Length - 1] == '"')
moel@171
    73
        return s.Substring(1, s.Length - 2);
moel@171
    74
      else
moel@171
    75
        return null;
moel@171
    76
    }
moel@171
    77
moel@195
    78
    protected int ReadInteger(int device, char field) {
moel@171
    79
      string s = ReadField(device, field);      
moel@171
    80
      int i;
moel@171
    81
      if (int.TryParse(s, out i))
moel@171
    82
        return i;
moel@171
    83
      else
moel@171
    84
        return 0;
moel@171
    85
    }
moel@171
    86
moel@171
    87
    private bool WriteField(int device, char field, string value) {
moel@171
    88
      serialPort.WriteLine("[0:" + device + "]W" + field + ":" + value);
moel@171
    89
      for (int i = 0; i < 5; i++) {
moel@172
    90
        string s = ReadLine(200);
moel@182
    91
        Match match = Regex.Match(s, @"-\[0:" + 
moel@182
    92
          device.ToString(CultureInfo.InvariantCulture) + @"\]W" + 
moel@182
    93
          Regex.Escape(field.ToString(CultureInfo.InvariantCulture)) +
moel@182
    94
          ":" + value);
moel@171
    95
        if (match.Success)
moel@171
    96
          return true;
moel@171
    97
      }
moel@171
    98
      return false;
moel@171
    99
    }
moel@171
   100
moel@195
   101
    protected bool WriteInteger(int device, char field, int value) {
moel@182
   102
      return WriteField(device, field, 
moel@182
   103
        value.ToString(CultureInfo.InvariantCulture));
moel@171
   104
    }
moel@171
   105
moel@195
   106
    protected bool WriteString(int device, char field, string value) {
moel@171
   107
      return WriteField(device, field, '"' + value + '"');
moel@171
   108
    }
moel@171
   109
moel@275
   110
    public Heatmaster(string portName, ISettings settings) 
moel@275
   111
      : base("Heatmaster", new Identifier("heatmaster",
moel@275
   112
        portName.TrimStart(new [] {'/'}).ToLowerInvariant()), settings)
moel@275
   113
    {
moel@171
   114
      this.portName = portName;
moel@171
   115
      try {
moel@171
   116
        serialPort = new SerialPort(portName, 38400, Parity.None, 8,
moel@171
   117
          StopBits.One);
moel@171
   118
        serialPort.Open();
moel@171
   119
        serialPort.NewLine = ((char)0x0D).ToString();
moel@171
   120
        
moel@171
   121
        hardwareRevision = ReadInteger(0, 'H');
moel@171
   122
        firmwareRevision = ReadInteger(0, 'V');
moel@171
   123
        firmwareCRC = ReadInteger(0, 'C');
moel@171
   124
moel@173
   125
        int fanCount = Math.Min(ReadInteger(32, '?'), 4);
moel@173
   126
        int temperatureCount = Math.Min(ReadInteger(48, '?'), 6);
moel@173
   127
        int flowCount = Math.Min(ReadInteger(64, '?'), 1);
moel@173
   128
        int relayCount =  Math.Min(ReadInteger(80, '?'), 1);
moel@171
   129
moel@171
   130
        fans = new Sensor[fanCount];
moel@171
   131
        controls = new Sensor[fanCount];
moel@171
   132
        for (int i = 0; i < fanCount; i++) {
moel@171
   133
          int device = 33 + i;
moel@171
   134
          string name = ReadString(device, 'C');
moel@171
   135
          fans[i] = new Sensor(name, device, SensorType.Fan, this, settings);          
moel@171
   136
          fans[i].Value = ReadInteger(device, 'R');
moel@171
   137
          ActivateSensor(fans[i]);
moel@171
   138
          controls[i] =
moel@171
   139
            new Sensor(name, device, SensorType.Control, this, settings);
moel@171
   140
          controls[i].Value = (100 / 255.0f) * ReadInteger(device, 'P');
moel@171
   141
          ActivateSensor(controls[i]);
moel@195
   142
        }       
moel@171
   143
moel@171
   144
        temperatures = new Sensor[temperatureCount];
moel@171
   145
        for (int i = 0; i < temperatureCount; i++) {
moel@171
   146
          int device = 49 + i;
moel@171
   147
          string name = ReadString(device, 'C');
moel@171
   148
          temperatures[i] =
moel@171
   149
            new Sensor(name, device, SensorType.Temperature, this, settings);
moel@171
   150
          int value = ReadInteger(device, 'T');
moel@171
   151
          temperatures[i].Value = 0.1f * value;
moel@171
   152
          if (value != -32768)
moel@171
   153
            ActivateSensor(temperatures[i]);
moel@171
   154
        }
moel@171
   155
moel@171
   156
        flows = new Sensor[flowCount];
moel@171
   157
        for (int i = 0; i < flowCount; i++) {
moel@171
   158
          int device = 65 + i;
moel@171
   159
          string name = ReadString(device, 'C');
moel@171
   160
          flows[i] = new Sensor(name, device, SensorType.Flow, this, settings);
moel@171
   161
          flows[i].Value = 0.1f * ReadInteger(device, 'L');
moel@171
   162
          ActivateSensor(flows[i]);
moel@171
   163
        }
moel@171
   164
moel@171
   165
        relays = new Sensor[relayCount];
moel@171
   166
        for (int i = 0; i < relayCount; i++) {
moel@171
   167
          int device = 81 + i;
moel@171
   168
          string name = ReadString(device, 'C');
moel@171
   169
          relays[i] = 
moel@171
   170
            new Sensor(name, device, SensorType.Control, this, settings);
moel@171
   171
          relays[i].Value = 100 * ReadInteger(device, 'S');
moel@171
   172
          ActivateSensor(relays[i]);
moel@171
   173
        }
moel@171
   174
moel@171
   175
        // set the update rate to 2 Hz
moel@171
   176
        WriteInteger(0, 'L', 2);
moel@171
   177
        
moel@171
   178
        available = true;
moel@171
   179
moel@171
   180
      } catch (IOException) { } catch (TimeoutException) { }      
moel@171
   181
    }
moel@171
   182
moel@171
   183
    public override HardwareType HardwareType {
moel@171
   184
      get { return HardwareType.Heatmaster; }
moel@171
   185
    }
moel@171
   186
moel@172
   187
    private void ProcessUpdateLine(string line) {
moel@172
   188
      Match match = Regex.Match(line, @">\[0:(\d+)\]([0-9:\|-]+)");
moel@172
   189
      if (match.Success) {
moel@172
   190
        int device;
moel@172
   191
        if (int.TryParse(match.Groups[1].Value, out device)) {
moel@172
   192
          foreach (string s in match.Groups[2].Value.Split('|')) {
moel@172
   193
            string[] strings = s.Split(':');
moel@260
   194
            int[] ints = new int[strings.Length];            
moel@260
   195
            bool valid = true;
moel@172
   196
            for (int i = 0; i < ints.Length; i++)
moel@260
   197
              if (!int.TryParse(strings[i], out ints[i])) {
moel@260
   198
                valid = false;
moel@260
   199
                break;
moel@260
   200
              }
moel@260
   201
            if (!valid)
moel@260
   202
              continue; 
moel@172
   203
            switch (device) {
moel@172
   204
              case 32:
moel@172
   205
                if (ints.Length == 3 && ints[0] <= fans.Length) {
moel@172
   206
                  fans[ints[0] - 1].Value = ints[1];
moel@172
   207
                  controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
moel@172
   208
                }
moel@172
   209
                break;
moel@172
   210
              case 48:
moel@172
   211
                if (ints.Length == 2 && ints[0] <= temperatures.Length)
moel@172
   212
                  temperatures[ints[0] - 1].Value = 0.1f * ints[1];
moel@172
   213
                break;
moel@172
   214
              case 64:
moel@172
   215
                if (ints.Length == 3 && ints[0] <= flows.Length)
moel@172
   216
                  flows[ints[0] - 1].Value = 0.1f * ints[1];
moel@172
   217
                break;
moel@172
   218
              case 80:
moel@172
   219
                if (ints.Length == 2 && ints[0] <= relays.Length)
moel@172
   220
                  relays[ints[0] - 1].Value = 100 * ints[1];
moel@172
   221
                break;
moel@172
   222
            }
moel@172
   223
          }
moel@172
   224
        }
moel@172
   225
      }
moel@172
   226
    }
moel@172
   227
moel@171
   228
    public override void Update() {
moel@171
   229
      if (!available)
moel@171
   230
        return;
moel@171
   231
moel@261
   232
      while (serialPort.IsOpen &&  serialPort.BytesToRead > 0) {
moel@172
   233
        byte b = (byte)serialPort.ReadByte();
moel@172
   234
        if (b == 0x0D) {
moel@172
   235
          ProcessUpdateLine(buffer.ToString());
moel@172
   236
          buffer.Length = 0;
moel@172
   237
        } else {
moel@172
   238
          buffer.Append((char)b);
moel@171
   239
        }
moel@171
   240
      }
moel@171
   241
    }
moel@171
   242
moel@171
   243
    public override string GetReport() {
moel@171
   244
      StringBuilder r = new StringBuilder();
moel@171
   245
moel@171
   246
      r.AppendLine("Heatmaster");
moel@171
   247
      r.AppendLine();
moel@171
   248
      r.Append("Port: ");
moel@171
   249
      r.AppendLine(portName);
moel@171
   250
      r.Append("Hardware Revision: ");
moel@182
   251
      r.AppendLine(hardwareRevision.ToString(CultureInfo.InvariantCulture));
moel@171
   252
      r.Append("Firmware Revision: ");
moel@182
   253
      r.AppendLine(firmwareRevision.ToString(CultureInfo.InvariantCulture));
moel@171
   254
      r.Append("Firmware CRC: ");
moel@182
   255
      r.AppendLine(firmwareCRC.ToString(CultureInfo.InvariantCulture));
moel@171
   256
      r.AppendLine();
moel@171
   257
moel@171
   258
      return r.ToString();
moel@171
   259
    }
moel@171
   260
moel@298
   261
    public override void Close() {
moel@171
   262
      serialPort.Close();
moel@182
   263
      serialPort.Dispose();
moel@182
   264
      serialPort = null;
moel@298
   265
      base.Close();
moel@182
   266
    }
moel@182
   267
moel@182
   268
    public void Dispose() {
moel@182
   269
      if (serialPort != null) {
moel@182
   270
        serialPort.Dispose();
moel@182
   271
        serialPort = null;
moel@182
   272
      }
moel@171
   273
    }
moel@171
   274
  }
moel@171
   275
}