TG WireView Pro 2: Changes for firmware update (#2193)

* Changes for firmware update.

* Undo SharedSerialPort.Open signature change.
This commit is contained in:
Blacktempel
2026-02-03 11:46:37 +01:00
committed by GitHub
parent 541926c757
commit cdb0c65b4c
4 changed files with 223 additions and 115 deletions
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using LibreHardwareMonitor.Interop.PowerMonitor;
namespace LibreHardwareMonitor.Hardware.Gpu.PowerMonitor;
@@ -46,6 +47,8 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
public VendorDataStruct? VendorData { get; private set; }
public int ConfigVersion => VendorData?.FwVersion > 2 ? 1 : 0;
public static WireViewPro2 TryFindDevice(ISettings settings)
{
if (!Software.OperatingSystem.IsWindows8OrGreater)
@@ -111,22 +114,46 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
}
}
public DeviceConfigStruct? ReadConfig()
public DeviceConfigStructV2? ReadConfig()
{
if (!IsConnected)
{
return null;
}
int size = 0;
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([(byte)UsbCmd.CMD_READ_CONFIG], 0, 1);
switch (ConfigVersion)
{
case 0:
size = Marshal.SizeOf<DeviceConfigStructV1>();
break;
case 1:
size = Marshal.SizeOf<DeviceConfigStructV2>();
break;
default:
return null;
}
byte[] bytes = ReadExact(Marshal.SizeOf<DeviceConfigStruct>());
var buf = SendCmd(UsbCmd.CMD_READ_CONFIG, size);
return bytes == null ? null : BytesToStructure<DeviceConfigStruct>(bytes);
if (buf == null)
{
return null;
}
switch (ConfigVersion)
{
case 0:
var s = BytesToStructure<DeviceConfigStructV1>(buf);
return StructureConversion.ConvertConfigV1ToV2(s);
case 1:
return BytesToStructure<DeviceConfigStructV2>(buf);
default:
return null;
}
}
finally
{
@@ -134,14 +161,27 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
}
}
public void WriteConfig(DeviceConfigStruct config)
public void WriteConfig(DeviceConfigStructV2 config)
{
if (!IsConnected)
{
return;
}
byte[] payload = StructureToBytes(config);
byte[] payload = [];
switch (ConfigVersion)
{
case 0:
var s = StructureConversion.ConvertConfigV2ToV1(config);
payload = StructureToBytes(s);
break;
case 1:
payload = StructureToBytes(config);
break;
default:
return;
}
byte[] frame = new byte[64];
frame[0] = (byte)UsbCmd.CMD_WRITE_CONFIG;
@@ -176,25 +216,15 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
return;
}
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([
(byte)UsbCmd.CMD_NVM_CONFIG,
0x55, //Magic
0xAA, //Magic
0x55, //Magic
0xAA, //Magic
(byte)cmd
],
0,
6);
}
finally
{
_serialPort.Close();
}
SendData(
[
(byte)UsbCmd.CMD_NVM_CONFIG,
0x55, //Magic
0xAA, //Magic
0x55, //Magic
0xAA, //Magic
(byte)cmd
]);
}
public void ScreenCmd(SCREEN_CMD cmd)
@@ -204,16 +234,7 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
return;
}
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([(byte)UsbCmd.CMD_SCREEN_CHANGE, (byte)cmd], 0, 2);
}
finally
{
_serialPort.Close();
}
SendData([(byte)UsbCmd.CMD_SCREEN_CHANGE, (byte)cmd]);
}
public void ClearFaults(int faultStatusMask = 0xFFFF, int faultLogMask = 0xFFFF)
@@ -223,24 +244,14 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
return;
}
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([
(byte)UsbCmd.CMD_CLEAR_FAULTS,
(byte)(faultStatusMask & 0xFF),
(byte)((faultStatusMask >> 8) & 0xFF),
(byte)(faultLogMask & 0xFF),
(byte)((faultLogMask >> 8) & 0xFF)
],
0,
5);
}
finally
{
_serialPort.Close();
}
SendData(
[
(byte)UsbCmd.CMD_CLEAR_FAULTS,
(byte)(faultStatusMask & 0xFF),
(byte)((faultStatusMask >> 8) & 0xFF),
(byte)(faultLogMask & 0xFF),
(byte)((faultLogMask >> 8) & 0xFF)
]);
}
private void CreateSensors()
@@ -364,63 +375,25 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
{
int size = WelcomeMessage.Length + 1;
try
{
_serialPort.Open();
_serialPort.RtsEnable = true;
var bytes = SendData([(byte)UsbCmd.CMD_WELCOME], size, true);
if (sendCmd)
{
_serialPort.DiscardInBuffer();
_serialPort.Write([(byte)UsbCmd.CMD_WELCOME], 0, 1);
}
byte[] bytes = ReadExact(size);
_serialPort.RtsEnable = false;
return bytes == null ? false : Encoding.ASCII.GetString(bytes, 0, size).TrimEnd('\0').CompareTo(WelcomeMessage) == 0;
}
finally
{
_serialPort.Close();
}
return bytes == null ? false : Encoding.ASCII.GetString(bytes, 0, size).TrimEnd('\0').CompareTo(WelcomeMessage) == 0;
}
private VendorDataStruct? ReadVendorData()
{
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([(byte)UsbCmd.CMD_READ_VENDOR_DATA], 0, 1);
var bytes = SendCmd(UsbCmd.CMD_READ_VENDOR_DATA, Marshal.SizeOf<VendorDataStruct>());
byte[] bytes = ReadExact(Marshal.SizeOf<VendorDataStruct>());
return bytes == null ? null : BytesToStructure<VendorDataStruct>(bytes);
}
finally
{
_serialPort.Close();
}
return bytes == null ? null : BytesToStructure<VendorDataStruct>(bytes);
}
private string ReadUniqueID()
{
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([(byte)UsbCmd.CMD_READ_UID], 0, 1);
const int UIDBytes = 12;
byte[] bytes = ReadExact(12);
var bytes = SendCmd(UsbCmd.CMD_READ_UID, UIDBytes);
return bytes == null ? null : BitConverter.ToString(bytes).Replace("-", string.Empty);
}
finally
{
_serialPort.Close();
}
return bytes == null ? null : BitConverter.ToString(bytes).Replace("-", string.Empty);
}
private SensorStruct? ReadSensorValues()
@@ -430,20 +403,9 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
return null;
}
try
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.Write([(byte)UsbCmd.CMD_READ_SENSOR_VALUES], 0, 1);
var bytes = SendCmd(UsbCmd.CMD_READ_SENSOR_VALUES, Marshal.SizeOf<SensorStruct>());
byte[] bytes = ReadExact(Marshal.SizeOf<SensorStruct>());
return bytes == null ? null : BytesToStructure<SensorStruct>(bytes);
}
finally
{
_serialPort.Close();
}
return bytes == null ? null : BytesToStructure<SensorStruct>(bytes);
}
private DeviceData MapSensorStructure(SensorStruct sensorStruct)
@@ -486,6 +448,55 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor
return deviceData;
}
private byte[] SendCmd(UsbCmd cmd, int responseSize = 0, bool rts = false)
{
return SendData(new[] { (byte)cmd }, responseSize, rts);
}
private byte[] SendData(byte[] data, int responseSize = 0, bool rts = false)
{
if (_serialPort == null)
{
return null;
}
byte[] buf = null;
try
{
lock (_serialPort)
{
_serialPort.Open();
_serialPort.DiscardInBuffer();
if (rts)
{
_serialPort.RtsEnable = true;
Thread.Sleep(10);
}
_serialPort.Write(data, 0, data.Length);
if (responseSize > 0)
{
buf = ReadExact(responseSize);
}
if (rts)
{
Thread.Sleep(10);
_serialPort.RtsEnable = false;
}
}
}
finally
{
_serialPort.Close();
}
return buf;
}
private byte[] ReadExact(int size)
{
byte[] buffer = new byte[size];
@@ -0,0 +1,57 @@
namespace LibreHardwareMonitor.Interop.PowerMonitor;
public static class StructureConversion
{
public static DeviceConfigStructV1 ConvertConfigV2ToV1(DeviceConfigStructV2 configV2)
{
DeviceConfigStructV1 configV1 = new DeviceConfigStructV1
{
Crc = configV2.Crc,
Version = configV2.Version,
FriendlyName = configV2.FriendlyName,
FanConfig = configV2.FanConfig,
BacklightDuty = configV2.BacklightDuty,
FaultDisplayEnable = configV2.FaultDisplayEnable,
FaultBuzzerEnable = configV2.FaultBuzzerEnable,
FaultSoftPowerEnable = configV2.FaultSoftPowerEnable,
FaultHardPowerEnable = configV2.FaultHardPowerEnable,
TsFaultThreshold = configV2.TsFaultThreshold,
OcpFaultThreshold = configV2.OcpFaultThreshold,
WireOcpFaultThreshold = configV2.WireOcpFaultThreshold,
OppFaultThreshold = configV2.OppFaultThreshold,
CurrentImbalanceFaultThreshold = configV2.CurrentImbalanceFaultThreshold,
CurrentImbalanceFaultMinLoad = configV2.CurrentImbalanceFaultMinLoad,
ShutdownWaitTime = configV2.ShutdownWaitTime,
LoggingInterval = configV2.LoggingInterval,
Ui = configV2.Ui
};
return configV1;
}
public static DeviceConfigStructV2 ConvertConfigV1ToV2(DeviceConfigStructV1 configV1)
{
DeviceConfigStructV2 configV2 = new DeviceConfigStructV2
{
Crc = configV1.Crc,
Version = configV1.Version,
FriendlyName = configV1.FriendlyName,
FanConfig = configV1.FanConfig,
BacklightDuty = configV1.BacklightDuty,
FaultDisplayEnable = configV1.FaultDisplayEnable,
FaultBuzzerEnable = configV1.FaultBuzzerEnable,
FaultSoftPowerEnable = configV1.FaultSoftPowerEnable,
FaultHardPowerEnable = configV1.FaultHardPowerEnable,
TsFaultThreshold = configV1.TsFaultThreshold,
OcpFaultThreshold = configV1.OcpFaultThreshold,
WireOcpFaultThreshold = configV1.WireOcpFaultThreshold,
OppFaultThreshold = configV1.OppFaultThreshold,
CurrentImbalanceFaultThreshold = configV1.CurrentImbalanceFaultThreshold,
CurrentImbalanceFaultMinLoad = configV1.CurrentImbalanceFaultMinLoad,
ShutdownWaitTime = configV1.ShutdownWaitTime,
LoggingInterval = configV1.LoggingInterval,
Average = AVG.AVG_1417MS, // Default value
Ui = configV1.Ui
};
return configV2;
}
}
@@ -118,3 +118,15 @@ public enum SCREEN_CMD : byte
SCREEN_PAUSE_UPDATES = 0xF0,
SCREEN_RESUME_UPDATES = 0xF1
}
public enum AVG : byte
{
AVG_22MS,
AVG_44MS,
AVG_89MS,
AVG_177MS,
AVG_354MS,
AVG_709MS,
AVG_1417MS,
AVG_NUM
}
@@ -68,8 +68,8 @@ public struct UiConfigStruct
public byte Timeout;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct DeviceConfigStruct
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct DeviceConfigStructV1
{
public ushort Crc;
public byte Version;
@@ -94,3 +94,31 @@ public struct DeviceConfigStruct
public byte LoggingInterval; // seconds
public UiConfigStruct Ui;
}
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct DeviceConfigStructV2
{
public ushort Crc;
public byte Version;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] FriendlyName;
public FanConfigStruct FanConfig;
public byte BacklightDuty;
public ushort FaultDisplayEnable;
public ushort FaultBuzzerEnable;
public ushort FaultSoftPowerEnable;
public ushort FaultHardPowerEnable;
public short TsFaultThreshold; // 0.1 °C
public byte OcpFaultThreshold; // A
public byte WireOcpFaultThreshold; // 0.1A
public ushort OppFaultThreshold; // W
public byte CurrentImbalanceFaultThreshold; // %
public byte CurrentImbalanceFaultMinLoad; // A
public byte ShutdownWaitTime; // seconds
public byte LoggingInterval; // seconds
public AVG Average;
public UiConfigStruct Ui;
}