first commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AeroCool;
|
||||
|
||||
public class AeroCoolGroup : IGroup
|
||||
{
|
||||
private readonly List<IHardware> _hardware = new();
|
||||
private readonly StringBuilder _report = new();
|
||||
|
||||
public AeroCoolGroup(ISettings settings)
|
||||
{
|
||||
_report.AppendLine("AeroCool Hardware");
|
||||
_report.AppendLine();
|
||||
|
||||
foreach (HidDevice dev in DeviceList.Local.GetHidDevices(0x2E97))
|
||||
{
|
||||
int hubno = dev.ProductID - 0x1000;
|
||||
if (dev.DevicePath.Contains("mi_02") && hubno is >= 1 and <= 8)
|
||||
{
|
||||
var device = new P7H1(dev, settings);
|
||||
_report.AppendLine($"Device name: {device.Name}");
|
||||
_report.AppendLine($"HUB number: {device.HubNumber}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(device);
|
||||
}
|
||||
}
|
||||
|
||||
if (_hardware.Count == 0)
|
||||
{
|
||||
_report.AppendLine("No AeroCool Hardware found.");
|
||||
_report.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (IHardware iHardware in _hardware)
|
||||
{
|
||||
if (iHardware is Hardware hardware)
|
||||
hardware.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
return _report.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AeroCool;
|
||||
|
||||
internal sealed class P7H1 : Hardware
|
||||
{
|
||||
private const byte REPORT_ID = 0x0;
|
||||
private readonly HidDevice _device;
|
||||
|
||||
private readonly Sensor[] _rpm = new Sensor[5];
|
||||
private readonly float[] _speeds = new float[5];
|
||||
private readonly HidStream _stream;
|
||||
private bool _running;
|
||||
|
||||
public P7H1(HidDevice dev, ISettings settings) : base("AeroCool P7-H1", new Identifier(dev), settings)
|
||||
{
|
||||
_device = dev;
|
||||
HubNumber = _device.ProductID - 0x1000;
|
||||
Name = $"AeroCool P7-H1 #{HubNumber}";
|
||||
|
||||
if (_device.TryOpen(out _stream))
|
||||
{
|
||||
_running = true;
|
||||
|
||||
Task.Run(ReadStream);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_rpm[i] = new Sensor($"Fan #{i + 1}", i, SensorType.Fan, this, settings);
|
||||
ActivateSensor(_rpm[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public int HubNumber { get; }
|
||||
|
||||
private void ReadStream()
|
||||
{
|
||||
byte[] inputReportBuffer = new byte[_device.GetMaxInputReportLength()];
|
||||
|
||||
while (_running)
|
||||
{
|
||||
IAsyncResult ar = null;
|
||||
|
||||
while (_running)
|
||||
{
|
||||
ar ??= _stream.BeginRead(inputReportBuffer, 0, inputReportBuffer.Length, null, null);
|
||||
|
||||
if (ar.IsCompleted)
|
||||
{
|
||||
int byteCount = _stream.EndRead(ar);
|
||||
ar = null;
|
||||
|
||||
if (byteCount == 16 && inputReportBuffer[0] == REPORT_ID)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_speeds[i] = (inputReportBuffer[(i * 3) + 2] * 256) + inputReportBuffer[(i * 3) + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ar.AsyncWaitHandle.WaitOne(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_running = false;
|
||||
_stream.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_rpm[i].Value = _speeds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
public class AquaComputerGroup : IGroup
|
||||
{
|
||||
private readonly List<IHardware> _hardware = new();
|
||||
private readonly StringBuilder _report = new();
|
||||
|
||||
public AquaComputerGroup(ISettings settings)
|
||||
{
|
||||
_report.AppendLine("AquaComputer Hardware");
|
||||
_report.AppendLine();
|
||||
|
||||
foreach (HidDevice dev in DeviceList.Local.GetHidDevices(0x0c70))
|
||||
{
|
||||
string productName = dev.GetProductName();
|
||||
productName = productName.Substring(0, 1).ToUpper() + productName.Substring(1);
|
||||
|
||||
switch (dev.ProductID)
|
||||
{
|
||||
case 0xF00E:
|
||||
var d5Next = new D5Next(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {d5Next.FirmwareVersion}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(d5Next);
|
||||
break;
|
||||
|
||||
case 0xf0b6:
|
||||
var aquastreamXt = new AquastreamXT(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Device variant: {aquastreamXt.Variant}");
|
||||
_report.AppendLine($"Firmware version: {aquastreamXt.FirmwareVersion}");
|
||||
_report.AppendLine($"{aquastreamXt.Status}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(aquastreamXt);
|
||||
break;
|
||||
|
||||
case 0xf003:
|
||||
var mps = new MPS(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {mps.FirmwareVersion}");
|
||||
_report.AppendLine($"{mps.Status}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(mps);
|
||||
break;
|
||||
|
||||
case 0xF00D:
|
||||
var quadro = new Quadro(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {quadro.FirmwareVersion}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(quadro);
|
||||
break;
|
||||
|
||||
case 0xF00B:
|
||||
var aquastreamUltimate = new AquastreamUltimate(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {aquastreamUltimate.FirmwareVersion}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(aquastreamUltimate);
|
||||
break;
|
||||
|
||||
case 0xF011:
|
||||
var octo = new Octo(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {octo.FirmwareVersion}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(octo);
|
||||
break;
|
||||
|
||||
case 0xF00A:
|
||||
var farbwerk = new Farbwerk(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {farbwerk.FirmwareVersion}");
|
||||
_report.AppendLine($"{farbwerk.Status}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(farbwerk);
|
||||
break;
|
||||
|
||||
case 0xF012:
|
||||
var highflownext = new HighFlowNext(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {highflownext.FirmwareVersion}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(highflownext);
|
||||
break;
|
||||
|
||||
default:
|
||||
_report.AppendLine($"Unknown Hardware PID: {dev.ProductID} Name: {productName}");
|
||||
_report.AppendLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hardware.Count == 0)
|
||||
{
|
||||
_report.AppendLine("No AquaComputer Hardware found.");
|
||||
_report.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (IHardware iHardware in _hardware)
|
||||
{
|
||||
if (iHardware is Hardware hardware)
|
||||
hardware.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
return _report.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
internal sealed class AquastreamUltimate : Hardware
|
||||
{
|
||||
private readonly byte[] _rawData = new byte[104];
|
||||
private readonly HidStream _stream;
|
||||
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[2];
|
||||
private readonly Sensor[] _temperatures = new Sensor[2];
|
||||
private readonly Sensor[] _voltages = new Sensor[2];
|
||||
private readonly Sensor[] _currents = new Sensor[2];
|
||||
private readonly Sensor[] _powers = new Sensor[2];
|
||||
private readonly Sensor[] _flows = new Sensor[2];
|
||||
|
||||
public AquastreamUltimate(HidDevice dev, ISettings settings) : base("AquastreamUltimate", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
// Reading output report instead of feature report, as the measurements are in the output report.
|
||||
_stream.Read(_rawData);
|
||||
|
||||
FirmwareVersion = GetConvertedValue(0xD).GetValueOrDefault(0);
|
||||
|
||||
Name = "Aquastream ULTIMATE";
|
||||
|
||||
_temperatures[0] = new Sensor("Coolant", 0, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[0]);
|
||||
|
||||
_temperatures[1] = new Sensor("External Sensor", 1, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[1]);
|
||||
|
||||
_rpmSensors[0] = new Sensor("Pump", 0, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[0]);
|
||||
|
||||
_voltages[0] = new Sensor("Pump", 0, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[0]);
|
||||
|
||||
_currents[0] = new Sensor("Pump", 0, SensorType.Current, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_currents[0]);
|
||||
|
||||
_powers[0] = new Sensor("Pump", 0, SensorType.Power, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_powers[0]);
|
||||
|
||||
// Initialize the flow sensor
|
||||
_flows[0] = new Sensor("Pump", 0, SensorType.Flow, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_flows[0]);
|
||||
|
||||
_flows[1] = new Sensor("Pressure (mBar)", 1, SensorType.Factor, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_flows[1]);
|
||||
|
||||
_rpmSensors[1] = new Sensor("Fan", 1, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[1]);
|
||||
|
||||
_voltages[1] = new Sensor("Fan", 1, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[1]);
|
||||
|
||||
_currents[1] = new Sensor("Fan", 1, SensorType.Current, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_currents[1]);
|
||||
|
||||
_powers[1] = new Sensor("Fan", 1, SensorType.Power, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_powers[1]);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
// Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
|
||||
_rpmSensors[0].Value = GetConvertedValue(0x51); // Pump speed.
|
||||
_rpmSensors[1].Value = GetConvertedValue(0x41 + 0x06); // Fan speed.
|
||||
|
||||
_temperatures[0].Value = GetConvertedValue(0x2D) / 100f; // Water temp.
|
||||
_temperatures[1].Value = GetConvertedValue(0x2F) / 100f; // Ext sensor temp.
|
||||
|
||||
_voltages[0].Value = GetConvertedValue(0x3D) / 100f; // Pump input voltage.
|
||||
_voltages[1].Value = GetConvertedValue(0x41 + 0x02) / 100f; // Fan output voltage.
|
||||
|
||||
_currents[0].Value = GetConvertedValue(0x53) / 1000f; // Pump current.
|
||||
_currents[1].Value = GetConvertedValue(0x41 + 0x00) / 1000f; // Fan current.
|
||||
|
||||
_powers[0].Value = GetConvertedValue(0x55) / 100f; // Pump power.
|
||||
_powers[1].Value = GetConvertedValue(0x41 + 0x04) / 100f; // Fan power.
|
||||
|
||||
_flows[0].Value = GetConvertedValue(0x37); // Flow.
|
||||
_flows[1].Value = GetConvertedValue(0x57) / 1000f; // Pressure.
|
||||
}
|
||||
|
||||
private ushort? GetConvertedValue(int index)
|
||||
{
|
||||
if (_rawData[index] == sbyte.MaxValue)
|
||||
return null;
|
||||
|
||||
return Convert.ToUInt16(_rawData[index + 1] | (_rawData[index] << 8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
//TODO:
|
||||
//Check tested and fix unknown variables in Update()
|
||||
//Check if property "Variant" is valid interpreted
|
||||
//Implement Fan Control in SetControl()
|
||||
|
||||
internal sealed class AquastreamXT : Hardware
|
||||
{
|
||||
private readonly Sensor _fanControl;
|
||||
private readonly Sensor[] _frequencies = new Sensor[2];
|
||||
private readonly Sensor _pumpFlow;
|
||||
private readonly Sensor _pumpPower;
|
||||
private readonly byte[] _rawData = new byte[64];
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[2];
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _temperatures = new Sensor[3];
|
||||
private readonly Sensor[] _voltages = new Sensor[2];
|
||||
|
||||
public AquastreamXT(HidDevice dev, ISettings settings) : base("Aquastream XT", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
do
|
||||
{
|
||||
_rawData[0] = 0x4;
|
||||
_stream.GetFeature(_rawData);
|
||||
}
|
||||
while (_rawData[0] != 0x4);
|
||||
|
||||
Name = $"Aquastream XT {Variant}";
|
||||
FirmwareVersion = BitConverter.ToUInt16(_rawData, 50);
|
||||
|
||||
_temperatures[0] = new Sensor("External Fan VRM", 0, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[0]);
|
||||
_temperatures[1] = new Sensor("External", 1, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[1]);
|
||||
_temperatures[2] = new Sensor("Internal Water", 2, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[2]);
|
||||
|
||||
_voltages[0] = new Sensor("External Fan", 1, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[0]);
|
||||
_voltages[1] = new Sensor("Pump", 2, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[1]);
|
||||
|
||||
_pumpPower = new Sensor("Pump", 0, SensorType.Power, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_pumpPower);
|
||||
|
||||
_pumpFlow = new Sensor("Pump", 0, SensorType.Flow, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_pumpFlow);
|
||||
|
||||
_rpmSensors[0] = new Sensor("External Fan", 0, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[0]);
|
||||
_rpmSensors[1] = new Sensor("Pump", 1, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[1]);
|
||||
|
||||
_fanControl = new Sensor("External Fan", 0, SensorType.Control, this, Array.Empty<ParameterDescription>(), settings);
|
||||
_fanControl.Control = new Control(_fanControl, settings, 0, 100);
|
||||
|
||||
ActivateSensor(_fanControl);
|
||||
_frequencies[0] = new Sensor("Pump Frequency", 0, SensorType.Frequency, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_frequencies[0]);
|
||||
_frequencies[1] = new Sensor("Pump MaxFrequency", 1, SensorType.Frequency, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_frequencies[1]);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; private set; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get
|
||||
{
|
||||
FirmwareVersion = BitConverter.ToUInt16(_rawData, 50);
|
||||
return FirmwareVersion < 1008 ? $"Status: Untested Firmware Version {FirmwareVersion}! Please consider Updating to Version 1018" : "Status: OK";
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Check if valid
|
||||
public string Variant
|
||||
{
|
||||
get
|
||||
{
|
||||
MODE mode = (MODE)_rawData[33];
|
||||
|
||||
if (mode.HasFlag(MODE.MODE_PUMP_ADV))
|
||||
return "Ultra + Internal Flow Sensor";
|
||||
|
||||
if (mode.HasFlag(MODE.MODE_FAN_CONTROLLER))
|
||||
return "Ultra";
|
||||
|
||||
if (mode.HasFlag(MODE.MODE_FAN_AMP))
|
||||
return "Advanced";
|
||||
|
||||
return "Standard";
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
|
||||
base.Close();
|
||||
}
|
||||
|
||||
//TODO: Check tested and fix unknown variables
|
||||
public override void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
_rawData[0] = 0x4;
|
||||
_stream.GetFeature(_rawData);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_rawData[0] != 0x4)
|
||||
return;
|
||||
|
||||
//var rawSensorsFan = BitConverter.ToUInt16(rawData, 1); //unknown - redundant?
|
||||
//var rawSensorsExt = BitConverter.ToUInt16(rawData, 3); //unknown - redundant?
|
||||
//var rawSensorsWater = BitConverter.ToUInt16(rawData, 5); //unknown - redundant?
|
||||
|
||||
_voltages[0].Value = BitConverter.ToUInt16(_rawData, 7) / 61f; //External Fan Voltage: tested - OK
|
||||
_voltages[1].Value = BitConverter.ToUInt16(_rawData, 9) / 61f; //Pump Voltage: tested - OK
|
||||
_pumpPower.Value = _voltages[1].Value * BitConverter.ToInt16(_rawData, 11) / 625f; //Pump Voltage * Pump Current: tested - OK
|
||||
|
||||
_temperatures[0].Value = BitConverter.ToUInt16(_rawData, 13) / 100f; //External Fan VRM Temperature: untested
|
||||
_temperatures[1].Value = BitConverter.ToUInt16(_rawData, 15) / 100f; //External Temperature Sensor: untested
|
||||
_temperatures[2].Value = BitConverter.ToUInt16(_rawData, 17) / 100f; //Internal Water Temperature Sensor: tested - OK
|
||||
|
||||
_frequencies[0].Value = (1f / BitConverter.ToInt16(_rawData, 19)) * 750000; //Pump Frequency: tested - OK
|
||||
_rpmSensors[1].Value = _frequencies[0].Value * 60f; //Pump RPM: tested - OK
|
||||
_frequencies[1].Value = (1f / BitConverter.ToUInt16(_rawData, 21)) * 750000; //Pump Max Frequency: tested - OK
|
||||
|
||||
_pumpFlow.Value = BitConverter.ToUInt32(_rawData, 23); //Internal Pump Flow Sensor: unknown
|
||||
|
||||
_rpmSensors[0].Value = BitConverter.ToUInt32(_rawData, 27); //External Fan RPM: untested
|
||||
|
||||
_fanControl.Value = 100f / byte.MaxValue * _rawData[31]; //External Fan Control: tested, External Fan Voltage scales by this value - OK
|
||||
}
|
||||
|
||||
[Flags]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
private enum MODE : byte
|
||||
{
|
||||
MODE_PUMP_ADV = 1,
|
||||
MODE_FAN_AMP = 2,
|
||||
MODE_FAN_CONTROLLER = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
internal sealed class D5Next : Hardware
|
||||
{
|
||||
//Available Reports, found them by looking at the below methods
|
||||
//var test = dev.GetRawReportDescriptor();
|
||||
//var test2 = dev.GetReportDescriptor();
|
||||
|
||||
// ID 1; Length 158; INPUT
|
||||
// ID 2; Length 11; OUTPUT
|
||||
// ID 3; Length 1025; <-- works FEATURE
|
||||
// ID 8; Length 1025; <-- works FEATURE
|
||||
// ID 12; Length 1025; <-- 0xC FEATURE
|
||||
|
||||
private readonly byte[] _rawData = new byte[1025];
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[1];
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _temperatures = new Sensor[1];
|
||||
|
||||
public D5Next(HidDevice dev, ISettings settings) : base("D5Next", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
//Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
|
||||
Name = "D5Next";
|
||||
FirmwareVersion = Convert.ToUInt16(_rawData[14] | (_rawData[13] << 8));
|
||||
_temperatures[0] = new Sensor("Water Temperature", 0, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[0]);
|
||||
|
||||
_rpmSensors[0] = new Sensor("Pump", 0, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
//Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
_temperatures[0].Value = (_rawData[88] | (_rawData[87] << 8)) / 100f; //Water Temp
|
||||
_rpmSensors[0].Value = _rawData[117] | (_rawData[116] << 8); //Pump RPM
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
//TODO:
|
||||
//Implement set RGB Controls
|
||||
|
||||
internal sealed class Farbwerk : Hardware
|
||||
{
|
||||
private const int FEATURE_ID = 3;
|
||||
|
||||
private const int HEADER_SIZE = 27;
|
||||
private const int SENSOR_OFFSET = 20;
|
||||
private const int COLORS_OFFSET = 40;
|
||||
|
||||
private const int TEMPERATURE_COUNT = 4;
|
||||
private const int COLOR_COUNT = 4;
|
||||
private const int COLOR_VALUE_COUNT = COLOR_COUNT * 3;
|
||||
|
||||
private readonly byte[] _rawData = new byte[140];
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _temperatures = new Sensor[TEMPERATURE_COUNT];
|
||||
private readonly Sensor[] _colors = new Sensor[COLOR_VALUE_COUNT];
|
||||
|
||||
public Farbwerk(HidDevice dev, ISettings settings) : base("Farbwerk", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
for (int i = 0; i < _temperatures.Length; i++)
|
||||
{
|
||||
_temperatures[i] = new Sensor($"Sensor {i + 1}", i, SensorType.Temperature, this, settings);
|
||||
ActivateSensor(_temperatures[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _colors.Length; i++)
|
||||
{
|
||||
int control = (i / 3) + 1;
|
||||
string color = (i % 3) switch
|
||||
{
|
||||
0 => "Red",
|
||||
1 => "Green",
|
||||
2 => "Blue",
|
||||
_ => "Invalid"
|
||||
};
|
||||
_colors[i] = new Sensor($"Controller {control} {color}", COLOR_COUNT + i, SensorType.Level, this, settings);
|
||||
ActivateSensor(_colors[i]);
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; private set; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.EmbeddedController; }
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rawData[0] != 0x1)
|
||||
{
|
||||
return $"Status: Invalid header {_rawData[0]}";
|
||||
}
|
||||
|
||||
if (FirmwareVersion < 1009)
|
||||
{
|
||||
return $"Status: Untested Firmware Version {FirmwareVersion}! Please consider Updating to Version 1009";
|
||||
}
|
||||
|
||||
return "Status: OK";
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
int length = _stream.Read(_rawData);
|
||||
|
||||
if (length != _rawData.Length || _rawData[0] != 0x1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FirmwareVersion = Convert.ToUInt16(_rawData[21] << 8 | _rawData[22]);
|
||||
|
||||
int offset = HEADER_SIZE + SENSOR_OFFSET;
|
||||
for (int i = 0; i < _temperatures.Length; i++)
|
||||
{
|
||||
_temperatures[i].Value = (_rawData[offset] << 8 | _rawData[offset + 1]) / 100.0f;
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
offset = HEADER_SIZE + COLORS_OFFSET;
|
||||
for (int i = 0; i < _colors.Length; i++)
|
||||
{
|
||||
_colors[i].Value = (_rawData[offset] << 8 | _rawData[offset + 1]) / 81.90f;
|
||||
offset += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
internal sealed class HighFlowNext : Hardware
|
||||
{
|
||||
private readonly byte[] _rawData = new byte[1025];
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _temperatures = new Sensor[2];
|
||||
private readonly Sensor[] _flows = new Sensor[1];
|
||||
private readonly Sensor[] _levels = new Sensor[1];
|
||||
private readonly Sensor[] _powers = new Sensor[1];
|
||||
private readonly Sensor[] _conductivities = new Sensor[1];
|
||||
private readonly Sensor[] _voltages = new Sensor[2];
|
||||
private readonly Sensor[] _alarms = new Sensor[4];
|
||||
|
||||
public HighFlowNext(HidDevice dev, ISettings settings) : base("high flow NEXT", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
// Reading output report instead of feature report, as the measurements are in the output report.
|
||||
_stream.Read(_rawData);
|
||||
|
||||
FirmwareVersion = ReadUInt16BE(_rawData, 13);
|
||||
|
||||
_temperatures[0] = new Sensor("Water Temperature", 0, SensorType.Temperature, this, settings);
|
||||
ActivateSensor(_temperatures[0]);
|
||||
|
||||
_temperatures[1] = new Sensor("External Temperature", 1, SensorType.Temperature, this, settings);
|
||||
ActivateSensor(_temperatures[1]);
|
||||
|
||||
_flows[0] = new Sensor("Flow", 0, SensorType.Flow, this, settings);
|
||||
ActivateSensor(_flows[0]);
|
||||
|
||||
_levels[0] = new Sensor("Water Quality", 0, SensorType.Level, this, settings);
|
||||
ActivateSensor(_levels[0]);
|
||||
|
||||
_powers[0] = new Sensor("Dissipated Power", 0, SensorType.Power, this, settings);
|
||||
ActivateSensor(_powers[0]);
|
||||
|
||||
_conductivities[0] = new Sensor("Conductivity", 0, SensorType.Conductivity, this, settings);
|
||||
ActivateSensor(_conductivities[0]);
|
||||
|
||||
_voltages[0] = new Sensor("VCC", 0, SensorType.Voltage, this, settings);
|
||||
ActivateSensor(_voltages[0]);
|
||||
|
||||
_voltages[1] = new Sensor("VCC USB", 1, SensorType.Voltage, this, settings);
|
||||
ActivateSensor(_voltages[1]);
|
||||
|
||||
_alarms[0] = new Sensor("Flow Alarm", 0, true, SensorType.Factor, this, null, settings);
|
||||
ActivateSensor(_alarms[0]);
|
||||
|
||||
_alarms[1] = new Sensor("Water Temperature Alarm", 1, true, SensorType.Factor, this, null, settings);
|
||||
ActivateSensor(_alarms[0]);
|
||||
|
||||
_alarms[2] = new Sensor("External Temperature Alarm", 2, true, SensorType.Factor, this, null, settings);
|
||||
ActivateSensor(_alarms[0]);
|
||||
|
||||
_alarms[3] = new Sensor("Water Quality Alarm", 3, true, SensorType.Factor, this, null, settings);
|
||||
ActivateSensor(_alarms[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
// Reading output report instead of feature report, as the measurements are in the output report.
|
||||
_stream.Read(_rawData);
|
||||
|
||||
_temperatures[0].Value = ReadUInt16BE(_rawData, 85) / 100f; // Water Temperature
|
||||
|
||||
// External Temperature.
|
||||
ushort rawExtTempValue = ReadUInt16BE(_rawData, 87);
|
||||
bool externalTempSensorConnected = rawExtTempValue != short.MaxValue;
|
||||
|
||||
if (externalTempSensorConnected)
|
||||
{
|
||||
_temperatures[1].Value = rawExtTempValue / 100f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No external temp sensor connected.
|
||||
_temperatures[1].Value = null;
|
||||
}
|
||||
|
||||
_flows[0].Value = ReadUInt16BE(_rawData, 81) / 10f; // Flow
|
||||
|
||||
|
||||
_levels[0].Value = ReadUInt16BE(_rawData, 89) / 100f; // Water Quality
|
||||
|
||||
// Dissipated Power.
|
||||
if (externalTempSensorConnected)
|
||||
{
|
||||
_powers[0].Value = ReadUInt16BE(_rawData, 91);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Power calculation requires the external temp sensor to be connected.
|
||||
_powers[0].Value = null;
|
||||
}
|
||||
|
||||
_conductivities[0].Value = ReadUInt16BE(_rawData, 95) / 10f; // Conductivity
|
||||
|
||||
_voltages[0].Value = ReadUInt16BE(_rawData, 97) / 100f; // VCC
|
||||
_voltages[1].Value = ReadUInt16BE(_rawData, 99) / 100f; // VCC USB
|
||||
|
||||
_alarms[0].Value = (_rawData[116] & 0x02) >> 1; // Flow alarm
|
||||
_alarms[1].Value = (_rawData[116] & 0x04) >> 2; // Water temperature alarm
|
||||
_alarms[2].Value = (_rawData[116] & 0x08) >> 3; // External temperature alarm
|
||||
_alarms[3].Value = (_rawData[116] & 0x10) >> 4; // Water quality alarm
|
||||
|
||||
// Unused:
|
||||
// _rawData[101..104] -> Total pumped volume liters
|
||||
// _rawData[105..109] -> Internal impulse counter from flow meter
|
||||
}
|
||||
|
||||
private ushort ReadUInt16BE(byte[] value, int startIndex)
|
||||
{
|
||||
return (ushort)(value[startIndex + 1] | (value[startIndex] << 8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
internal sealed class MPS : Hardware
|
||||
{
|
||||
public const int ExternalTemperature = 43;
|
||||
public const int InternalWaterTemperature = 45;
|
||||
public const int PumpFlow = 35;
|
||||
private const byte MPS_REPORT_ID = 0x2;
|
||||
|
||||
private readonly Sensor _pumpFlow;
|
||||
private readonly byte[] _rawData = new byte[64];
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _temperatures = new Sensor[2];
|
||||
|
||||
private ushort _externalTemperature;
|
||||
|
||||
public MPS(HidDevice dev, ISettings settings) : base("MPS", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
do
|
||||
{
|
||||
_rawData[0] = MPS_REPORT_ID;
|
||||
_stream.GetFeature(_rawData);
|
||||
}
|
||||
while (_rawData[0] != MPS_REPORT_ID);
|
||||
|
||||
Name = "MPS";
|
||||
FirmwareVersion = ExtractFirmwareVersion();
|
||||
|
||||
_temperatures[0] = new Sensor("External", 0, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[0]);
|
||||
_temperatures[1] = new Sensor("Internal Water", 1, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[1]);
|
||||
|
||||
_pumpFlow = new Sensor("Pump", 0, SensorType.Flow, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_pumpFlow);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; private set; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get
|
||||
{
|
||||
FirmwareVersion = ExtractFirmwareVersion();
|
||||
if (FirmwareVersion < 1012)
|
||||
{
|
||||
return $"Status: Untested Firmware Version {FirmwareVersion}! Please consider Updating to Version 1012";
|
||||
}
|
||||
|
||||
return "Status: OK";
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
_rawData[0] = MPS_REPORT_ID;
|
||||
_stream.GetFeature(_rawData);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_rawData[0] != MPS_REPORT_ID)
|
||||
return;
|
||||
|
||||
_pumpFlow.Value = BitConverter.ToUInt16(_rawData, PumpFlow) / 10f;
|
||||
|
||||
_externalTemperature = BitConverter.ToUInt16(_rawData, ExternalTemperature);
|
||||
//sensor reading returns Int16.MaxValue (32767), when not connected
|
||||
if (_externalTemperature != short.MaxValue)
|
||||
{
|
||||
_temperatures[0].Value = _externalTemperature / 100f;
|
||||
}
|
||||
else
|
||||
{
|
||||
_temperatures[0].Value = null;
|
||||
}
|
||||
|
||||
_temperatures[1].Value = BitConverter.ToUInt16(_rawData, InternalWaterTemperature) / 100f;
|
||||
|
||||
}
|
||||
|
||||
private ushort ExtractFirmwareVersion()
|
||||
{
|
||||
return BitConverter.ToUInt16(_rawData, 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
internal sealed class Octo : Hardware
|
||||
{
|
||||
private readonly byte[] _rawData = new byte[1025];
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[8];
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _temperatures = new Sensor[4];
|
||||
private readonly Sensor[] _voltages = new Sensor[9];
|
||||
private readonly Sensor[] _currents = new Sensor[8];
|
||||
private readonly Sensor[] _powers = new Sensor[8];
|
||||
|
||||
public Octo(HidDevice dev, ISettings settings) : base("Octo", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
//Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
|
||||
Name = "OCTO";
|
||||
FirmwareVersion = GetConvertedValue(OctoDataIndexes.FIRMWARE_VERSION).GetValueOrDefault(0);
|
||||
|
||||
// Initialize the 4 temperature sensors
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_temperatures[i] = new Sensor($"Temperature {i + 1}", i, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[i]);
|
||||
}
|
||||
|
||||
// Initialize the 8 fan speed sensors
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
_rpmSensors[i] = new Sensor($"Fan {i + 1}", i, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[i]);
|
||||
}
|
||||
|
||||
// Initialize the input voltage sensor
|
||||
_voltages[0] = new Sensor("Input", 0, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[0]);
|
||||
|
||||
// Initialize the 8 fan voltage sensors
|
||||
for (int i = 1; i < 9; i++)
|
||||
{
|
||||
_voltages[i] = new Sensor($"Fan {i}", i, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[i]);
|
||||
}
|
||||
|
||||
// Initialize the 8 fan current sensors
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
_currents[i] = new Sensor($"Fan {i + 1}", i, SensorType.Current, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_currents[i]);
|
||||
}
|
||||
|
||||
// Initialize the 8 fan power sensors
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
_powers[i] = new Sensor($"Fan {i + 1}", i, SensorType.Power, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_powers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
//Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
|
||||
_temperatures[0].Value = GetConvertedValue(OctoDataIndexes.TEMP_1) / 100f; // Temp 1
|
||||
_temperatures[1].Value = GetConvertedValue(OctoDataIndexes.TEMP_2) / 100f; // Temp 2
|
||||
_temperatures[2].Value = GetConvertedValue(OctoDataIndexes.TEMP_3) / 100f; // Temp 3
|
||||
_temperatures[3].Value = GetConvertedValue(OctoDataIndexes.TEMP_4) / 100f; // Temp 4
|
||||
|
||||
_rpmSensors[0].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_1); // Fan 1 speed
|
||||
_rpmSensors[1].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_2); // Fan 2 speed
|
||||
_rpmSensors[2].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_3); // Fan 3 speed
|
||||
_rpmSensors[3].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_4); // Fan 4 speed
|
||||
_rpmSensors[4].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_5); // Fan 5 speed
|
||||
_rpmSensors[5].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_6); // Fan 6 speed
|
||||
_rpmSensors[6].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_7); // Fan 7 speed
|
||||
_rpmSensors[7].Value = GetConvertedValue(OctoDataIndexes.FAN_SPEED_8); // Fan 8 speed
|
||||
|
||||
_voltages[0].Value = GetConvertedValue(OctoDataIndexes.VOLTAGE) / 100f; // Input voltage
|
||||
_voltages[1].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_1) / 100f; // Fan 1 voltage
|
||||
_voltages[2].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_2) / 100f; // Fan 2 voltage
|
||||
_voltages[3].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_3) / 100f; // Fan 3 voltage
|
||||
_voltages[4].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_4) / 100f; // Fan 4 voltage
|
||||
_voltages[5].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_5) / 100f; // Fan 5 voltage
|
||||
_voltages[6].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_6) / 100f; // Fan 6 voltage
|
||||
_voltages[7].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_7) / 100f; // Fan 7 voltage
|
||||
_voltages[8].Value = GetConvertedValue(OctoDataIndexes.FAN_VOLTAGE_8) / 100f; // Fan 8 voltage
|
||||
|
||||
_currents[0].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_1) / 1000f; // Fan 1 current
|
||||
_currents[1].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_2) / 1000f; // Fan 2 current
|
||||
_currents[2].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_3) / 1000f; // Fan 3 current
|
||||
_currents[3].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_4) / 1000f; // Fan 4 current
|
||||
_currents[4].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_5) / 1000f; // Fan 5 current
|
||||
_currents[5].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_6) / 1000f; // Fan 6 current
|
||||
_currents[6].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_7) / 1000f; // Fan 7 current
|
||||
_currents[7].Value = GetConvertedValue(OctoDataIndexes.FAN_CURRENT_8) / 1000f; // Fan 8 current
|
||||
|
||||
_powers[0].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_1) / 100f; // Fan 1 power
|
||||
_powers[1].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_2) / 100f; // Fan 2 power
|
||||
_powers[2].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_3) / 100f; // Fan 3 power
|
||||
_powers[3].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_4) / 100f; // Fan 4 power
|
||||
_powers[4].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_5) / 100f; // Fan 5 power
|
||||
_powers[5].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_6) / 100f; // Fan 6 power
|
||||
_powers[6].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_7) / 100f; // Fan 7 power
|
||||
_powers[7].Value = GetConvertedValue(OctoDataIndexes.FAN_POWER_8) / 100f; // Fan 8 power
|
||||
}
|
||||
|
||||
private sealed class OctoDataIndexes
|
||||
{
|
||||
public const int FIRMWARE_VERSION = 13;
|
||||
|
||||
public const int TEMP_1 = 61;
|
||||
public const int TEMP_2 = 63;
|
||||
public const int TEMP_3 = 65;
|
||||
public const int TEMP_4 = 67;
|
||||
|
||||
public const int FAN_SPEED_1 = 133;
|
||||
public const int FAN_SPEED_2 = 146;
|
||||
public const int FAN_SPEED_3 = 159;
|
||||
public const int FAN_SPEED_4 = 172;
|
||||
public const int FAN_SPEED_5 = 185;
|
||||
public const int FAN_SPEED_6 = 198;
|
||||
public const int FAN_SPEED_7 = 211;
|
||||
public const int FAN_SPEED_8 = 224;
|
||||
|
||||
public const int FAN_POWER_1 = 131;
|
||||
public const int FAN_POWER_2 = 144;
|
||||
public const int FAN_POWER_3 = 157;
|
||||
public const int FAN_POWER_4 = 170;
|
||||
public const int FAN_POWER_5 = 183;
|
||||
public const int FAN_POWER_6 = 196;
|
||||
public const int FAN_POWER_7 = 209;
|
||||
public const int FAN_POWER_8 = 222;
|
||||
|
||||
public const int VOLTAGE = 117;
|
||||
public const int FAN_VOLTAGE_1 = 127;
|
||||
public const int FAN_VOLTAGE_2 = 140;
|
||||
public const int FAN_VOLTAGE_3 = 153;
|
||||
public const int FAN_VOLTAGE_4 = 166;
|
||||
public const int FAN_VOLTAGE_5 = 179;
|
||||
public const int FAN_VOLTAGE_6 = 192;
|
||||
public const int FAN_VOLTAGE_7 = 205;
|
||||
public const int FAN_VOLTAGE_8 = 218;
|
||||
|
||||
public const int FAN_CURRENT_1 = 129;
|
||||
public const int FAN_CURRENT_2 = 142;
|
||||
public const int FAN_CURRENT_3 = 155;
|
||||
public const int FAN_CURRENT_4 = 168;
|
||||
public const int FAN_CURRENT_5 = 181;
|
||||
public const int FAN_CURRENT_6 = 194;
|
||||
public const int FAN_CURRENT_7 = 207;
|
||||
public const int FAN_CURRENT_8 = 220;
|
||||
}
|
||||
|
||||
private ushort? GetConvertedValue(int index)
|
||||
{
|
||||
if (_rawData[index] == sbyte.MaxValue)
|
||||
return null;
|
||||
|
||||
return Convert.ToUInt16(_rawData[index + 1] | (_rawData[index] << 8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.AquaComputer;
|
||||
|
||||
internal sealed class Quadro : Hardware
|
||||
{
|
||||
private readonly byte[] _rawData = new byte[210];
|
||||
private readonly HidStream _stream;
|
||||
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[4];
|
||||
private readonly Sensor[] _temperatures = new Sensor[4];
|
||||
private readonly Sensor[] _voltages = new Sensor[5];
|
||||
private readonly Sensor[] _currents = new Sensor[4];
|
||||
private readonly Sensor[] _powers = new Sensor[4];
|
||||
private readonly Sensor[] _flows = new Sensor[1];
|
||||
|
||||
public Quadro(HidDevice dev, ISettings settings) : base("Quadro", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
//Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
|
||||
Name = "QUADRO";
|
||||
FirmwareVersion = GetConvertedValue(QuadroDataIndexes.FIRMWARE_VERSION).GetValueOrDefault(0);
|
||||
|
||||
// Initialize the 4 temperature sensors
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_temperatures[i] = new Sensor($"Temperature {i + 1}", i, SensorType.Temperature, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_temperatures[i]);
|
||||
}
|
||||
|
||||
// Initialize the input voltage sensor
|
||||
_voltages[0] = new Sensor("Input", 0, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[0]);
|
||||
|
||||
// Initialize the flow sensor
|
||||
_flows[0] = new Sensor("Flow", 0, SensorType.Flow, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_flows[0]);
|
||||
|
||||
// Initialize the 4 fan voltage sensors
|
||||
for (int i = 1; i < 5; i++)
|
||||
{
|
||||
_voltages[i] = new Sensor($"Fan {i}", i, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_voltages[i]);
|
||||
}
|
||||
|
||||
// Initialize the 4 fan current sensors
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_currents[i] = new Sensor($"Fan {i + 1}", i, SensorType.Current, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_currents[i]);
|
||||
}
|
||||
|
||||
// Initialize the 4 fan power sensors
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_powers[i] = new Sensor($"Fan {i + 1}", i, SensorType.Power, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_powers[i]);
|
||||
}
|
||||
|
||||
// Initialize the 4 fan speed sensors
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_rpmSensors[i] = new Sensor($"Fan {i + 1}", i, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
//Reading output report instead of feature report, as the measurements are in the output report
|
||||
_stream.Read(_rawData);
|
||||
|
||||
_temperatures[0].Value = GetConvertedValue(QuadroDataIndexes.TEMP_1) / 100f; // Temp 1
|
||||
_temperatures[1].Value = GetConvertedValue(QuadroDataIndexes.TEMP_2) / 100f; // Temp 2
|
||||
_temperatures[2].Value = GetConvertedValue(QuadroDataIndexes.TEMP_3) / 100f; // Temp 3
|
||||
_temperatures[3].Value = GetConvertedValue(QuadroDataIndexes.TEMP_4) / 100f; // Temp 4
|
||||
|
||||
_voltages[0].Value = GetConvertedValue(QuadroDataIndexes.VOLTAGE) / 100f; // Input voltage
|
||||
|
||||
_flows[0].Value = GetConvertedValue(QuadroDataIndexes.FLOW) / 10f; // Flow
|
||||
|
||||
_voltages[1].Value = GetConvertedValue(QuadroDataIndexes.FAN_VOLTAGE_1) / 100f; // Fan 1 voltage
|
||||
_voltages[2].Value = GetConvertedValue(QuadroDataIndexes.FAN_VOLTAGE_2) / 100f; // Fan 2 voltage
|
||||
_voltages[3].Value = GetConvertedValue(QuadroDataIndexes.FAN_VOLTAGE_3) / 100f; // Fan 3 voltage
|
||||
_voltages[4].Value = GetConvertedValue(QuadroDataIndexes.FAN_VOLTAGE_4) / 100f; // Fan 4 voltage
|
||||
|
||||
_currents[0].Value = GetConvertedValue(QuadroDataIndexes.FAN_CURRENT_1) / 1000f; // Fan 1 current
|
||||
_currents[1].Value = GetConvertedValue(QuadroDataIndexes.FAN_CURRENT_2) / 1000f; // Fan 2 current
|
||||
_currents[2].Value = GetConvertedValue(QuadroDataIndexes.FAN_CURRENT_3) / 1000f; // Fan 3 current
|
||||
_currents[3].Value = GetConvertedValue(QuadroDataIndexes.FAN_CURRENT_4) / 1000f; // Fan 4 current
|
||||
|
||||
_powers[0].Value = GetConvertedValue(QuadroDataIndexes.FAN_POWER_1) / 100f; // Fan 1 power
|
||||
_powers[1].Value = GetConvertedValue(QuadroDataIndexes.FAN_POWER_2) / 100f; // Fan 2 power
|
||||
_powers[2].Value = GetConvertedValue(QuadroDataIndexes.FAN_POWER_3) / 100f; // Fan 3 power
|
||||
_powers[3].Value = GetConvertedValue(QuadroDataIndexes.FAN_POWER_4) / 100f; // Fan 4 power
|
||||
|
||||
_rpmSensors[0].Value = GetConvertedValue(QuadroDataIndexes.FAN_SPEED_1); // Fan 1 speed
|
||||
_rpmSensors[1].Value = GetConvertedValue(QuadroDataIndexes.FAN_SPEED_2); // Fan 2 speed
|
||||
_rpmSensors[2].Value = GetConvertedValue(QuadroDataIndexes.FAN_SPEED_3); // Fan 3 speed
|
||||
_rpmSensors[3].Value = GetConvertedValue(QuadroDataIndexes.FAN_SPEED_4); // Fan 4 speed
|
||||
|
||||
}
|
||||
|
||||
private sealed class QuadroDataIndexes
|
||||
{
|
||||
public const int FIRMWARE_VERSION = 13;
|
||||
|
||||
public const int TEMP_1 = 52;
|
||||
public const int TEMP_2 = 54;
|
||||
public const int TEMP_3 = 56;
|
||||
public const int TEMP_4 = 58;
|
||||
|
||||
public const int VOLTAGE = 108;
|
||||
|
||||
public const int FLOW = 110;
|
||||
|
||||
public const int FAN_VOLTAGE_1 = 114;
|
||||
public const int FAN_VOLTAGE_2 = 127;
|
||||
public const int FAN_VOLTAGE_3 = 140;
|
||||
public const int FAN_VOLTAGE_4 = 153;
|
||||
|
||||
public const int FAN_CURRENT_1 = 116;
|
||||
public const int FAN_CURRENT_2 = 129;
|
||||
public const int FAN_CURRENT_3 = 142;
|
||||
public const int FAN_CURRENT_4 = 155;
|
||||
|
||||
public const int FAN_POWER_1 = 118;
|
||||
public const int FAN_POWER_2 = 131;
|
||||
public const int FAN_POWER_3 = 144;
|
||||
public const int FAN_POWER_4 = 157;
|
||||
|
||||
public const int FAN_SPEED_1 = 120;
|
||||
public const int FAN_SPEED_2 = 133;
|
||||
public const int FAN_SPEED_3 = 146;
|
||||
public const int FAN_SPEED_4 = 159;
|
||||
|
||||
}
|
||||
|
||||
private ushort? GetConvertedValue(int index)
|
||||
{
|
||||
if (_rawData[index] == sbyte.MaxValue)
|
||||
return null;
|
||||
|
||||
return Convert.ToUInt16(_rawData[index + 1] | (_rawData[index] << 8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Heatmaster;
|
||||
|
||||
internal sealed class Heatmaster : Hardware, IDisposable
|
||||
{
|
||||
private readonly bool _available;
|
||||
private readonly StringBuilder _buffer = new();
|
||||
private readonly Sensor[] _controls;
|
||||
private readonly Sensor[] _fans;
|
||||
private readonly int _firmwareCrc;
|
||||
private readonly int _firmwareRevision;
|
||||
private readonly Sensor[] _flows;
|
||||
private readonly int _hardwareRevision;
|
||||
private readonly string _portName;
|
||||
private readonly Sensor[] _relays;
|
||||
private readonly Sensor[] _temperatures;
|
||||
private SerialPort _serialPort;
|
||||
|
||||
public Heatmaster(string portName, ISettings settings) : base("Heatmaster", new Identifier("heatmaster", portName.TrimStart('/').ToLowerInvariant()), settings)
|
||||
{
|
||||
_portName = portName;
|
||||
try
|
||||
{
|
||||
_serialPort = new SerialPort(portName, 38400, Parity.None, 8, StopBits.One);
|
||||
_serialPort.Open();
|
||||
_serialPort.NewLine = ((char)0x0D).ToString();
|
||||
|
||||
_hardwareRevision = ReadInteger(0, 'H');
|
||||
_firmwareRevision = ReadInteger(0, 'V');
|
||||
_firmwareCrc = ReadInteger(0, 'C');
|
||||
|
||||
int fanCount = Math.Min(ReadInteger(32, '?'), 4);
|
||||
int temperatureCount = Math.Min(ReadInteger(48, '?'), 6);
|
||||
int flowCount = Math.Min(ReadInteger(64, '?'), 1);
|
||||
int relayCount = Math.Min(ReadInteger(80, '?'), 1);
|
||||
|
||||
_fans = new Sensor[fanCount];
|
||||
_controls = new Sensor[fanCount];
|
||||
for (int i = 0; i < fanCount; i++)
|
||||
{
|
||||
int device = 33 + i;
|
||||
string name = ReadString(device, 'C');
|
||||
_fans[i] = new Sensor(name, device, SensorType.Fan, this, settings) { Value = ReadInteger(device, 'R') };
|
||||
ActivateSensor(_fans[i]);
|
||||
_controls[i] = new Sensor(name, device, SensorType.Control, this, settings) { Value = (100 / 255.0f) * ReadInteger(device, 'P') };
|
||||
ActivateSensor(_controls[i]);
|
||||
}
|
||||
|
||||
_temperatures = new Sensor[temperatureCount];
|
||||
for (int i = 0; i < temperatureCount; i++)
|
||||
{
|
||||
int device = 49 + i;
|
||||
string name = ReadString(device, 'C');
|
||||
_temperatures[i] = new Sensor(name, device, SensorType.Temperature, this, settings);
|
||||
int value = ReadInteger(device, 'T');
|
||||
_temperatures[i].Value = 0.1f * value;
|
||||
if (value != -32768)
|
||||
ActivateSensor(_temperatures[i]);
|
||||
}
|
||||
|
||||
_flows = new Sensor[flowCount];
|
||||
for (int i = 0; i < flowCount; i++)
|
||||
{
|
||||
int device = 65 + i;
|
||||
string name = ReadString(device, 'C');
|
||||
_flows[i] = new Sensor(name, device, SensorType.Flow, this, settings) { Value = 0.1f * ReadInteger(device, 'L') };
|
||||
ActivateSensor(_flows[i]);
|
||||
}
|
||||
|
||||
_relays = new Sensor[relayCount];
|
||||
for (int i = 0; i < relayCount; i++)
|
||||
{
|
||||
int device = 81 + i;
|
||||
string name = ReadString(device, 'C');
|
||||
_relays[i] = new Sensor(name, device, SensorType.Control, this, settings)
|
||||
{
|
||||
Value = 100 * ReadInteger(device, 'S')
|
||||
};
|
||||
ActivateSensor(_relays[i]);
|
||||
}
|
||||
|
||||
// set the update rate to 2 Hz
|
||||
WriteInteger(0, 'L', 2);
|
||||
_available = true;
|
||||
}
|
||||
catch (IOException)
|
||||
{ }
|
||||
catch (TimeoutException)
|
||||
{ }
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_serialPort != null)
|
||||
{
|
||||
_serialPort.Dispose();
|
||||
_serialPort = null;
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadLine(int timeout)
|
||||
{
|
||||
int i = 0;
|
||||
StringBuilder builder = new();
|
||||
while (i <= timeout)
|
||||
{
|
||||
while (_serialPort.BytesToRead > 0)
|
||||
{
|
||||
byte b = (byte)_serialPort.ReadByte();
|
||||
switch (b)
|
||||
{
|
||||
case 0xAA: return ((char)b).ToString();
|
||||
case 0x0D: return builder.ToString();
|
||||
default:
|
||||
builder.Append((char)b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
private string ReadField(int device, char field)
|
||||
{
|
||||
_serialPort.WriteLine("[0:" + device + "]R" + field);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string s = ReadLine(200);
|
||||
Match match = Regex.Match(s, @"-\[0:" + device.ToString(CultureInfo.InvariantCulture) + @"\]R" + Regex.Escape(field.ToString(CultureInfo.InvariantCulture)) + ":(.*)");
|
||||
if (match.Success)
|
||||
return match.Groups[1].Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string ReadString(int device, char field)
|
||||
{
|
||||
string s = ReadField(device, field);
|
||||
if (s?[0] == '"' && s[s.Length - 1] == '"')
|
||||
return s.Substring(1, s.Length - 2);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int ReadInteger(int device, char field)
|
||||
{
|
||||
string s = ReadField(device, field);
|
||||
if (int.TryParse(s, out int i))
|
||||
return i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void WriteField(int device, char field, string value)
|
||||
{
|
||||
_serialPort.WriteLine("[0:" + device + "]W" + field + ":" + value);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string s = ReadLine(200);
|
||||
Match match = Regex.Match(s, @"-\[0:" + device.ToString(CultureInfo.InvariantCulture) + @"\]W" + Regex.Escape(field.ToString(CultureInfo.InvariantCulture)) + ":" + value);
|
||||
if (match.Success)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteInteger(int device, char field, int value)
|
||||
{
|
||||
WriteField(device, field, value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private void ProcessUpdateLine(string line)
|
||||
{
|
||||
Match match = Regex.Match(line, @">\[0:(\d+)\]([0-9:\|-]+)");
|
||||
if (match.Success && int.TryParse(match.Groups[1].Value, out int device))
|
||||
{
|
||||
foreach (string s in match.Groups[2].Value.Split('|'))
|
||||
{
|
||||
string[] strings = s.Split(':');
|
||||
int[] ints = new int[strings.Length];
|
||||
bool valid = true;
|
||||
for (int i = 0; i < ints.Length; i++)
|
||||
{
|
||||
if (!int.TryParse(strings[i], out ints[i]))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
switch (device)
|
||||
{
|
||||
case 32:
|
||||
if (ints.Length == 3 && ints[0] <= _fans.Length)
|
||||
{
|
||||
_fans[ints[0] - 1].Value = ints[1];
|
||||
_controls[ints[0] - 1].Value = (100 / 255.0f) * ints[2];
|
||||
}
|
||||
|
||||
break;
|
||||
case 48:
|
||||
if (ints.Length == 2 && ints[0] <= _temperatures.Length)
|
||||
_temperatures[ints[0] - 1].Value = 0.1f * ints[1];
|
||||
|
||||
break;
|
||||
case 64:
|
||||
if (ints.Length == 3 && ints[0] <= _flows.Length)
|
||||
_flows[ints[0] - 1].Value = 0.1f * ints[1];
|
||||
|
||||
break;
|
||||
case 80:
|
||||
if (ints.Length == 2 && ints[0] <= _relays.Length)
|
||||
_relays[ints[0] - 1].Value = 100 * ints[1];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!_available)
|
||||
return;
|
||||
|
||||
while (_serialPort.IsOpen && _serialPort.BytesToRead > 0)
|
||||
{
|
||||
byte b = (byte)_serialPort.ReadByte();
|
||||
if (b == 0x0D)
|
||||
{
|
||||
ProcessUpdateLine(_buffer.ToString());
|
||||
_buffer.Length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_buffer.Append((char)b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
r.AppendLine("Heatmaster");
|
||||
r.AppendLine();
|
||||
r.Append("Port: ");
|
||||
r.AppendLine(_portName);
|
||||
r.Append("Hardware Revision: ");
|
||||
r.AppendLine(_hardwareRevision.ToString(CultureInfo.InvariantCulture));
|
||||
r.Append("Firmware Revision: ");
|
||||
r.AppendLine(_firmwareRevision.ToString(CultureInfo.InvariantCulture));
|
||||
r.Append("Firmware CRC: ");
|
||||
r.AppendLine(_firmwareCrc.ToString(CultureInfo.InvariantCulture));
|
||||
r.AppendLine();
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_serialPort.Close();
|
||||
_serialPort.Dispose();
|
||||
_serialPort = null;
|
||||
base.Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO.Ports;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Heatmaster;
|
||||
|
||||
internal class HeatmasterGroup : IGroup
|
||||
{
|
||||
private readonly List<Heatmaster> _hardware = new();
|
||||
private readonly StringBuilder _report = new();
|
||||
|
||||
public HeatmasterGroup(ISettings settings)
|
||||
{
|
||||
// No implementation for Heatmaster on Unix systems
|
||||
if (Software.OperatingSystem.IsUnix)
|
||||
return;
|
||||
|
||||
string[] portNames = GetRegistryPortNames();
|
||||
for (int i = 0; i < portNames.Length; i++)
|
||||
{
|
||||
bool isValid = false;
|
||||
try
|
||||
{
|
||||
using SerialPort serialPort = new(portNames[i], 38400, Parity.None, 8, StopBits.One);
|
||||
serialPort.NewLine = ((char)0x0D).ToString();
|
||||
_report.Append("Port Name: ");
|
||||
_report.AppendLine(portNames[i]);
|
||||
try
|
||||
{
|
||||
serialPort.Open();
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
_report.AppendLine("Exception: Access Denied");
|
||||
}
|
||||
|
||||
if (serialPort.IsOpen)
|
||||
{
|
||||
serialPort.DiscardInBuffer();
|
||||
serialPort.DiscardOutBuffer();
|
||||
serialPort.Write(new byte[] { 0xAA }, 0, 1);
|
||||
|
||||
int j = 0;
|
||||
while (serialPort.BytesToRead == 0 && j < 10)
|
||||
{
|
||||
Thread.Sleep(20);
|
||||
j++;
|
||||
}
|
||||
|
||||
if (serialPort.BytesToRead > 0)
|
||||
{
|
||||
bool flag = false;
|
||||
while (serialPort.BytesToRead > 0 && !flag)
|
||||
{
|
||||
flag |= serialPort.ReadByte() == 0xAA;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
serialPort.WriteLine("[0:0]RH");
|
||||
try
|
||||
{
|
||||
int k = 0;
|
||||
int revision = 0;
|
||||
while (k < 5)
|
||||
{
|
||||
string line = ReadLine(serialPort, 100);
|
||||
if (line.StartsWith("-[0:0]RH:", StringComparison.Ordinal))
|
||||
{
|
||||
revision = int.Parse(line.Substring(9), CultureInfo.InvariantCulture);
|
||||
break;
|
||||
}
|
||||
|
||||
k++;
|
||||
}
|
||||
|
||||
isValid = revision == 770;
|
||||
if (!isValid)
|
||||
{
|
||||
_report.Append("Status: Wrong Hardware Revision " + revision.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
_report.AppendLine("Status: Timeout Reading Revision");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_report.AppendLine("Status: Wrong Startflag");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_report.AppendLine("Status: No Response");
|
||||
}
|
||||
|
||||
serialPort.DiscardInBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
_report.AppendLine("Status: Port not Open");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_report.AppendLine(e.ToString());
|
||||
}
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
_report.AppendLine("Status: OK");
|
||||
_hardware.Add(new Heatmaster(portNames[i], settings));
|
||||
}
|
||||
|
||||
_report.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
if (_report.Length > 0)
|
||||
{
|
||||
StringBuilder r = new();
|
||||
r.AppendLine("Serial Port Heatmaster");
|
||||
r.AppendLine();
|
||||
r.Append(_report);
|
||||
r.AppendLine();
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (Heatmaster heatmaster in _hardware)
|
||||
heatmaster.Close();
|
||||
}
|
||||
|
||||
private static string ReadLine(SerialPort port, int timeout)
|
||||
{
|
||||
int i = 0;
|
||||
StringBuilder builder = new();
|
||||
while (i < timeout)
|
||||
{
|
||||
while (port.BytesToRead > 0)
|
||||
{
|
||||
byte b = (byte)port.ReadByte();
|
||||
switch (b)
|
||||
{
|
||||
case 0xAA: return ((char)b).ToString();
|
||||
case 0x0D: return builder.ToString();
|
||||
default:
|
||||
builder.Append((char)b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
private static string[] GetRegistryPortNames()
|
||||
{
|
||||
List<string> result = new();
|
||||
string[] paths = { string.Empty, "&MI_00" };
|
||||
try
|
||||
{
|
||||
foreach (string path in paths)
|
||||
{
|
||||
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\USB\VID_10C4&PID_EA60" + path);
|
||||
if (key != null)
|
||||
{
|
||||
foreach (string subKeyName in key.GetSubKeyNames())
|
||||
{
|
||||
RegistryKey subKey = key.OpenSubKey(subKeyName + "\\" + "Device Parameters");
|
||||
if (subKey?.GetValue("PortName") is string name && !result.Contains(name))
|
||||
result.Add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SecurityException)
|
||||
{ }
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Nzxt;
|
||||
|
||||
/// <summary>
|
||||
/// Support for the NZXT GRID+ V3 devices.
|
||||
/// </summary>
|
||||
internal sealed class GridV3 : Hardware
|
||||
{
|
||||
private const int FANS_COUNT = 6;
|
||||
|
||||
// Some initialization messages to send to the controller. No visible effects but NZXT CAM send them.
|
||||
private static readonly byte[] _initialize1 = { 0x01, 0x5c };
|
||||
private static readonly byte[] _initialize2 = { 0x01, 0x5d };
|
||||
private static readonly byte[] _initialize3 = { 0x01, 0x59 };
|
||||
|
||||
private readonly Sensor[] _currents = new Sensor[FANS_COUNT];
|
||||
private readonly Control[] _fanControls = new Control[FANS_COUNT];
|
||||
private readonly Sensor _noise;
|
||||
private readonly Sensor[] _powers = new Sensor[FANS_COUNT];
|
||||
private readonly Sensor[] _pwmControls = new Sensor[FANS_COUNT];
|
||||
private readonly Dictionary<int, byte[]> _rawData = new();
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[FANS_COUNT];
|
||||
private readonly byte[] _setFanSpeedMsg;
|
||||
private readonly HidStream _stream;
|
||||
private readonly Sensor[] _voltages = new Sensor[FANS_COUNT];
|
||||
|
||||
public GridV3(HidDevice dev, ISettings settings) : base("NZXT GRID+ V3", new Identifier(dev), settings)
|
||||
{
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
for (int fanId = 0; fanId < FANS_COUNT; fanId++)
|
||||
_rawData[fanId] = new byte[21];
|
||||
|
||||
_setFanSpeedMsg = new byte[65];
|
||||
_setFanSpeedMsg[0] = 0x02;
|
||||
_setFanSpeedMsg[1] = 0x4d;
|
||||
_setFanSpeedMsg[3] = 0x00;
|
||||
|
||||
_stream.Write(_initialize1);
|
||||
_stream.Write(_initialize2);
|
||||
_stream.Write(_initialize3);
|
||||
|
||||
do
|
||||
{
|
||||
_stream.Read(_rawData[0]);
|
||||
if (_rawData[0][0] == 0x04)
|
||||
{
|
||||
FirmwareVersion = $"{_rawData[0][11]}.{_rawData[0][14]}";
|
||||
}
|
||||
}
|
||||
while (FirmwareVersion == null);
|
||||
|
||||
Name = "NZXT GRID+ V3";
|
||||
|
||||
// Initialize all sensors and controls for all fans
|
||||
for (int i = 0; i < FANS_COUNT; i++)
|
||||
{
|
||||
_rpmSensors[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
_voltages[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Voltage, this, Array.Empty<ParameterDescription>(), settings);
|
||||
_currents[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Current, this, Array.Empty<ParameterDescription>(), settings);
|
||||
_powers[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Power, this, Array.Empty<ParameterDescription>(), settings);
|
||||
_pwmControls[i] = new Sensor($"GRID Fan #{i + 1}", i, SensorType.Control, this, Array.Empty<ParameterDescription>(), settings);
|
||||
|
||||
_fanControls[i] = new Control(_pwmControls[i], settings, 0, 100);
|
||||
|
||||
_pwmControls[i].Control = _fanControls[i];
|
||||
_fanControls[i].ControlModeChanged += SoftwareControlValueChanged;
|
||||
_fanControls[i].SoftwareControlValueChanged += SoftwareControlValueChanged;
|
||||
SoftwareControlValueChanged(_fanControls[i]);
|
||||
|
||||
ActivateSensor(_rpmSensors[i]);
|
||||
ActivateSensor(_voltages[i]);
|
||||
ActivateSensor(_currents[i]);
|
||||
ActivateSensor(_powers[i]);
|
||||
ActivateSensor(_pwmControls[i]);
|
||||
|
||||
// NZXT GRID does not report current PWM value. So we need to initialize it with some value to keep GUI and device values in sync.
|
||||
_fanControls[i].SetDefault();
|
||||
}
|
||||
|
||||
_noise = new Sensor("GRID Noise", 0, SensorType.Noise, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_noise);
|
||||
|
||||
Thread readGridReports = new(ContinuousRead) { IsBackground = true };
|
||||
readGridReports.Start(_rawData);
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Cooler;
|
||||
|
||||
public bool IsValid { get; }
|
||||
|
||||
private void SoftwareControlValueChanged(Control control)
|
||||
{
|
||||
switch (control.ControlMode)
|
||||
{
|
||||
case ControlMode.Software:
|
||||
float value = control.SoftwareValue;
|
||||
byte fanSpeed = (byte)(value > 100 ? 100 : value < 0 ? 0 : value); // Clamp the value, anything out of range will fail
|
||||
|
||||
//_controlling = true;
|
||||
_setFanSpeedMsg[2] = (byte)control.Sensor.Index;
|
||||
_setFanSpeedMsg[4] = fanSpeed;
|
||||
|
||||
_stream.Write(_setFanSpeedMsg);
|
||||
|
||||
_pwmControls[control.Sensor.Index].Value = value;
|
||||
break;
|
||||
case ControlMode.Default:
|
||||
// There isn't a "default" mode, but let's say a safe setting is 40%
|
||||
_setFanSpeedMsg[2] = (byte)control.Sensor.Index;
|
||||
_setFanSpeedMsg[4] = 40;
|
||||
|
||||
_stream.Write(_setFanSpeedMsg);
|
||||
|
||||
_pwmControls[control.Sensor.Index].Value = 40;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
_stream?.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
private void ContinuousRead(object state)
|
||||
{
|
||||
byte[] buffer = new byte[_rawData[0].Length];
|
||||
while (_stream.CanRead)
|
||||
{
|
||||
try
|
||||
{
|
||||
_stream.Read(buffer); // This is a blocking call, will wait for bytes to become available
|
||||
if (buffer[0] == 0x04)
|
||||
{
|
||||
lock (_rawData)
|
||||
{
|
||||
int fanId = (buffer[15] >> 4) & 0x0f;
|
||||
Array.Copy(buffer, _rawData[fanId], buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
// Don't care, just make sure the stream is still open
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Could be unplugged, or the app is stopping...
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
// The NZXT GRID+ V3 series sends updates periodically. We have to read it in a separate thread, this call just reads that data.
|
||||
lock (_rawData)
|
||||
{
|
||||
for (int fanId = 0; fanId < FANS_COUNT; fanId++)
|
||||
{
|
||||
_rpmSensors[fanId].Value = (_rawData[fanId][3] << 8) | _rawData[fanId][4];
|
||||
_voltages[fanId].Value = _rawData[fanId][7] + _rawData[fanId][8] / 100.0f;
|
||||
_currents[fanId].Value = _rawData[fanId][9] + _rawData[fanId][10] / 100.0f;
|
||||
_powers[fanId].Value = _currents[fanId].Value * _voltages[fanId].Value;
|
||||
}
|
||||
|
||||
_noise.Value = _rawData[2][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Nzxt;
|
||||
|
||||
/// <summary>
|
||||
/// Support for the Kraken X (X42, X52, X62 or X72) devices.
|
||||
/// </summary>
|
||||
internal sealed class KrakenV2 : Hardware
|
||||
{
|
||||
private static readonly byte[] _getFirmwareInfo = [0x10, 0x01];
|
||||
|
||||
private readonly HidDevice _device;
|
||||
private readonly Sensor _fan;
|
||||
private readonly byte _fanChannel;
|
||||
private readonly bool _fanControl;
|
||||
private readonly Sensor _fanRpm;
|
||||
private readonly TimeSpan _interval = TimeSpan.FromMilliseconds(5000);
|
||||
private readonly Sensor _liquidTemperature;
|
||||
private readonly Sensor _pump;
|
||||
private readonly byte _pumpChannel;
|
||||
private readonly Sensor _pumpRpm;
|
||||
private readonly byte[] _rawData = new byte[64];
|
||||
private readonly string _supportedFirmware;
|
||||
|
||||
private DateTime _lastUpdate = DateTime.MinValue;
|
||||
|
||||
public KrakenV2(HidDevice dev, ISettings settings) : base("Nzxt Kraken X", new Identifier(dev), settings)
|
||||
{
|
||||
_device = dev;
|
||||
|
||||
switch (dev.ProductID)
|
||||
{
|
||||
case 0x170e:
|
||||
default:
|
||||
Name = "NZXT Kraken X";
|
||||
|
||||
_fanControl = true;
|
||||
|
||||
_fanChannel = 0x00;
|
||||
_pumpChannel = 0x40;
|
||||
|
||||
_supportedFirmware = "6.2.0";
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using HidStream stream = dev.Open();
|
||||
|
||||
stream.Write(_getFirmwareInfo);
|
||||
|
||||
int tries = 0;
|
||||
|
||||
while (FirmwareVersion == null && tries++ < 10)
|
||||
{
|
||||
stream.Read(_rawData);
|
||||
|
||||
if (_rawData[0] == 0x11 && _rawData[1] == 0x01)
|
||||
FirmwareVersion = $"{_rawData[0x11]}.{_rawData[0x12]}.{_rawData[0x13]}";
|
||||
}
|
||||
|
||||
if (FirmwareVersion == null)
|
||||
return;
|
||||
|
||||
// Liquid temperature
|
||||
_liquidTemperature = new Sensor("Liquid", 0, SensorType.Temperature, this, [], settings);
|
||||
ActivateSensor(_liquidTemperature);
|
||||
|
||||
// Pump Control
|
||||
_pump = new Sensor("Pump Control", 0, SensorType.Control, this, [], settings);
|
||||
Control pumpControl = new(_pump, settings, 60, 100);
|
||||
_pump.Control = pumpControl;
|
||||
pumpControl.ControlModeChanged += c => ControlValueChanged(_pump, c);
|
||||
pumpControl.SoftwareControlValueChanged += c => ControlValueChanged(_pump, c);
|
||||
ControlValueChanged(_pump, pumpControl);
|
||||
ActivateSensor(_pump);
|
||||
|
||||
// Pump RPM
|
||||
_pumpRpm = new Sensor("Pump", 0, SensorType.Fan, this, [], settings);
|
||||
ActivateSensor(_pumpRpm);
|
||||
|
||||
if (_fanControl)
|
||||
{
|
||||
// Fan Control
|
||||
_fan = new Sensor("Fans Control", 1, SensorType.Control, this, [], settings);
|
||||
Control fanControl = new(_fan, settings, 0, 100);
|
||||
_fan.Control = fanControl;
|
||||
fanControl.ControlModeChanged += c => ControlValueChanged(_fan, c);
|
||||
fanControl.SoftwareControlValueChanged += c => ControlValueChanged(_fan, c);
|
||||
ControlValueChanged(_fan, fanControl);
|
||||
ActivateSensor(_fan);
|
||||
|
||||
// Fan RPM
|
||||
_fanRpm = new Sensor("Fans", 1, SensorType.Fan, this, [], settings);
|
||||
ActivateSensor(_fanRpm);
|
||||
}
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
|
||||
public string FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Cooler;
|
||||
|
||||
public bool IsValid { get; }
|
||||
|
||||
public string Status => FirmwareVersion != _supportedFirmware ? $"Status: Untested firmware version {FirmwareVersion}! Please consider updating to version {_supportedFirmware}" : "Status: OK";
|
||||
|
||||
private void ControlValueChanged(Sensor sensor, IControl control)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (control.ControlMode == ControlMode.Software)
|
||||
{
|
||||
//value will be updated at next Update()
|
||||
sensor.Value = control.SoftwareValue;
|
||||
_lastUpdate = DateTime.MinValue;
|
||||
Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
//will let the device handle the value
|
||||
sensor.Value = null;
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Could be unplugged, or the app is stopping...
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
using HidStream stream = _device.Open();
|
||||
|
||||
stream.Read(_rawData);
|
||||
|
||||
// if not 0x04, it is not temperature data
|
||||
if (_rawData[0] != 0x04)
|
||||
return;
|
||||
|
||||
// some packet may have 0 as temperature, don't know why just ignore it
|
||||
if (_rawData[1] == 0x00)
|
||||
return;
|
||||
|
||||
_liquidTemperature.Value = _rawData[1] + (_rawData[2] / 10.0f);
|
||||
_fanRpm.Value = (_rawData[3] << 8) | _rawData[4];
|
||||
_pumpRpm.Value = (_rawData[5] << 8) | _rawData[6];
|
||||
|
||||
// if we don't have control over the fan or pump, we don't need to update
|
||||
if (!_pump.Value.HasValue && (!_fanControl || !_fan.Value.HasValue))
|
||||
return;
|
||||
|
||||
//control value need to be updated every 5 seconds or it falls back to default
|
||||
if (DateTime.Now - _lastUpdate < _interval)
|
||||
return;
|
||||
|
||||
if (_fanControl && _fan.Value.HasValue)
|
||||
SetDuty(stream, _fanChannel, (byte)_fan.Value);
|
||||
|
||||
if (_fanControl && _pump.Value.HasValue)
|
||||
SetDuty(stream, _pumpChannel, (byte)_pump.Value);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Could be unplugged, or the app is stopping...
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDuty(HidStream stream, byte channel, byte duty)
|
||||
{
|
||||
SetDuty(stream, channel, 0, duty);
|
||||
}
|
||||
|
||||
private void SetDuty(HidStream stream, byte channel, byte temperature, byte duty)
|
||||
{
|
||||
stream.Write([0x2, 0x4d, channel, temperature, duty]);
|
||||
_lastUpdate = DateTime.Now;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Nzxt;
|
||||
|
||||
/// <summary>
|
||||
/// Support for the KrakenZ devices.
|
||||
/// </summary>
|
||||
internal sealed class KrakenV3 : Hardware
|
||||
{
|
||||
private static readonly byte[] _getFirmwareInfo = [0x10, 0x01];
|
||||
private static readonly byte[] _setFanTarget = new byte[64];
|
||||
private static readonly byte[] _setPumpTarget = new byte[64];
|
||||
private static readonly byte[] _statusRequest = [0x74, 0x01];
|
||||
|
||||
private readonly Sensor _fan;
|
||||
private readonly bool _fanControl;
|
||||
private readonly Sensor _fanRpm;
|
||||
private readonly Sensor _pump;
|
||||
private readonly Sensor _pumpRpm;
|
||||
private readonly byte[] _rawData = new byte[64];
|
||||
private readonly HidStream _stream;
|
||||
private readonly string _supportedFirmware;
|
||||
private readonly Sensor _temperature;
|
||||
private volatile bool _controllingFans;
|
||||
private volatile bool _controllingPump;
|
||||
|
||||
public KrakenV3(HidDevice dev, ISettings settings) : base("Nzxt Kraken Z", new Identifier(dev), settings)
|
||||
{
|
||||
switch (dev.ProductID)
|
||||
{
|
||||
case 0x3008:
|
||||
Name = "NZXT Kraken Z3";
|
||||
_fanControl = true;
|
||||
_supportedFirmware = "5.7.0";
|
||||
Array.Copy(new byte[] { 0x72, 0x01, 0x00, 0x00 }, 0, _setPumpTarget, 0, 4);
|
||||
Array.Copy(new byte[] { 0x72, 0x02, 0x00, 0x00 }, 0, _setFanTarget, 0, 4);
|
||||
break;
|
||||
case 0x300C:
|
||||
Name = "NZXT Kraken Elite";
|
||||
_fanControl = true;
|
||||
_supportedFirmware = "1.2.4";
|
||||
Array.Copy(new byte[] { 0x72, 0x01, 0x01, 0x00 }, 0, _setPumpTarget, 0, 4);
|
||||
Array.Copy(new byte[] { 0x72, 0x02, 0x01, 0x01 }, 0, _setFanTarget, 0, 4);
|
||||
break;
|
||||
case 0x300E:
|
||||
Name = "NZXT Kraken";
|
||||
_fanControl = true;
|
||||
_supportedFirmware = "1.2.4"; // Firmware version to be confirmed
|
||||
Array.Copy(new byte[] { 0x72, 0x01, 0x01, 0x00 }, 0, _setPumpTarget, 0, 4);
|
||||
Array.Copy(new byte[] { 0x72, 0x02, 0x01, 0x01 }, 0, _setFanTarget, 0, 4);
|
||||
break;
|
||||
default:
|
||||
Name = "NZXT Kraken X3";
|
||||
_fanControl = false;
|
||||
_supportedFirmware = "2.1.0";
|
||||
Array.Copy(new byte[] { 0x72, 0x01, 0x00, 0x00 }, 0, _setPumpTarget, 0, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
FillTargetArray(_setPumpTarget, 60);
|
||||
FillTargetArray(_setFanTarget, 40);
|
||||
|
||||
if (dev.TryOpen(out _stream))
|
||||
{
|
||||
_stream.ReadTimeout = 5000;
|
||||
|
||||
_stream.Write(_getFirmwareInfo);
|
||||
|
||||
int tries = 0;
|
||||
|
||||
while (FirmwareVersion == null && tries++ < 10)
|
||||
{
|
||||
_stream.Read(_rawData);
|
||||
|
||||
if (_rawData[0] == 0x11 && _rawData[1] == 0x01)
|
||||
FirmwareVersion = $"{_rawData[0x11]}.{_rawData[0x12]}.{_rawData[0x13]}";
|
||||
}
|
||||
|
||||
if (FirmwareVersion == null)
|
||||
return;
|
||||
|
||||
// Liquid temperature
|
||||
_temperature = new Sensor("Liquid", 0, SensorType.Temperature, this, [], settings);
|
||||
ActivateSensor(_temperature);
|
||||
|
||||
// Pump Control
|
||||
_pump = new Sensor("Pump Control", 0, SensorType.Control, this, [], settings);
|
||||
Control pumpControl = new(_pump, settings, 20, 100);
|
||||
_pump.Control = pumpControl;
|
||||
pumpControl.ControlModeChanged += PumpSoftwareControlValueChanged;
|
||||
pumpControl.SoftwareControlValueChanged += PumpSoftwareControlValueChanged;
|
||||
PumpSoftwareControlValueChanged(pumpControl);
|
||||
ActivateSensor(_pump);
|
||||
|
||||
// Pump RPM
|
||||
_pumpRpm = new Sensor("Pump", 0, SensorType.Fan, this, [], settings);
|
||||
ActivateSensor(_pumpRpm);
|
||||
|
||||
if (_fanControl)
|
||||
{
|
||||
// Fan Control
|
||||
_fan = new Sensor("Fans Control", 1, SensorType.Control, this, [], settings);
|
||||
Control fanControl = new(_fan, settings, 20, 100);
|
||||
_fan.Control = fanControl;
|
||||
fanControl.ControlModeChanged += FanSoftwareControlValueChanged;
|
||||
fanControl.SoftwareControlValueChanged += FanSoftwareControlValueChanged;
|
||||
FanSoftwareControlValueChanged(fanControl);
|
||||
ActivateSensor(_fan);
|
||||
|
||||
// Fan RPM
|
||||
_fanRpm = new Sensor("Fans", 1, SensorType.Fan, this, [], settings);
|
||||
ActivateSensor(_fanRpm);
|
||||
}
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Cooler;
|
||||
|
||||
public bool IsValid { get; }
|
||||
|
||||
public string Status => FirmwareVersion != _supportedFirmware ? $"Status: Untested firmware version {FirmwareVersion}! Please consider updating to version {_supportedFirmware}" : "Status: OK";
|
||||
|
||||
private static void FillTargetArray(byte[] targetArray, byte value)
|
||||
{
|
||||
for (byte i = 4; i < targetArray.Length; i++)
|
||||
targetArray[i] = value;
|
||||
}
|
||||
|
||||
private void PumpSoftwareControlValueChanged(Control control)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (control.ControlMode)
|
||||
{
|
||||
case ControlMode.Software:
|
||||
float value = control.SoftwareValue;
|
||||
|
||||
FillTargetArray(_setPumpTarget, (byte)(value > 100 ? 100 : value < 0 ? 0 : value));
|
||||
|
||||
_controllingPump = true;
|
||||
_stream.Write(_setPumpTarget);
|
||||
_pump.Value = value;
|
||||
break;
|
||||
case ControlMode.Default:
|
||||
// There isn't a "default" mode with this pump, but a safe setting is 60%
|
||||
FillTargetArray(_setPumpTarget, 60);
|
||||
_stream.Write(_setPumpTarget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Could be unplugged, or the app is stopping...
|
||||
}
|
||||
}
|
||||
|
||||
private void FanSoftwareControlValueChanged(Control control)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (control.ControlMode)
|
||||
{
|
||||
case ControlMode.Software:
|
||||
float value = control.SoftwareValue;
|
||||
FillTargetArray(_setFanTarget, (byte)(value > 100 ? 100 : value < 0 ? 0 : value));
|
||||
|
||||
_controllingFans = true;
|
||||
_stream.Write(_setFanTarget);
|
||||
_fan.Value = value;
|
||||
break;
|
||||
case ControlMode.Default:
|
||||
// There isn't a "default" mode with this fan, but a safe setting is 40%
|
||||
FillTargetArray(_setFanTarget, 40);
|
||||
_stream.Write(_setFanTarget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Could be unplugged, or the app is stopping...
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
_stream?.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
_stream.Write(_statusRequest);
|
||||
|
||||
do
|
||||
{
|
||||
_stream.Read(_rawData);
|
||||
}
|
||||
while (_rawData[0] != 0x75 || _rawData[1] != 0x1);
|
||||
|
||||
_temperature.Value = _rawData[15] + (_rawData[16] / 10.0f);
|
||||
_pumpRpm.Value = (_rawData[18] << 8) | _rawData[17];
|
||||
|
||||
// The following logic makes sure the pump is set to the controlling value. This pump sometimes sets itself to 0% when instructed to a value.
|
||||
if (!_controllingPump)
|
||||
{
|
||||
_pump.Value = _rawData[19];
|
||||
}
|
||||
else if (_pump.Value != _rawData[19])
|
||||
{
|
||||
float value = _pump.Value.GetValueOrDefault();
|
||||
FillTargetArray(_setPumpTarget, (byte)value);
|
||||
_stream.Write(_setPumpTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
_controllingPump = false;
|
||||
}
|
||||
|
||||
if (_fanControl)
|
||||
{
|
||||
_fanRpm.Value = (_rawData[24] << 8) | _rawData[23];
|
||||
if (!_controllingFans)
|
||||
{
|
||||
_fan.Value = _rawData[25];
|
||||
}
|
||||
else if (_fan.Value != _rawData[25])
|
||||
{
|
||||
float value = _fan.Value.GetValueOrDefault();
|
||||
FillTargetArray(_setFanTarget, (byte)value);
|
||||
_stream.Write(_setFanTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
_controllingFans = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Could be unplugged, or the app is stopping...
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Nzxt;
|
||||
|
||||
internal class NzxtGroup : IGroup
|
||||
{
|
||||
private readonly List<IHardware> _hardware = new();
|
||||
private readonly StringBuilder _report = new();
|
||||
|
||||
public NzxtGroup(ISettings settings)
|
||||
{
|
||||
_report.AppendLine("Nzxt Hardware");
|
||||
_report.AppendLine();
|
||||
|
||||
foreach (HidDevice dev in DeviceList.Local.GetHidDevices(0x1e71))
|
||||
{
|
||||
string productName = dev.GetProductName();
|
||||
|
||||
switch (dev.ProductID)
|
||||
{
|
||||
case 0x170E: // NZXT Kraken X (X42, X52, X62 or X72)
|
||||
var krakenV2 = new KrakenV2(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {krakenV2.FirmwareVersion}");
|
||||
_report.AppendLine($"{krakenV2.Status}");
|
||||
_report.AppendLine();
|
||||
|
||||
if (krakenV2.IsValid)
|
||||
_hardware.Add(krakenV2);
|
||||
|
||||
break;
|
||||
case 0x2007: // Kraken X3 original pid
|
||||
case 0x2014: // Kraken X3 new pid
|
||||
case 0x3008: // Kraken Z3
|
||||
case 0x300C: // Kraken 2023 elite
|
||||
case 0x300E: // Kraken 2023 standard
|
||||
// NZXT KrakenV3 Devices
|
||||
var krakenV3 = new KrakenV3(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {krakenV3.FirmwareVersion}");
|
||||
_report.AppendLine($"{krakenV3.Status}");
|
||||
_report.AppendLine();
|
||||
|
||||
if (krakenV3.IsValid)
|
||||
_hardware.Add(krakenV3);
|
||||
|
||||
break;
|
||||
case 0x1711:
|
||||
var gridv3 = new GridV3(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {gridv3.FirmwareVersion}");
|
||||
_report.AppendLine();
|
||||
|
||||
if (gridv3.IsValid)
|
||||
_hardware.Add(gridv3);
|
||||
|
||||
break;
|
||||
default:
|
||||
_report.AppendLine($"Unknown Hardware PID: {dev.ProductID} Name: {productName}");
|
||||
_report.AppendLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hardware.Count == 0)
|
||||
{
|
||||
_report.AppendLine("No Nzxt Hardware found.");
|
||||
_report.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (IHardware iHardware in _hardware)
|
||||
{
|
||||
if (iHardware is Hardware hardware)
|
||||
hardware.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
return _report.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Razer;
|
||||
|
||||
internal sealed class RazerFanController : Hardware
|
||||
{
|
||||
private const int DEFAULT_SPEED_CHANNEL_POWER = 50;
|
||||
private const byte PERCENT_MIN = 0;
|
||||
private const byte PERCENT_MAX = 100;
|
||||
private const int DEVICE_READ_DELAY_MS = 5;
|
||||
private const int DEVICE_READ_TIMEOUT_MS = 500;
|
||||
private const int CHANNEL_COUNT = 8;
|
||||
//private const int FORCE_WRITE_SPEEDS_INTERVAL_MS = 2500; // TODO: Add timer
|
||||
|
||||
private HidStream _stream;
|
||||
private readonly HidDevice _device;
|
||||
private readonly SequenceCounter _sequenceCounter = new();
|
||||
|
||||
private readonly float?[] _pwm = new float?[CHANNEL_COUNT];
|
||||
private readonly Sensor[] _pwmControls = new Sensor[CHANNEL_COUNT];
|
||||
private readonly Sensor[] _rpmSensors = new Sensor[CHANNEL_COUNT];
|
||||
|
||||
public RazerFanController(HidDevice dev, ISettings settings) : base("Razer PWM PC Fan Controller", new Identifier(dev), settings)
|
||||
{
|
||||
_device = dev;
|
||||
|
||||
if (_device.TryOpen(out _stream))
|
||||
{
|
||||
_stream.ReadTimeout = 5000;
|
||||
|
||||
Packet packet = new Packet
|
||||
{
|
||||
SequenceNumber = _sequenceCounter.Next(),
|
||||
DataLength = 0,
|
||||
CommandClass = CommandClass.Info,
|
||||
Command = 0x87,
|
||||
};
|
||||
|
||||
if (Mutexes.WaitRazer(250))
|
||||
{
|
||||
while (FirmwareVersion == null)
|
||||
{
|
||||
Thread.Sleep(DEVICE_READ_DELAY_MS);
|
||||
|
||||
try
|
||||
{
|
||||
Packet response = TryWriteAndRead(packet);
|
||||
FirmwareVersion = $"{response.Data[0]:D}.{response.Data[1]:D2}.{response.Data[2]:D2}";
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
Mutexes.ReleaseRazer();
|
||||
}
|
||||
|
||||
Name = "Razer PWM PC Fan Controller";
|
||||
|
||||
for (int i = 0; i < CHANNEL_COUNT; i++)
|
||||
{
|
||||
// Fan Control
|
||||
_pwmControls[i] = new Sensor("Fan Control #" + (i + 1), i, SensorType.Control, this, Array.Empty<ParameterDescription>(), settings);
|
||||
Control fanControl = new(_pwmControls[i], settings, PERCENT_MIN, PERCENT_MAX);
|
||||
_pwmControls[i].Control = fanControl;
|
||||
fanControl.ControlModeChanged += FanSoftwareControlValueChanged;
|
||||
fanControl.SoftwareControlValueChanged += FanSoftwareControlValueChanged;
|
||||
//fanControl.SetDefault();
|
||||
FanSoftwareControlValueChanged(fanControl);
|
||||
ActivateSensor(_pwmControls[i]);
|
||||
|
||||
// Fan RPM
|
||||
_rpmSensors[i] = new Sensor("Fan #" + (i + 1), i, SensorType.Fan, this, Array.Empty<ParameterDescription>(), settings);
|
||||
ActivateSensor(_rpmSensors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string FirmwareVersion { get; }
|
||||
|
||||
public override HardwareType HardwareType => HardwareType.Cooler;
|
||||
|
||||
public string Status => FirmwareVersion != "1.01.00" ? $"Status: Untested Firmware Version {FirmwareVersion}! Please consider Updating to Version 1.01.00" : "Status: OK";
|
||||
|
||||
private void FanSoftwareControlValueChanged(Control control) // TODO: Add timer here
|
||||
{
|
||||
if (control.ControlMode == ControlMode.Undefined || !Mutexes.WaitRazer(250))
|
||||
return;
|
||||
|
||||
if (control.ControlMode == ControlMode.Software)
|
||||
{
|
||||
SetChannelModeToManual(control.Sensor.Index);
|
||||
|
||||
float value = control.SoftwareValue;
|
||||
byte fanSpeed = (byte)(value > 100 ? 100 : value < 0 ? 0 : value);
|
||||
|
||||
var packet = new Packet
|
||||
{
|
||||
SequenceNumber = _sequenceCounter.Next(),
|
||||
DataLength = 3,
|
||||
CommandClass = CommandClass.Pwm,
|
||||
Command = PwmCommand.SetChannelPercent,
|
||||
};
|
||||
|
||||
packet.Data[0] = 0x01;
|
||||
packet.Data[1] = (byte)(0x05 + control.Sensor.Index);
|
||||
packet.Data[2] = fanSpeed;
|
||||
|
||||
TryWriteAndRead(packet);
|
||||
|
||||
_pwm[control.Sensor.Index] = value;
|
||||
}
|
||||
else if (control.ControlMode == ControlMode.Default)
|
||||
{
|
||||
SetChannelModeToManual(control.Sensor.Index); // TODO: switch to auto mode here if it enabled before
|
||||
|
||||
var packet = new Packet
|
||||
{
|
||||
SequenceNumber = _sequenceCounter.Next(),
|
||||
DataLength = 3,
|
||||
CommandClass = CommandClass.Pwm,
|
||||
Command = PwmCommand.SetChannelPercent,
|
||||
};
|
||||
|
||||
packet.Data[0] = 0x01;
|
||||
packet.Data[1] = (byte)(0x05 + control.Sensor.Index);
|
||||
packet.Data[2] = DEFAULT_SPEED_CHANNEL_POWER;
|
||||
|
||||
TryWriteAndRead(packet);
|
||||
|
||||
_pwm[control.Sensor.Index] = DEFAULT_SPEED_CHANNEL_POWER;
|
||||
}
|
||||
|
||||
Mutexes.ReleaseRazer();
|
||||
}
|
||||
|
||||
private int GetChannelSpeed(int channel)
|
||||
{
|
||||
Packet packet = new Packet
|
||||
{
|
||||
SequenceNumber = _sequenceCounter.Next(),
|
||||
DataLength = 6,
|
||||
CommandClass = CommandClass.Pwm,
|
||||
Command = PwmCommand.GetChannelSpeed,
|
||||
};
|
||||
|
||||
packet.Data[0] = 0x01;
|
||||
packet.Data[1] = (byte)(0x05 + channel);
|
||||
|
||||
Packet response = TryWriteAndRead(packet);
|
||||
return (response.Data[4] << 8) | response.Data[5];
|
||||
}
|
||||
|
||||
private void SetChannelModeToManual(int channel)
|
||||
{
|
||||
Packet packet = new Packet
|
||||
{
|
||||
SequenceNumber = _sequenceCounter.Next(),
|
||||
DataLength = 3,
|
||||
CommandClass = CommandClass.Pwm,
|
||||
Command = PwmCommand.SetChannelMode,
|
||||
};
|
||||
|
||||
packet.Data[0] = 0x01;
|
||||
packet.Data[1] = (byte)(0x05 + channel);
|
||||
packet.Data[2] = 0x04;
|
||||
|
||||
TryWriteAndRead(packet);
|
||||
}
|
||||
|
||||
private void ThrowIfNotReady()
|
||||
{
|
||||
bool @throw;
|
||||
try
|
||||
{
|
||||
@throw = _stream is null;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
@throw = true;
|
||||
}
|
||||
|
||||
if (@throw)
|
||||
{
|
||||
throw new InvalidOperationException("The device is not ready.");
|
||||
}
|
||||
}
|
||||
|
||||
private Packet TryWriteAndRead(Packet packet)
|
||||
{
|
||||
Packet readPacket = null;
|
||||
int devTimeout = 400;
|
||||
int devReconnectTimeout = 3000;
|
||||
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] response = Packet.CreateBuffer();
|
||||
byte[] buffer = packet.ToBuffer();
|
||||
|
||||
ThrowIfNotReady();
|
||||
_stream?.SetFeature(buffer, 0, buffer.Length);
|
||||
Thread.Sleep(DEVICE_READ_DELAY_MS);
|
||||
ThrowIfNotReady();
|
||||
_stream?.GetFeature(response, 0, response.Length);
|
||||
readPacket = Packet.FromBuffer(response);
|
||||
|
||||
if (readPacket.Status == DeviceStatus.Busy)
|
||||
{
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
while (stopwatch.ElapsedMilliseconds < DEVICE_READ_TIMEOUT_MS && readPacket.Status == DeviceStatus.Busy)
|
||||
{
|
||||
Thread.Sleep(DEVICE_READ_DELAY_MS);
|
||||
ThrowIfNotReady();
|
||||
_stream?.GetFeature(response, 0, response.Length);
|
||||
readPacket = Packet.FromBuffer(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException) // Unexpected device disconnect or fan plug/unplug
|
||||
{
|
||||
if (devTimeout <= 0)
|
||||
{
|
||||
while (devReconnectTimeout > 0)
|
||||
{
|
||||
_stream?.Close();
|
||||
if (_device.TryOpen(out _stream))
|
||||
break;
|
||||
|
||||
Thread.Sleep(1000);
|
||||
devReconnectTimeout -= 500;
|
||||
}
|
||||
|
||||
if (devReconnectTimeout <= 0) // Device disconnected
|
||||
{
|
||||
for (int i = 0; i < CHANNEL_COUNT; i++)
|
||||
{
|
||||
_pwmControls[i].Control = null;
|
||||
_pwmControls[i].Value = null;
|
||||
_rpmSensors[i].Value = null;
|
||||
_pwm[i] = null;
|
||||
|
||||
DeactivateSensor(_pwmControls[i]);
|
||||
DeactivateSensor(_rpmSensors[i]);
|
||||
}
|
||||
|
||||
Close();
|
||||
|
||||
Packet ret = new Packet();
|
||||
for (int i = 0; i < 80; i++)
|
||||
ret.Data[i] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
devTimeout = 400;
|
||||
}
|
||||
|
||||
Thread.Sleep(DEVICE_READ_DELAY_MS);
|
||||
devTimeout -= DEVICE_READ_DELAY_MS;
|
||||
}
|
||||
} while (readPacket == null);
|
||||
|
||||
return readPacket;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
_stream?.Close();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!Mutexes.WaitRazer(250))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < CHANNEL_COUNT; i++)
|
||||
{
|
||||
_rpmSensors[i].Value = GetChannelSpeed(i);
|
||||
_pwmControls[i].Value = _pwm[i];
|
||||
}
|
||||
|
||||
Mutexes.ReleaseRazer();
|
||||
}
|
||||
|
||||
private enum DeviceStatus : byte
|
||||
{
|
||||
Default = 0x00,
|
||||
Busy = 0x01,
|
||||
Success = 0x02,
|
||||
Error = 0x03,
|
||||
Timeout = 0x04,
|
||||
Invalid = 0x05,
|
||||
}
|
||||
|
||||
private enum ProtocolType : byte
|
||||
{
|
||||
Default = 0x00,
|
||||
}
|
||||
|
||||
private static class CommandClass
|
||||
{
|
||||
public static readonly byte Info = 0x00;
|
||||
public static readonly byte Pwm = 0x0d;
|
||||
}
|
||||
|
||||
private static class PwmCommand
|
||||
{
|
||||
public static readonly byte SetChannelPercent = 0x0d;
|
||||
public static readonly byte SetChannelMode = 0x02;
|
||||
public static readonly byte GetChannelSpeed = 0x81;
|
||||
}
|
||||
|
||||
private sealed class Packet
|
||||
{
|
||||
public byte ReportId { get; set; }
|
||||
public DeviceStatus Status { get; set; }
|
||||
public byte SequenceNumber { get; set; }
|
||||
public short RemainingCount { get; set; }
|
||||
public ProtocolType ProtocolType { get; set; }
|
||||
public byte DataLength { get; set; }
|
||||
public byte CommandClass { get; set; }
|
||||
public byte Command { get; set; }
|
||||
public byte[] Data { get; } = new byte[80];
|
||||
public byte CRC { get; set; }
|
||||
public byte Reserved { get; set; }
|
||||
|
||||
public byte[] ToBuffer()
|
||||
{
|
||||
byte[] buffer = CreateBuffer();
|
||||
buffer[0] = ReportId;
|
||||
buffer[1] = (byte)Status;
|
||||
buffer[2] = SequenceNumber;
|
||||
buffer[3] = (byte)((RemainingCount >> 8) & 0xff);
|
||||
buffer[4] = (byte)(RemainingCount & 0xff);
|
||||
buffer[5] = (byte)ProtocolType;
|
||||
buffer[6] = DataLength;
|
||||
buffer[7] = CommandClass;
|
||||
buffer[8] = Command;
|
||||
|
||||
for (int i = 0; i < Data.Length; i++)
|
||||
buffer[9 + i] = Data[i];
|
||||
|
||||
buffer[89] = GenerateChecksum(buffer);
|
||||
buffer[90] = Reserved;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static Packet FromBuffer(byte[] buffer)
|
||||
{
|
||||
var packet = new Packet
|
||||
{
|
||||
ReportId = buffer[0],
|
||||
Status = (DeviceStatus)buffer[1],
|
||||
SequenceNumber = buffer[2],
|
||||
RemainingCount = (short)((buffer[3] << 8) | buffer[4]),
|
||||
ProtocolType = (ProtocolType)buffer[5],
|
||||
DataLength = buffer[6],
|
||||
CommandClass = buffer[7],
|
||||
Command = buffer[8],
|
||||
CRC = buffer[89],
|
||||
Reserved = buffer[90]
|
||||
};
|
||||
|
||||
for (int i = 0; i < packet.Data.Length; i++)
|
||||
packet.Data[i] = buffer[9 + i];
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public static byte[] CreateBuffer() => new byte[91];
|
||||
|
||||
internal static byte GenerateChecksum(byte[] buffer)
|
||||
{
|
||||
byte result = 0;
|
||||
for (int i = 3; i < 89; i++)
|
||||
{
|
||||
result = (byte)(result ^ buffer[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SequenceCounter
|
||||
{
|
||||
private byte _sequenceId = 0x00;
|
||||
|
||||
public byte Next()
|
||||
{
|
||||
while (_sequenceId == 0x00)
|
||||
{
|
||||
_sequenceId += 0x08;
|
||||
}
|
||||
|
||||
return _sequenceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HidSharp;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.Razer;
|
||||
|
||||
internal class RazerGroup : IGroup
|
||||
{
|
||||
private readonly List<IHardware> _hardware = new();
|
||||
private readonly StringBuilder _report = new();
|
||||
|
||||
public RazerGroup(ISettings settings)
|
||||
{
|
||||
_report.AppendLine("Razer Hardware");
|
||||
_report.AppendLine();
|
||||
|
||||
foreach (HidDevice dev in DeviceList.Local.GetHidDevices(0x1532))
|
||||
{
|
||||
string productName = dev.GetProductName();
|
||||
|
||||
switch (dev.ProductID)
|
||||
{
|
||||
case 0x0F3C: // Razer PWM PC fan controller
|
||||
if (dev.GetMaxFeatureReportLength() <= 0)
|
||||
break;
|
||||
|
||||
var device = new RazerFanController(dev, settings);
|
||||
_report.AppendLine($"Device name: {productName}");
|
||||
_report.AppendLine($"Firmware version: {device.FirmwareVersion}");
|
||||
_report.AppendLine($"{device.Status}");
|
||||
_report.AppendLine();
|
||||
_hardware.Add(device);
|
||||
break;
|
||||
|
||||
default:
|
||||
_report.AppendLine($"Unknown Hardware PID: {dev.ProductID} Name: {productName}");
|
||||
_report.AppendLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hardware.Count == 0)
|
||||
{
|
||||
_report.AppendLine("No Razer Hardware found.");
|
||||
_report.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (IHardware iHardware in _hardware)
|
||||
{
|
||||
if (iHardware is Hardware hardware)
|
||||
hardware.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
return _report.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.TBalancer;
|
||||
|
||||
internal class TBalancer : Hardware
|
||||
{
|
||||
internal const byte EndFlag = 254;
|
||||
internal const byte StartFlag = 100;
|
||||
|
||||
private readonly MethodDelegate _alternativeRequest;
|
||||
private readonly Sensor[] _analogTemperatures = new Sensor[4];
|
||||
private readonly Sensor[] _controls = new Sensor[4];
|
||||
private readonly List<ISensor> _deactivating = new();
|
||||
private readonly Sensor[] _digitalTemperatures = new Sensor[8];
|
||||
private readonly Sensor[] _fans = new Sensor[4];
|
||||
private readonly Sensor[] _miniNgControls = new Sensor[4];
|
||||
private readonly Sensor[] _miniNgFans = new Sensor[4];
|
||||
private readonly Sensor[] _miniNgTemperatures = new Sensor[4];
|
||||
private readonly int _portIndex;
|
||||
private readonly byte _protocolVersion;
|
||||
private readonly Sensor[] _sensorHubFlows = new Sensor[2];
|
||||
private readonly Sensor[] _sensorHubTemperatures = new Sensor[6];
|
||||
private byte[] _alternativeData = Array.Empty<byte>();
|
||||
private readonly byte[] _data = new byte[285];
|
||||
private Ftd2xx.FT_HANDLE _handle;
|
||||
private byte[] _primaryData = Array.Empty<byte>();
|
||||
|
||||
public TBalancer(int portIndex, byte protocolVersion, ISettings settings) :
|
||||
base("T-Balancer bigNG", new Identifier("bigng", portIndex.ToString(CultureInfo.InvariantCulture)), settings)
|
||||
{
|
||||
_portIndex = portIndex;
|
||||
_protocolVersion = protocolVersion;
|
||||
|
||||
ParameterDescription[] parameter = { new("Offset [°C]", "Temperature offset.", 0) };
|
||||
int offset = 0;
|
||||
for (int i = 0; i < _digitalTemperatures.Length; i++)
|
||||
_digitalTemperatures[i] = new Sensor("Digital Sensor " + i, offset + i, SensorType.Temperature, this, parameter, settings);
|
||||
|
||||
offset += _digitalTemperatures.Length;
|
||||
|
||||
for (int i = 0; i < _analogTemperatures.Length; i++)
|
||||
_analogTemperatures[i] = new Sensor("Analog Sensor " + (i + 1), offset + i, SensorType.Temperature, this, parameter, settings);
|
||||
|
||||
offset += _analogTemperatures.Length;
|
||||
|
||||
for (int i = 0; i < _sensorHubTemperatures.Length; i++)
|
||||
_sensorHubTemperatures[i] = new Sensor("Sensorhub Sensor " + i, offset + i, SensorType.Temperature, this, parameter, settings);
|
||||
|
||||
offset += _sensorHubTemperatures.Length;
|
||||
|
||||
for (int i = 0; i < _miniNgTemperatures.Length; i++)
|
||||
_miniNgTemperatures[i] = new Sensor("miniNG #" + ((i / 2) + 1) + " Sensor " + ((i % 2) + 1), offset + i, SensorType.Temperature, this, parameter, settings);
|
||||
|
||||
for (int i = 0; i < _sensorHubFlows.Length; i++)
|
||||
{
|
||||
_sensorHubFlows[i] = new Sensor("Flowmeter " + (i + 1),
|
||||
i,
|
||||
SensorType.Flow,
|
||||
this,
|
||||
new[] { new ParameterDescription("Impulse Rate", "The impulse rate of the flowmeter in pulses/L", 509) },
|
||||
settings);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _controls.Length; i++)
|
||||
{
|
||||
_controls[i] = new Sensor("Fan Channel " + i, i, SensorType.Control, this, settings);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _miniNgControls.Length; i++)
|
||||
{
|
||||
_miniNgControls[i] = new Sensor("miniNG #" + ((i / 2) + 1) + " Fan Channel " + ((i % 2) + 1), 4 + i, SensorType.Control, this, settings);
|
||||
}
|
||||
|
||||
_alternativeRequest = DelayedAlternativeRequest;
|
||||
|
||||
Open();
|
||||
Update();
|
||||
}
|
||||
|
||||
public override HardwareType HardwareType
|
||||
{
|
||||
get { return HardwareType.Cooler; }
|
||||
}
|
||||
|
||||
protected override void ActivateSensor(ISensor sensor)
|
||||
{
|
||||
_deactivating.Remove(sensor);
|
||||
base.ActivateSensor(sensor);
|
||||
}
|
||||
|
||||
protected override void DeactivateSensor(ISensor sensor)
|
||||
{
|
||||
if (_deactivating.Contains(sensor))
|
||||
{
|
||||
_deactivating.Remove(sensor);
|
||||
base.DeactivateSensor(sensor);
|
||||
}
|
||||
else if (_active.Contains(sensor))
|
||||
{
|
||||
_deactivating.Add(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadMiniNg(int number)
|
||||
{
|
||||
int offset = 1 + (number * 65);
|
||||
|
||||
if (_data[offset + 61] != EndFlag)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Sensor sensor = _miniNgTemperatures[(number * 2) + i];
|
||||
if (_data[offset + 7 + i] > 0)
|
||||
{
|
||||
sensor.Value = (0.5f * _data[offset + 7 + i]) +
|
||||
sensor.Parameters[0].Value;
|
||||
|
||||
ActivateSensor(sensor);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
_miniNgFans[(number * 2) + i] ??= new Sensor("miniNG #" + (number + 1) + " Fan Channel " + (i + 1), 4 + (number * 2) + i, SensorType.Fan, this, _settings);
|
||||
|
||||
Sensor sensor = _miniNgFans[(number * 2) + i];
|
||||
sensor.Value = 20.0f * _data[offset + 43 + (2 * i)];
|
||||
ActivateSensor(sensor);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Sensor sensor = _miniNgControls[(number * 2) + i];
|
||||
sensor.Value = _data[offset + 15 + i];
|
||||
ActivateSensor(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadData()
|
||||
{
|
||||
Ftd2xx.Read(_handle, _data);
|
||||
if (_data[0] != StartFlag)
|
||||
{
|
||||
Ftd2xx.FT_Purge(_handle, Ftd2xx.FT_PURGE.FT_PURGE_RX);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data[1] is 255 or 88)
|
||||
{
|
||||
// bigNG
|
||||
|
||||
if (_data[274] != _protocolVersion)
|
||||
return;
|
||||
|
||||
if (_primaryData.Length == 0)
|
||||
_primaryData = new byte[_data.Length];
|
||||
|
||||
_data.CopyTo(_primaryData, 0);
|
||||
|
||||
for (int i = 0; i < _digitalTemperatures.Length; i++)
|
||||
{
|
||||
if (_data[238 + i] > 0)
|
||||
{
|
||||
_digitalTemperatures[i].Value = (0.5f * _data[238 + i]) + _digitalTemperatures[i].Parameters[0].Value;
|
||||
ActivateSensor(_digitalTemperatures[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_digitalTemperatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _analogTemperatures.Length; i++)
|
||||
{
|
||||
if (_data[260 + i] > 0)
|
||||
{
|
||||
_analogTemperatures[i].Value = (0.5f * _data[260 + i]) + _analogTemperatures[i].Parameters[0].Value;
|
||||
ActivateSensor(_analogTemperatures[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_analogTemperatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _sensorHubTemperatures.Length; i++)
|
||||
{
|
||||
if (_data[246 + i] > 0)
|
||||
{
|
||||
_sensorHubTemperatures[i].Value = (0.5f * _data[246 + i]) + _sensorHubTemperatures[i].Parameters[0].Value;
|
||||
ActivateSensor(_sensorHubTemperatures[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_sensorHubTemperatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _sensorHubFlows.Length; i++)
|
||||
{
|
||||
if (_data[231 + i] > 0 && _data[234] > 0)
|
||||
{
|
||||
float pulsesPerSecond = (_data[231 + i] * 4.0f) / _data[234];
|
||||
float pulsesPerLiter = _sensorHubFlows[i].Parameters[0].Value;
|
||||
_sensorHubFlows[i].Value = pulsesPerSecond * 3600 / pulsesPerLiter;
|
||||
ActivateSensor(_sensorHubFlows[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeactivateSensor(_sensorHubFlows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _fans.Length; i++)
|
||||
{
|
||||
float maxRpm = 11.5f * ((_data[149 + (2 * i)] << 8) | _data[148 + (2 * i)]);
|
||||
|
||||
_fans[i] ??= new Sensor("Fan Channel " + i,
|
||||
i,
|
||||
SensorType.Fan,
|
||||
this,
|
||||
new[]
|
||||
{
|
||||
new ParameterDescription("MaxRPM",
|
||||
"Maximum revolutions per minute (RPM) of the fan.",
|
||||
maxRpm)
|
||||
},
|
||||
_settings);
|
||||
|
||||
float value;
|
||||
if ((_data[136] & (1 << i)) == 0) // pwm mode
|
||||
value = 0.02f * _data[137 + i];
|
||||
else // analog mode
|
||||
value = 0.01f * _data[141 + i];
|
||||
|
||||
_fans[i].Value = _fans[i].Parameters[0].Value * value;
|
||||
ActivateSensor(_fans[i]);
|
||||
|
||||
_controls[i].Value = 100 * value;
|
||||
ActivateSensor(_controls[i]);
|
||||
}
|
||||
}
|
||||
else if (_data[1] == 253)
|
||||
{
|
||||
// miniNG #1
|
||||
if (_alternativeData.Length == 0)
|
||||
_alternativeData = new byte[_data.Length];
|
||||
|
||||
_data.CopyTo(_alternativeData, 0);
|
||||
|
||||
ReadMiniNg(0);
|
||||
if (_data[66] == 253) // miniNG #2
|
||||
ReadMiniNg(1);
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetReport()
|
||||
{
|
||||
StringBuilder r = new();
|
||||
|
||||
r.AppendLine("T-Balancer bigNG");
|
||||
r.AppendLine();
|
||||
r.Append("Port Index: ");
|
||||
r.AppendLine(_portIndex.ToString(CultureInfo.InvariantCulture));
|
||||
r.AppendLine();
|
||||
|
||||
r.AppendLine("Primary System Information Answer");
|
||||
r.AppendLine();
|
||||
r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
|
||||
r.AppendLine();
|
||||
for (int i = 0; i <= 0x11; i++)
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append((i << 4).ToString("X3", CultureInfo.InvariantCulture));
|
||||
r.Append(" ");
|
||||
for (int j = 0; j <= 0xF; j++)
|
||||
{
|
||||
int index = (i << 4) | j;
|
||||
if (index < _primaryData.Length)
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append(_primaryData[index].ToString("X2", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
|
||||
if (_alternativeData.Length > 0)
|
||||
{
|
||||
r.AppendLine("Alternative System Information Answer");
|
||||
r.AppendLine();
|
||||
r.AppendLine(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
|
||||
r.AppendLine();
|
||||
for (int i = 0; i <= 0x11; i++)
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append((i << 4).ToString("X3", CultureInfo.InvariantCulture));
|
||||
r.Append(" ");
|
||||
for (int j = 0; j <= 0xF; j++)
|
||||
{
|
||||
int index = (i << 4) | j;
|
||||
if (index < _alternativeData.Length)
|
||||
{
|
||||
r.Append(" ");
|
||||
r.Append(_alternativeData[index].ToString("X2", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
}
|
||||
|
||||
r.AppendLine();
|
||||
}
|
||||
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
private void DelayedAlternativeRequest()
|
||||
{
|
||||
System.Threading.Thread.Sleep(500);
|
||||
Ftd2xx.Write(_handle, new byte[] { 0x37 });
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
Ftd2xx.FT_Open(_portIndex, out _handle);
|
||||
Ftd2xx.FT_SetBaudRate(_handle, 19200);
|
||||
Ftd2xx.FT_SetDataCharacteristics(_handle, 8, 1, 0);
|
||||
Ftd2xx.FT_SetFlowControl(_handle, Ftd2xx.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11, 0x13);
|
||||
Ftd2xx.FT_SetTimeouts(_handle, 1000, 1000);
|
||||
Ftd2xx.FT_Purge(_handle, Ftd2xx.FT_PURGE.FT_PURGE_ALL);
|
||||
}
|
||||
|
||||
public sealed override void Update()
|
||||
{
|
||||
while (Ftd2xx.BytesToRead(_handle) >= 285)
|
||||
ReadData();
|
||||
|
||||
if (Ftd2xx.BytesToRead(_handle) == 1)
|
||||
Ftd2xx.ReadByte(_handle);
|
||||
|
||||
Ftd2xx.Write(_handle, new byte[] { 0x38 });
|
||||
_alternativeRequest.BeginInvoke(null, null);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
Ftd2xx.FT_Close(_handle);
|
||||
base.Close();
|
||||
}
|
||||
|
||||
private delegate void MethodDelegate();
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Controller.TBalancer;
|
||||
|
||||
internal class TBalancerGroup : IGroup
|
||||
{
|
||||
private readonly List<TBalancer> _hardware = new();
|
||||
private readonly StringBuilder _report = new();
|
||||
|
||||
public TBalancerGroup(ISettings settings)
|
||||
{
|
||||
uint numDevices;
|
||||
|
||||
try
|
||||
{
|
||||
if (!Ftd2xx.DllExists())
|
||||
{
|
||||
_report.AppendLine("Status: missing DLL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Ftd2xx.FT_CreateDeviceInfoList(out numDevices) != Ftd2xx.FT_STATUS.FT_OK)
|
||||
{
|
||||
_report.AppendLine("Status: FT_CreateDeviceInfoList failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is DllNotFoundException or ArgumentNullException or EntryPointNotFoundException or BadImageFormatException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Ftd2xx.FT_DEVICE_INFO_NODE[] info = new Ftd2xx.FT_DEVICE_INFO_NODE[numDevices];
|
||||
if (Ftd2xx.FT_GetDeviceInfoList(info, ref numDevices) != Ftd2xx.FT_STATUS.FT_OK)
|
||||
{
|
||||
_report.AppendLine("Status: FT_GetDeviceInfoList failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure numDevices is not larger than the info array
|
||||
if (numDevices > info.Length)
|
||||
numDevices = (uint)info.Length;
|
||||
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
_report.Append("Device Index: ");
|
||||
_report.AppendLine(i.ToString(CultureInfo.InvariantCulture));
|
||||
_report.Append("Device Type: ");
|
||||
_report.AppendLine(info[i].Type.ToString());
|
||||
|
||||
// the T-Balancer always uses an FT232BM
|
||||
if (info[i].Type != Ftd2xx.FT_DEVICE.FT_DEVICE_232BM)
|
||||
{
|
||||
_report.AppendLine("Status: Wrong device type");
|
||||
continue;
|
||||
}
|
||||
|
||||
Ftd2xx.FT_STATUS status = Ftd2xx.FT_Open(i, out Ftd2xx.FT_HANDLE handle);
|
||||
if (status != Ftd2xx.FT_STATUS.FT_OK)
|
||||
{
|
||||
_report.AppendLine("Open Status: " + status);
|
||||
continue;
|
||||
}
|
||||
|
||||
Ftd2xx.FT_SetBaudRate(handle, 19200);
|
||||
Ftd2xx.FT_SetDataCharacteristics(handle, 8, 1, 0);
|
||||
Ftd2xx.FT_SetFlowControl(handle, Ftd2xx.FT_FLOW_CONTROL.FT_FLOW_RTS_CTS, 0x11, 0x13);
|
||||
Ftd2xx.FT_SetTimeouts(handle, 1000, 1000);
|
||||
Ftd2xx.FT_Purge(handle, Ftd2xx.FT_PURGE.FT_PURGE_ALL);
|
||||
|
||||
status = Ftd2xx.Write(handle, new byte[] { 0x38 });
|
||||
if (status != Ftd2xx.FT_STATUS.FT_OK)
|
||||
{
|
||||
_report.AppendLine("Write Status: " + status);
|
||||
Ftd2xx.FT_Close(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isValid = false;
|
||||
byte protocolVersion = 0;
|
||||
|
||||
int j = 0;
|
||||
while (Ftd2xx.BytesToRead(handle) == 0 && j < 2)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
j++;
|
||||
}
|
||||
|
||||
if (Ftd2xx.BytesToRead(handle) > 0)
|
||||
{
|
||||
if (Ftd2xx.ReadByte(handle) == TBalancer.StartFlag)
|
||||
{
|
||||
while (Ftd2xx.BytesToRead(handle) < 284 && j < 5)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
j++;
|
||||
}
|
||||
|
||||
int length = Ftd2xx.BytesToRead(handle);
|
||||
if (length >= 284)
|
||||
{
|
||||
byte[] data = new byte[285];
|
||||
data[0] = TBalancer.StartFlag;
|
||||
for (int k = 1; k < data.Length; k++)
|
||||
data[k] = Ftd2xx.ReadByte(handle);
|
||||
|
||||
// check protocol version 2X (protocols seen: 2C, 2A, 28)
|
||||
isValid = (data[274] & 0xF0) == 0x20;
|
||||
protocolVersion = data[274];
|
||||
if (!isValid)
|
||||
{
|
||||
_report.Append("Status: Wrong Protocol Version: 0x");
|
||||
_report.AppendLine(protocolVersion.ToString("X", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_report.AppendLine("Status: Wrong Message Length: " + length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_report.AppendLine("Status: Wrong Startflag");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_report.AppendLine("Status: No Response");
|
||||
}
|
||||
|
||||
Ftd2xx.FT_Purge(handle, Ftd2xx.FT_PURGE.FT_PURGE_ALL);
|
||||
Ftd2xx.FT_Close(handle);
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
_report.AppendLine("Status: OK");
|
||||
_hardware.Add(new TBalancer(i, protocolVersion, settings));
|
||||
}
|
||||
|
||||
if (i < numDevices - 1)
|
||||
_report.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<IHardware> Hardware => _hardware;
|
||||
|
||||
public string GetReport()
|
||||
{
|
||||
if (_report.Length > 0)
|
||||
{
|
||||
StringBuilder r = new();
|
||||
r.AppendLine("FTD2XX");
|
||||
r.AppendLine();
|
||||
r.Append(_report);
|
||||
r.AppendLine();
|
||||
return r.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
foreach (TBalancer balancer in _hardware)
|
||||
balancer.Close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user