Hardware/Heatmaster/Heatmaster.cs
author moel.mich
Wed, 15 Sep 2010 18:43:15 +0000
changeset 186 010d719f9245
parent 173 fb96c0ca3c2d
child 195 0ee888c485d5
permissions -rw-r--r--
Added a check to verify the FTDI chip ID before opening the T-Balancer 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.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 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:" + 
    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     private 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     private 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     private bool WriteInteger(int device, char field, int value) {
   129       return WriteField(device, field, 
   130         value.ToString(CultureInfo.InvariantCulture));
   131     }
   132 
   133     private 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         for (int i = 0; i < fanCount; i++) {
   170           int device = 33 + i;
   171           string name = ReadString(device, 'C');
   172           
   173           fans[i].Value = ReadInteger(device, 'R');
   174           ActivateSensor(fans[i]);
   175         }
   176 
   177         temperatures = new Sensor[temperatureCount];
   178         for (int i = 0; i < temperatureCount; i++) {
   179           int device = 49 + i;
   180           string name = ReadString(device, 'C');
   181           temperatures[i] =
   182             new Sensor(name, device, SensorType.Temperature, this, settings);
   183           int value = ReadInteger(device, 'T');
   184           temperatures[i].Value = 0.1f * value;
   185           if (value != -32768)
   186             ActivateSensor(temperatures[i]);
   187         }
   188 
   189         flows = new Sensor[flowCount];
   190         for (int i = 0; i < flowCount; i++) {
   191           int device = 65 + i;
   192           string name = ReadString(device, 'C');
   193           flows[i] = new Sensor(name, device, SensorType.Flow, this, settings);
   194           flows[i].Value = 0.1f * ReadInteger(device, 'L');
   195           ActivateSensor(flows[i]);
   196         }
   197 
   198         relays = new Sensor[relayCount];
   199         for (int i = 0; i < relayCount; i++) {
   200           int device = 81 + i;
   201           string name = ReadString(device, 'C');
   202           relays[i] = 
   203             new Sensor(name, device, SensorType.Control, this, settings);
   204           relays[i].Value = 100 * ReadInteger(device, 'S');
   205           ActivateSensor(relays[i]);
   206         }
   207 
   208         // set the update rate to 2 Hz
   209         WriteInteger(0, 'L', 2);
   210         
   211         available = true;
   212 
   213       } catch (IOException) { } catch (TimeoutException) { }      
   214     }
   215 
   216     public override HardwareType HardwareType {
   217       get { return HardwareType.Heatmaster; }
   218     }
   219 
   220     public override Identifier Identifier {
   221       get {
   222         return new Identifier("heatmaster",
   223           serialPort.PortName.TrimStart(new char[]{'/'}).ToLowerInvariant());
   224       }
   225     }
   226 
   227     public override string Name {
   228       get { return "Heatmaster"; }
   229     }
   230 
   231     private void ProcessUpdateLine(string line) {
   232       Match match = Regex.Match(line, @">\[0:(\d+)\]([0-9:\|-]+)");
   233       if (match.Success) {
   234         int device;
   235         if (int.TryParse(match.Groups[1].Value, out device)) {
   236           foreach (string s in match.Groups[2].Value.Split('|')) {
   237             string[] strings = s.Split(':');
   238             int[] ints = new int[strings.Length];
   239             for (int i = 0; i < ints.Length; i++)
   240               ints[i] = int.Parse(strings[i], CultureInfo.InvariantCulture);
   241             switch (device) {
   242               case 32:
   243                 if (ints.Length == 3 && ints[0] <= fans.Length) {
   244                   fans[ints[0] - 1].Value = ints[1];
   245                   controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
   246                 }
   247                 break;
   248               case 48:
   249                 if (ints.Length == 2 && ints[0] <= temperatures.Length)
   250                   temperatures[ints[0] - 1].Value = 0.1f * ints[1];
   251                 break;
   252               case 64:
   253                 if (ints.Length == 3 && ints[0] <= flows.Length)
   254                   flows[ints[0] - 1].Value = 0.1f * ints[1];
   255                 break;
   256               case 80:
   257                 if (ints.Length == 2 && ints[0] <= relays.Length)
   258                   relays[ints[0] - 1].Value = 100 * ints[1];
   259                 break;
   260             }
   261           }
   262         }
   263       }
   264     }
   265 
   266     public override void Update() {
   267       if (!available)
   268         return;
   269 
   270       while (serialPort.BytesToRead > 0) {
   271         byte b = (byte)serialPort.ReadByte();
   272         if (b == 0x0D) {
   273           ProcessUpdateLine(buffer.ToString());
   274           buffer.Length = 0;
   275         } else {
   276           buffer.Append((char)b);
   277         }
   278       }
   279     }
   280 
   281     public override string GetReport() {
   282       StringBuilder r = new StringBuilder();
   283 
   284       r.AppendLine("Heatmaster");
   285       r.AppendLine();
   286       r.Append("Port: ");
   287       r.AppendLine(portName);
   288       r.Append("Hardware Revision: ");
   289       r.AppendLine(hardwareRevision.ToString(CultureInfo.InvariantCulture));
   290       r.Append("Firmware Revision: ");
   291       r.AppendLine(firmwareRevision.ToString(CultureInfo.InvariantCulture));
   292       r.Append("Firmware CRC: ");
   293       r.AppendLine(firmwareCRC.ToString(CultureInfo.InvariantCulture));
   294       r.AppendLine();
   295 
   296       return r.ToString();
   297     }
   298 
   299     public void Close() {
   300       serialPort.Close();
   301       serialPort.Dispose();
   302       serialPort = null;
   303     }
   304 
   305     public void Dispose() {
   306       if (serialPort != null) {
   307         serialPort.Dispose();
   308         serialPort = null;
   309       }
   310     }
   311   }
   312 }