Hardware/TBalancer/TBalancer.cs
author moel.mich
Mon, 05 Apr 2010 15:31:19 +0000
changeset 87 ecdc3bcef083
parent 86 b4f0f206173d
child 88 bce8363c119c
permissions -rw-r--r--
Rewritten the T-Balancer code to use the FTDI D2XX drivers directly instead of the System.IO.Ports.SerialPort class. The SerialPort class has some ugly problems like http://connect.microsoft.com/VisualStudio/feedback/details/140018/serialport-crashes-after-disconnect-of-usb-com-port or http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/8a1825d2-c84b-4620-91e7-3934a4d47330 for which no real solution seems to exist. And Microsoft doesn't feel like it needs to be fixed for years now.
     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) 2009-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.Configuration;
    41 using System.Drawing;
    42 using System.Text;
    43 
    44 namespace OpenHardwareMonitor.Hardware.TBalancer {
    45   public class TBalancer : IHardware {
    46 
    47     private int portIndex;
    48     private FT_HANDLE handle;
    49     private Image icon;
    50     private byte protocolVersion;
    51     private Sensor[] digitalTemperatures = new Sensor[8];
    52     private Sensor[] analogTemperatures = new Sensor[4];
    53     private Sensor[] sensorhubTemperatures = new Sensor[6];
    54     private Sensor[] sensorhubFlows = new Sensor[2];
    55     private Sensor[] fans = new Sensor[4];
    56     private Sensor[] miniNGTemperatures = new Sensor[4];
    57     private Sensor[] miniNGFans = new Sensor[4];
    58     private List<ISensor> active = new List<ISensor>();
    59     private List<ISensor> deactivating = new List<ISensor>();
    60     private int[] primaryData = new int[0];
    61     private int[] alternativeData = new int[0];
    62 
    63     public const byte STARTFLAG = 100;
    64     public const byte ENDFLAG = 254;
    65 
    66     private delegate void MethodDelegate();
    67     private MethodDelegate alternativeRequest;    
    68 
    69     public TBalancer(int portIndex, byte protocolVersion) {
    70       this.portIndex = portIndex;
    71       this.icon = Utilities.EmbeddedResources.GetImage("bigng.png");
    72       this.protocolVersion = protocolVersion;
    73 
    74       ParameterDescription[] parameter = new ParameterDescription[] {
    75         new ParameterDescription("Offset", "Temperature offset.", 0)
    76       };
    77       int offset = 0;
    78       for (int i = 0; i < digitalTemperatures.Length; i++)
    79         digitalTemperatures[i] = new Sensor("Digital Sensor #" + (i + 1),
    80           offset + i, null, SensorType.Temperature, this, parameter);
    81       offset += digitalTemperatures.Length;
    82 
    83       for (int i = 0; i < analogTemperatures.Length; i++)
    84         analogTemperatures[i] = new Sensor("Analog Sensor #" + (i + 1),
    85           offset + i, null, SensorType.Temperature, this, parameter);
    86       offset += analogTemperatures.Length;
    87 
    88       for (int i = 0; i < sensorhubTemperatures.Length; i++)
    89         sensorhubTemperatures[i] = new Sensor("Sensorhub Sensor #" + (i + 1),
    90           offset + i, null, SensorType.Temperature, this, parameter);
    91       offset += sensorhubTemperatures.Length;
    92 
    93       for (int i = 0; i < sensorhubFlows.Length; i++)
    94         sensorhubFlows[i] = new Sensor("Flowmeter #" + (i + 1),
    95           offset + i, null, SensorType.Flow, this, new ParameterDescription[] {
    96             new ParameterDescription("Impulse Rate", 
    97               "The impulse rate of the flowmeter in pulses/L", 509)
    98           });
    99       offset += sensorhubFlows.Length;
   100 
   101       for (int i = 0; i < miniNGTemperatures.Length; i++)
   102         miniNGTemperatures[i] = new Sensor("miniNG #" + (i / 2 + 1) +
   103           " Sensor #" + (i % 2 + 1), offset + i, null, SensorType.Temperature, 
   104           this, parameter);
   105       offset += miniNGTemperatures.Length;
   106 
   107       alternativeRequest = new MethodDelegate(DelayedAlternativeRequest);
   108 
   109       Open();
   110       Update(); 
   111     }
   112 
   113     private void ActivateSensor(Sensor sensor) {
   114       deactivating.Remove(sensor);
   115       if (!active.Contains(sensor)) {
   116         active.Add(sensor);
   117         if (SensorAdded != null)
   118           SensorAdded(sensor);
   119       }      
   120     }
   121 
   122     private void DeactivateSensor(Sensor sensor) {
   123       if (deactivating.Contains(sensor)) {
   124         active.Remove(sensor);
   125         deactivating.Remove(sensor);
   126         if (SensorRemoved != null)
   127           SensorRemoved(sensor);
   128       } else if (active.Contains(sensor)) {
   129         deactivating.Add(sensor);
   130       }     
   131     }
   132 
   133     private void ReadminiNG(int[] data, int number) {
   134       int offset = 1 + number * 65;
   135 
   136       if (data[offset + 61] != ENDFLAG)
   137         return;
   138 
   139       for (int i = 0; i < 2; i++) {
   140         Sensor sensor = miniNGTemperatures[number * 2 + i];
   141         if (data[offset + 7 + i] > 0) {
   142           sensor.Value = 0.5f * data[offset + 7 + i] + 
   143             sensor.Parameters[0].Value;
   144           ActivateSensor(sensor);
   145         } else {
   146           DeactivateSensor(sensor);
   147         }
   148       }
   149 
   150       for (int i = 0; i < 2; i++) {
   151         float maxRPM = 20.0f * data[offset + 44 + 2 * i];
   152 
   153         if (miniNGFans[number * 2 + i] == null)
   154           miniNGFans[number * 2 + i] = 
   155             new Sensor("miniNG #" + (number + 1) + " Fan #" + (i + 1),
   156             4 + number * 2 + i, maxRPM, SensorType.Fan, this);
   157         
   158         Sensor sensor = miniNGFans[number * 2 + i];
   159 
   160         sensor.Value = 20.0f * data[offset + 43 + 2 * i];
   161         ActivateSensor(sensor);
   162       }
   163     }
   164 
   165     private void ReadData() {
   166       int[] data = new int[285];
   167       for (int i = 0; i < data.Length; i++)
   168         data[i] = FTD2XX.ReadByte(handle);
   169 
   170       if (data[0] != STARTFLAG) {
   171         FTD2XX.FT_Purge(handle, FT_PURGE.FT_PURGE_RX);   
   172         return;
   173       }
   174 
   175       if (data[1] == 255 || data[1] == 88) { // bigNG
   176 
   177         if (data[274] != protocolVersion) 
   178           return;
   179 
   180         this.primaryData = data;
   181 
   182         for (int i = 0; i < digitalTemperatures.Length; i++)
   183           if (data[238 + i] > 0) {
   184             digitalTemperatures[i].Value = 0.5f * data[238 + i] + 
   185               digitalTemperatures[i].Parameters[0].Value;
   186             ActivateSensor(digitalTemperatures[i]);
   187           } else {
   188             DeactivateSensor(digitalTemperatures[i]);
   189           }
   190 
   191         for (int i = 0; i < analogTemperatures.Length; i++)
   192           if (data[260 + i] > 0) {
   193             analogTemperatures[i].Value = 0.5f * data[260 + i] +
   194               analogTemperatures[i].Parameters[0].Value;
   195             ActivateSensor(analogTemperatures[i]);
   196           } else {
   197             DeactivateSensor(analogTemperatures[i]);
   198           }
   199 
   200         for (int i = 0; i < sensorhubTemperatures.Length; i++)
   201           if (data[246 + i] > 0) {
   202             sensorhubTemperatures[i].Value = 0.5f * data[246 + i] +
   203               sensorhubTemperatures[i].Parameters[0].Value;
   204             ActivateSensor(sensorhubTemperatures[i]);
   205           } else {
   206             DeactivateSensor(sensorhubTemperatures[i]);
   207           }
   208 
   209         for (int i = 0; i < sensorhubFlows.Length; i++)
   210           if (data[231 + i] > 0 && data[234] > 0) {
   211             float pulsesPerSecond = (data[231 + i] * 4.0f) / data[234];
   212             float pulsesPerLiter = sensorhubFlows[i].Parameters[0].Value;
   213             sensorhubFlows[i].Value = pulsesPerSecond * 3600 / pulsesPerLiter;
   214             ActivateSensor(sensorhubFlows[i]);
   215           } else {
   216             DeactivateSensor(sensorhubFlows[i]);
   217           }
   218         
   219         for (int i = 0; i < fans.Length; i++) {
   220           float maxRPM = 11.5f * ((data[149 + 2 * i] << 8) | data[148 + 2 * i]);
   221 
   222           if (fans[i] == null)
   223             fans[i] = new Sensor("Fan #" + (i + 1), i, maxRPM, SensorType.Fan,
   224               this, new ParameterDescription[] {
   225                 new ParameterDescription("MaxRPM", 
   226                   "Maximum revolutions per minute (RPM) of the fan.", maxRPM)
   227               });
   228 
   229           if ((data[136] & (1 << i)) == 0) // pwm mode
   230             fans[i].Value = fans[i].Parameters[0].Value * 0.02f * data[137 + i];
   231           else // analog mode
   232             fans[i].Value = fans[i].Parameters[0].Value * 0.01f * data[141 + i]; 
   233           ActivateSensor(fans[i]);
   234         }
   235 
   236       } else if (data[1] == 253) { // miniNG #1
   237         this.alternativeData = data;
   238 
   239         ReadminiNG(data, 0);        
   240               
   241         if (data[66] == 252)  // miniNG #2
   242           ReadminiNG(data, 1);
   243       } 
   244     }
   245 
   246     public Image Icon {
   247       get { return icon; }
   248     }
   249 
   250     public string Name {
   251       get { return "T-Balancer bigNG"; }
   252     }
   253 
   254     public string Identifier {
   255       get { return "/bigng/" + this.portIndex; }
   256     }
   257 
   258     public IHardware[] SubHardware {
   259       get { return new IHardware[0]; }
   260     }
   261 
   262     public ISensor[] Sensors {
   263       get { return active.ToArray(); }
   264     }
   265 
   266     public string GetReport() {
   267       StringBuilder r = new StringBuilder();
   268 
   269       r.AppendLine("T-Balancer bigNG");
   270       r.AppendLine();
   271       r.Append("Port Index: "); r.AppendLine(portIndex.ToString());
   272       r.AppendLine();
   273 
   274       r.AppendLine("Primary System Information Answer");
   275       r.AppendLine();
   276       r.AppendLine("       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   277       r.AppendLine();
   278       for (int i = 0; i <= 0x11; i++) {
   279         r.Append(" "); r.Append((i << 4).ToString("X3")); r.Append("  ");
   280         for (int j = 0; j <= 0xF; j++) {
   281           int index = ((i << 4) | j);
   282           if (index < primaryData.Length) {
   283             r.Append(" ");
   284             r.Append(primaryData[index].ToString("X2"));
   285           }          
   286         }
   287         r.AppendLine();
   288       }
   289       r.AppendLine();
   290 
   291       if (alternativeData.Length > 0) {
   292         r.AppendLine("Alternative System Information Answer");
   293         r.AppendLine();
   294         r.AppendLine("       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
   295         r.AppendLine();
   296         for (int i = 0; i <= 0x11; i++) {
   297           r.Append(" "); r.Append((i << 4).ToString("X3")); r.Append("  ");
   298           for (int j = 0; j <= 0xF; j++) {
   299             int index = ((i << 4) | j);
   300             if (index < alternativeData.Length) {
   301               r.Append(" ");
   302               r.Append(alternativeData[index].ToString("X2"));
   303             }
   304           }
   305           r.AppendLine();
   306         }
   307         r.AppendLine();
   308       }
   309 
   310       return r.ToString();
   311     }
   312 
   313     private void DelayedAlternativeRequest() {
   314       System.Threading.Thread.Sleep(500);      
   315       FTD2XX.Write(handle, new byte[] { 0x37 });
   316     }
   317 
   318     public void Open() {
   319       FTD2XX.FT_Open(portIndex, out handle); 
   320       FTD2XX.FT_SetBaudRate(handle, 19200);
   321       FTD2XX.FT_SetDataCharacteristics(handle, 8, 1, 0);
   322       FTD2XX.FT_SetFlowControl(handle, FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11,
   323         0x13);
   324       FTD2XX.FT_SetTimeouts(handle, 1000, 1000);
   325       FTD2XX.FT_Purge(handle, FT_PURGE.FT_PURGE_ALL);
   326     }
   327 
   328     public void Update() {
   329       while (FTD2XX.BytesToRead(handle) >= 285)
   330         ReadData();
   331       if (FTD2XX.BytesToRead(handle) == 1)
   332         FTD2XX.ReadByte(handle);
   333 
   334       FTD2XX.Write(handle, new byte[] { 0x38 });
   335       alternativeRequest.BeginInvoke(null, null);
   336     }
   337 
   338     public void Close() {
   339       FTD2XX.FT_Close(handle);
   340     }
   341 
   342     public event SensorEventHandler SensorAdded;
   343     public event SensorEventHandler SensorRemoved;
   344   }
   345 }