From cdb0c65b4c5d6ad2c4dc0619ccf907176a020cf7 Mon Sep 17 00:00:00 2001 From: Blacktempel <6657796+Blacktempel@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:46:37 +0100 Subject: [PATCH] TG WireView Pro 2: Changes for firmware update (#2193) * Changes for firmware update. * Undo SharedSerialPort.Open signature change. --- .../Hardware/Gpu/PowerMonitor/WireViewPro2.cs | 237 +++++++++--------- .../PowerMonitor/StructureConversion.cs | 57 +++++ .../Interop/PowerMonitor/WireViewPro2Enums.cs | 12 + .../PowerMonitor/WireViewPro2Structures.cs | 32 ++- 4 files changed, 223 insertions(+), 115 deletions(-) create mode 100644 LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs diff --git a/LibreHardwareMonitorLib/Hardware/Gpu/PowerMonitor/WireViewPro2.cs b/LibreHardwareMonitorLib/Hardware/Gpu/PowerMonitor/WireViewPro2.cs index f97aa04..81c3825 100644 --- a/LibreHardwareMonitorLib/Hardware/Gpu/PowerMonitor/WireViewPro2.cs +++ b/LibreHardwareMonitorLib/Hardware/Gpu/PowerMonitor/WireViewPro2.cs @@ -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(); + break; + case 1: + size = Marshal.SizeOf(); + break; + default: + return null; + } - byte[] bytes = ReadExact(Marshal.SizeOf()); + var buf = SendCmd(UsbCmd.CMD_READ_CONFIG, size); - return bytes == null ? null : BytesToStructure(bytes); + if (buf == null) + { + return null; + } + + switch (ConfigVersion) + { + case 0: + var s = BytesToStructure(buf); + return StructureConversion.ConvertConfigV1ToV2(s); + case 1: + return BytesToStructure(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()); - byte[] bytes = ReadExact(Marshal.SizeOf()); - - return bytes == null ? null : BytesToStructure(bytes); - } - finally - { - _serialPort.Close(); - } + return bytes == null ? null : BytesToStructure(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()); - byte[] bytes = ReadExact(Marshal.SizeOf()); - - return bytes == null ? null : BytesToStructure(bytes); - } - finally - { - _serialPort.Close(); - } + return bytes == null ? null : BytesToStructure(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]; diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs new file mode 100644 index 0000000..ccb0096 --- /dev/null +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs @@ -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; + } +} diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs index 5822ef8..0ed786a 100644 --- a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs @@ -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 +} diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs index 8aab921..8fd3d9e 100644 --- a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs @@ -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; +}