Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Sun, 15 May 2011 16:44:14 +0000
changeset 284 075a1822ef34
parent 261 ddb02fd788ec
child 298 96263190189a
permissions -rw-r--r--
Fixed Issue 219.
     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       : base("Heatmaster", new Identifier("heatmaster",
   139         portName.TrimStart(new [] {'/'}).ToLowerInvariant()), settings)
   140     {
   141       this.portName = portName;
   142       try {
   143         serialPort = new SerialPort(portName, 38400, Parity.None, 8,
   144           StopBits.One);
   145         serialPort.Open();
   146         serialPort.NewLine = ((char)0x0D).ToString();
   147         
   148         hardwareRevision = ReadInteger(0, 'H');
   149         firmwareRevision = ReadInteger(0, 'V');
   150         firmwareCRC = ReadInteger(0, 'C');
   151 
   152         int fanCount = Math.Min(ReadInteger(32, '?'), 4);
   153         int temperatureCount = Math.Min(ReadInteger(48, '?'), 6);
   154         int flowCount = Math.Min(ReadInteger(64, '?'), 1);
   155         int relayCount =  Math.Min(ReadInteger(80, '?'), 1);
   156 
   157         fans = new Sensor[fanCount];
   158         controls = new Sensor[fanCount];
   159         for (int i = 0; i < fanCount; i++) {
   160           int device = 33 + i;
   161           string name = ReadString(device, 'C');
   162           fans[i] = new Sensor(name, device, SensorType.Fan, this, settings);          
   163           fans[i].Value = ReadInteger(device, 'R');
   164           ActivateSensor(fans[i]);
   165           controls[i] =
   166             new Sensor(name, device, SensorType.Control, this, settings);
   167           controls[i].Value = (100 / 255.0f) * ReadInteger(device, 'P');
   168           ActivateSensor(controls[i]);
   169         }       
   170 
   171         temperatures = new Sensor[temperatureCount];
   172         for (int i = 0; i < temperatureCount; i++) {
   173           int device = 49 + i;
   174           string name = ReadString(device, 'C');
   175           temperatures[i] =
   176             new Sensor(name, device, SensorType.Temperature, this, settings);
   177           int value = ReadInteger(device, 'T');
   178           temperatures[i].Value = 0.1f * value;
   179           if (value != -32768)
   180             ActivateSensor(temperatures[i]);
   181         }
   182 
   183         flows = new Sensor[flowCount];
   184         for (int i = 0; i < flowCount; i++) {
   185           int device = 65 + i;
   186           string name = ReadString(device, 'C');
   187           flows[i] = new Sensor(name, device, SensorType.Flow, this, settings);
   188           flows[i].Value = 0.1f * ReadInteger(device, 'L');
   189           ActivateSensor(flows[i]);
   190         }
   191 
   192         relays = new Sensor[relayCount];
   193         for (int i = 0; i < relayCount; i++) {
   194           int device = 81 + i;
   195           string name = ReadString(device, 'C');
   196           relays[i] = 
   197             new Sensor(name, device, SensorType.Control, this, settings);
   198           relays[i].Value = 100 * ReadInteger(device, 'S');
   199           ActivateSensor(relays[i]);
   200         }
   201 
   202         // set the update rate to 2 Hz
   203         WriteInteger(0, 'L', 2);
   204         
   205         available = true;
   206 
   207       } catch (IOException) { } catch (TimeoutException) { }      
   208     }
   209 
   210     public override HardwareType HardwareType {
   211       get { return HardwareType.Heatmaster; }
   212     }
   213 
   214     private void ProcessUpdateLine(string line) {
   215       Match match = Regex.Match(line, @">\[0:(\d+)\]([0-9:\|-]+)");
   216       if (match.Success) {
   217         int device;
   218         if (int.TryParse(match.Groups[1].Value, out device)) {
   219           foreach (string s in match.Groups[2].Value.Split('|')) {
   220             string[] strings = s.Split(':');
   221             int[] ints = new int[strings.Length];            
   222             bool valid = true;
   223             for (int i = 0; i < ints.Length; i++)
   224               if (!int.TryParse(strings[i], out ints[i])) {
   225                 valid = false;
   226                 break;
   227               }
   228             if (!valid)
   229               continue; 
   230             switch (device) {
   231               case 32:
   232                 if (ints.Length == 3 && ints[0] <= fans.Length) {
   233                   fans[ints[0] - 1].Value = ints[1];
   234                   controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
   235                 }
   236                 break;
   237               case 48:
   238                 if (ints.Length == 2 && ints[0] <= temperatures.Length)
   239                   temperatures[ints[0] - 1].Value = 0.1f * ints[1];
   240                 break;
   241               case 64:
   242                 if (ints.Length == 3 && ints[0] <= flows.Length)
   243                   flows[ints[0] - 1].Value = 0.1f * ints[1];
   244                 break;
   245               case 80:
   246                 if (ints.Length == 2 && ints[0] <= relays.Length)
   247                   relays[ints[0] - 1].Value = 100 * ints[1];
   248                 break;
   249             }
   250           }
   251         }
   252       }
   253     }
   254 
   255     public override void Update() {
   256       if (!available)
   257         return;
   258 
   259       while (serialPort.IsOpen &&  serialPort.BytesToRead > 0) {
   260         byte b = (byte)serialPort.ReadByte();
   261         if (b == 0x0D) {
   262           ProcessUpdateLine(buffer.ToString());
   263           buffer.Length = 0;
   264         } else {
   265           buffer.Append((char)b);
   266         }
   267       }
   268     }
   269 
   270     public override string GetReport() {
   271       StringBuilder r = new StringBuilder();
   272 
   273       r.AppendLine("Heatmaster");
   274       r.AppendLine();
   275       r.Append("Port: ");
   276       r.AppendLine(portName);
   277       r.Append("Hardware Revision: ");
   278       r.AppendLine(hardwareRevision.ToString(CultureInfo.InvariantCulture));
   279       r.Append("Firmware Revision: ");
   280       r.AppendLine(firmwareRevision.ToString(CultureInfo.InvariantCulture));
   281       r.Append("Firmware CRC: ");
   282       r.AppendLine(firmwareCRC.ToString(CultureInfo.InvariantCulture));
   283       r.AppendLine();
   284 
   285       return r.ToString();
   286     }
   287 
   288     public void Close() {
   289       serialPort.Close();
   290       serialPort.Dispose();
   291       serialPort = null;
   292     }
   293 
   294     public void Dispose() {
   295       if (serialPort != null) {
   296         serialPort.Dispose();
   297         serialPort = null;
   298       }
   299     }
   300   }
   301 }