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