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