Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Tue, 21 Sep 2010 20:32:36 +0000
changeset 195 0ee888c485d5
parent 182 4801e9eaf979
child 260 5a6ac1d1bc0e
permissions -rw-r--r--
Refactored some of the hardware monitoring code and fixed a few code inspection warnings.
     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.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             for (int i = 0; i < ints.Length; i++)
   232               ints[i] = int.Parse(strings[i], CultureInfo.InvariantCulture);
   233             switch (device) {
   234               case 32:
   235                 if (ints.Length == 3 && ints[0] <= fans.Length) {
   236                   fans[ints[0] - 1].Value = ints[1];
   237                   controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
   238                 }
   239                 break;
   240               case 48:
   241                 if (ints.Length == 2 && ints[0] <= temperatures.Length)
   242                   temperatures[ints[0] - 1].Value = 0.1f * ints[1];
   243                 break;
   244               case 64:
   245                 if (ints.Length == 3 && ints[0] <= flows.Length)
   246                   flows[ints[0] - 1].Value = 0.1f * ints[1];
   247                 break;
   248               case 80:
   249                 if (ints.Length == 2 && ints[0] <= relays.Length)
   250                   relays[ints[0] - 1].Value = 100 * ints[1];
   251                 break;
   252             }
   253           }
   254         }
   255       }
   256     }
   257 
   258     public override void Update() {
   259       if (!available)
   260         return;
   261 
   262       while (serialPort.BytesToRead > 0) {
   263         byte b = (byte)serialPort.ReadByte();
   264         if (b == 0x0D) {
   265           ProcessUpdateLine(buffer.ToString());
   266           buffer.Length = 0;
   267         } else {
   268           buffer.Append((char)b);
   269         }
   270       }
   271     }
   272 
   273     public override string GetReport() {
   274       StringBuilder r = new StringBuilder();
   275 
   276       r.AppendLine("Heatmaster");
   277       r.AppendLine();
   278       r.Append("Port: ");
   279       r.AppendLine(portName);
   280       r.Append("Hardware Revision: ");
   281       r.AppendLine(hardwareRevision.ToString(CultureInfo.InvariantCulture));
   282       r.Append("Firmware Revision: ");
   283       r.AppendLine(firmwareRevision.ToString(CultureInfo.InvariantCulture));
   284       r.Append("Firmware CRC: ");
   285       r.AppendLine(firmwareCRC.ToString(CultureInfo.InvariantCulture));
   286       r.AppendLine();
   287 
   288       return r.ToString();
   289     }
   290 
   291     public void Close() {
   292       serialPort.Close();
   293       serialPort.Dispose();
   294       serialPort = null;
   295     }
   296 
   297     public void Dispose() {
   298       if (serialPort != null) {
   299         serialPort.Dispose();
   300         serialPort = null;
   301       }
   302     }
   303   }
   304 }