Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Wed, 15 Sep 2010 18:43:15 +0000
changeset 186 010d719f9245
parent 173 fb96c0ca3c2d
child 195 0ee888c485d5
permissions -rw-r--r--
Added a check to verify the FTDI chip ID before opening the T-Balancer port.
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@171
    49
    private string portName;
moel@171
    50
    private SerialPort serialPort;
moel@171
    51
moel@171
    52
    private int hardwareRevision;
moel@171
    53
    private int firmwareRevision;
moel@171
    54
    private int firmwareCRC;
moel@171
    55
moel@171
    56
    private Sensor[] fans;
moel@171
    57
    private Sensor[] controls;
moel@171
    58
    private Sensor[] temperatures;
moel@171
    59
    private Sensor[] flows;
moel@171
    60
    private Sensor[] relays;
moel@171
    61
    
moel@171
    62
    private bool available = false;
moel@171
    63
moel@172
    64
    private 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@171
    97
    private 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@171
   105
    private 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@171
   128
    private 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@171
   133
    private 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@171
   167
        }
moel@171
   168
        
moel@171
   169
        for (int i = 0; i < fanCount; i++) {
moel@171
   170
          int device = 33 + i;
moel@171
   171
          string name = ReadString(device, 'C');
moel@171
   172
          
moel@171
   173
          fans[i].Value = ReadInteger(device, 'R');
moel@171
   174
          ActivateSensor(fans[i]);
moel@171
   175
        }
moel@171
   176
moel@171
   177
        temperatures = new Sensor[temperatureCount];
moel@171
   178
        for (int i = 0; i < temperatureCount; i++) {
moel@171
   179
          int device = 49 + i;
moel@171
   180
          string name = ReadString(device, 'C');
moel@171
   181
          temperatures[i] =
moel@171
   182
            new Sensor(name, device, SensorType.Temperature, this, settings);
moel@171
   183
          int value = ReadInteger(device, 'T');
moel@171
   184
          temperatures[i].Value = 0.1f * value;
moel@171
   185
          if (value != -32768)
moel@171
   186
            ActivateSensor(temperatures[i]);
moel@171
   187
        }
moel@171
   188
moel@171
   189
        flows = new Sensor[flowCount];
moel@171
   190
        for (int i = 0; i < flowCount; i++) {
moel@171
   191
          int device = 65 + i;
moel@171
   192
          string name = ReadString(device, 'C');
moel@171
   193
          flows[i] = new Sensor(name, device, SensorType.Flow, this, settings);
moel@171
   194
          flows[i].Value = 0.1f * ReadInteger(device, 'L');
moel@171
   195
          ActivateSensor(flows[i]);
moel@171
   196
        }
moel@171
   197
moel@171
   198
        relays = new Sensor[relayCount];
moel@171
   199
        for (int i = 0; i < relayCount; i++) {
moel@171
   200
          int device = 81 + i;
moel@171
   201
          string name = ReadString(device, 'C');
moel@171
   202
          relays[i] = 
moel@171
   203
            new Sensor(name, device, SensorType.Control, this, settings);
moel@171
   204
          relays[i].Value = 100 * ReadInteger(device, 'S');
moel@171
   205
          ActivateSensor(relays[i]);
moel@171
   206
        }
moel@171
   207
moel@171
   208
        // set the update rate to 2 Hz
moel@171
   209
        WriteInteger(0, 'L', 2);
moel@171
   210
        
moel@171
   211
        available = true;
moel@171
   212
moel@171
   213
      } catch (IOException) { } catch (TimeoutException) { }      
moel@171
   214
    }
moel@171
   215
moel@171
   216
    public override HardwareType HardwareType {
moel@171
   217
      get { return HardwareType.Heatmaster; }
moel@171
   218
    }
moel@171
   219
moel@171
   220
    public override Identifier Identifier {
moel@171
   221
      get {
moel@171
   222
        return new Identifier("heatmaster",
moel@171
   223
          serialPort.PortName.TrimStart(new char[]{'/'}).ToLowerInvariant());
moel@171
   224
      }
moel@171
   225
    }
moel@171
   226
moel@171
   227
    public override string Name {
moel@171
   228
      get { return "Heatmaster"; }
moel@171
   229
    }
moel@171
   230
moel@172
   231
    private void ProcessUpdateLine(string line) {
moel@172
   232
      Match match = Regex.Match(line, @">\[0:(\d+)\]([0-9:\|-]+)");
moel@172
   233
      if (match.Success) {
moel@172
   234
        int device;
moel@172
   235
        if (int.TryParse(match.Groups[1].Value, out device)) {
moel@172
   236
          foreach (string s in match.Groups[2].Value.Split('|')) {
moel@172
   237
            string[] strings = s.Split(':');
moel@172
   238
            int[] ints = new int[strings.Length];
moel@172
   239
            for (int i = 0; i < ints.Length; i++)
moel@182
   240
              ints[i] = int.Parse(strings[i], CultureInfo.InvariantCulture);
moel@172
   241
            switch (device) {
moel@172
   242
              case 32:
moel@172
   243
                if (ints.Length == 3 && ints[0] <= fans.Length) {
moel@172
   244
                  fans[ints[0] - 1].Value = ints[1];
moel@172
   245
                  controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
moel@172
   246
                }
moel@172
   247
                break;
moel@172
   248
              case 48:
moel@172
   249
                if (ints.Length == 2 && ints[0] <= temperatures.Length)
moel@172
   250
                  temperatures[ints[0] - 1].Value = 0.1f * ints[1];
moel@172
   251
                break;
moel@172
   252
              case 64:
moel@172
   253
                if (ints.Length == 3 && ints[0] <= flows.Length)
moel@172
   254
                  flows[ints[0] - 1].Value = 0.1f * ints[1];
moel@172
   255
                break;
moel@172
   256
              case 80:
moel@172
   257
                if (ints.Length == 2 && ints[0] <= relays.Length)
moel@172
   258
                  relays[ints[0] - 1].Value = 100 * ints[1];
moel@172
   259
                break;
moel@172
   260
            }
moel@172
   261
          }
moel@172
   262
        }
moel@172
   263
      }
moel@172
   264
    }
moel@172
   265
moel@171
   266
    public override void Update() {
moel@171
   267
      if (!available)
moel@171
   268
        return;
moel@171
   269
moel@171
   270
      while (serialPort.BytesToRead > 0) {
moel@172
   271
        byte b = (byte)serialPort.ReadByte();
moel@172
   272
        if (b == 0x0D) {
moel@172
   273
          ProcessUpdateLine(buffer.ToString());
moel@172
   274
          buffer.Length = 0;
moel@172
   275
        } else {
moel@172
   276
          buffer.Append((char)b);
moel@171
   277
        }
moel@171
   278
      }
moel@171
   279
    }
moel@171
   280
moel@171
   281
    public override string GetReport() {
moel@171
   282
      StringBuilder r = new StringBuilder();
moel@171
   283
moel@171
   284
      r.AppendLine("Heatmaster");
moel@171
   285
      r.AppendLine();
moel@171
   286
      r.Append("Port: ");
moel@171
   287
      r.AppendLine(portName);
moel@171
   288
      r.Append("Hardware Revision: ");
moel@182
   289
      r.AppendLine(hardwareRevision.ToString(CultureInfo.InvariantCulture));
moel@171
   290
      r.Append("Firmware Revision: ");
moel@182
   291
      r.AppendLine(firmwareRevision.ToString(CultureInfo.InvariantCulture));
moel@171
   292
      r.Append("Firmware CRC: ");
moel@182
   293
      r.AppendLine(firmwareCRC.ToString(CultureInfo.InvariantCulture));
moel@171
   294
      r.AppendLine();
moel@171
   295
moel@171
   296
      return r.ToString();
moel@171
   297
    }
moel@171
   298
moel@171
   299
    public void Close() {
moel@171
   300
      serialPort.Close();
moel@182
   301
      serialPort.Dispose();
moel@182
   302
      serialPort = null;
moel@182
   303
    }
moel@182
   304
moel@182
   305
    public void Dispose() {
moel@182
   306
      if (serialPort != null) {
moel@182
   307
        serialPort.Dispose();
moel@182
   308
        serialPort = null;
moel@182
   309
      }
moel@171
   310
    }
moel@171
   311
  }
moel@171
   312
}