Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Sun, 22 Aug 2010 21:53:11 +0000
changeset 171 81ab5e53122e
child 172 c9d8de472546
permissions -rw-r--r--
Added a first implementation for the Heatmaster fan controller.
     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 string ReadLine(int timeout) {
    65       int i = 0;
    66       StringBuilder builder = new StringBuilder();
    67       while (i <= timeout) {
    68         while (serialPort.BytesToRead > 0) {
    69           byte b = (byte)serialPort.ReadByte();
    70           switch (b) {
    71             case 0xAA: return ((char)b).ToString();
    72             case 0x0D: return builder.ToString();
    73             default: builder.Append((char)b); break;
    74           }
    75         }
    76         i++;
    77         Thread.Sleep(1);
    78       }
    79       throw new TimeoutException();
    80     }
    81 
    82     private string ReadField(int device, char field) {
    83       serialPort.WriteLine("[0:" + device + "]R" + field);
    84       for (int i = 0; i < 5; i++) {
    85         string s = ReadLine(100);
    86         Match match = Regex.Match(s, @"-\[0:" + device.ToString() + @"\]R" +
    87           Regex.Escape(field.ToString()) + ":(.*)");
    88         if (match.Success) 
    89           return match.Groups[1].Value;
    90       }
    91       return null;
    92     }
    93 
    94     private string ReadString(int device, char field) {
    95       string s = ReadField(device, field);
    96       if (s != null && s[0] == '"' && s[s.Length - 1] == '"')
    97         return s.Substring(1, s.Length - 2);
    98       else
    99         return null;
   100     }
   101 
   102     private int ReadInteger(int device, char field) {
   103       string s = ReadField(device, field);      
   104       int i;
   105       if (int.TryParse(s, out i))
   106         return i;
   107       else
   108         return 0;
   109     }
   110 
   111     private bool WriteField(int device, char field, string value) {
   112       serialPort.WriteLine("[0:" + device + "]W" + field + ":" + value);
   113       for (int i = 0; i < 5; i++) {
   114         string s = ReadLine(100);
   115         Match match = Regex.Match(s, @"-\[0:" + device.ToString() + @"\]W" + 
   116           Regex.Escape(field.ToString()) + ":" + value);
   117         if (match.Success)
   118           return true;
   119       }
   120       return false;
   121     }
   122 
   123     private bool WriteInteger(int device, char field, int value) {
   124       return WriteField(device, field, value.ToString());
   125     }
   126 
   127     private bool WriteString(int device, char field, string value) {
   128       return WriteField(device, field, '"' + value + '"');
   129     }
   130 
   131     public Heatmaster(string portName, ISettings settings) {
   132 
   133       this.portName = portName;
   134       try {
   135         serialPort = new SerialPort(portName, 38400, Parity.None, 8,
   136           StopBits.One);
   137         serialPort.Open();
   138         serialPort.NewLine = ((char)0x0D).ToString();
   139         
   140         hardwareRevision = ReadInteger(0, 'H');
   141         firmwareRevision = ReadInteger(0, 'V');
   142         firmwareCRC = ReadInteger(0, 'C');
   143 
   144         int fanCount = ReadInteger(32, '?');
   145         int temperatureCount = ReadInteger(48, '?');
   146         int flowCount = ReadInteger(64, '?');
   147         int relayCount =  ReadInteger(80, '?');
   148 
   149         fans = new Sensor[fanCount];
   150         controls = new Sensor[fanCount];
   151         for (int i = 0; i < fanCount; i++) {
   152           int device = 33 + i;
   153           string name = ReadString(device, 'C');
   154           fans[i] = new Sensor(name, device, SensorType.Fan, this, settings);          
   155           fans[i].Value = ReadInteger(device, 'R');
   156           ActivateSensor(fans[i]);
   157           controls[i] =
   158             new Sensor(name, device, SensorType.Control, this, settings);
   159           controls[i].Value = (100 / 255.0f) * ReadInteger(device, 'P');
   160           ActivateSensor(controls[i]);
   161         }
   162         
   163         for (int i = 0; i < fanCount; i++) {
   164           int device = 33 + i;
   165           string name = ReadString(device, 'C');
   166           
   167           fans[i].Value = ReadInteger(device, 'R');
   168           ActivateSensor(fans[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     public override Identifier Identifier {
   215       get {
   216         return new Identifier("heatmaster",
   217           serialPort.PortName.TrimStart(new char[]{'/'}).ToLowerInvariant());
   218       }
   219     }
   220 
   221     public override string Name {
   222       get { return "Heatmaster"; }
   223     }
   224 
   225     public override void Update() {
   226       if (!available)
   227         return;
   228 
   229       while (serialPort.BytesToRead > 0) {
   230         Match match = Regex.Match(ReadLine(0), @">\[0:(\d+)\]([0-9:\|-]+)");
   231         if (match.Success) {
   232           int device;
   233           if (int.TryParse(match.Groups[1].Value, out device)) {
   234             foreach (string s in match.Groups[2].Value.Split('|')) {
   235               string[] strings = s.Split(':');
   236               int[] ints = new int[strings.Length];
   237               for (int i = 0; i < ints.Length; i++)
   238                 ints[i] = int.Parse(strings[i]);
   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 
   265     public override string GetReport() {
   266       StringBuilder r = new StringBuilder();
   267 
   268       r.AppendLine("Heatmaster");
   269       r.AppendLine();
   270       r.Append("Port: ");
   271       r.AppendLine(portName);
   272       r.Append("Hardware Revision: ");
   273       r.AppendLine(hardwareRevision.ToString());
   274       r.Append("Firmware Revision: ");
   275       r.AppendLine(firmwareRevision.ToString());
   276       r.Append("Firmware CRC: ");
   277       r.AppendLine(firmwareCRC.ToString());
   278       r.AppendLine();
   279 
   280       return r.ToString();
   281     }
   282 
   283     public void Close() {
   284       serialPort.Close();
   285     }
   286   }
   287 }