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