diff --git a/LibreHardwareMonitorLib/Hardware/PowerMonitor/SharedSerialPort.cs b/LibreHardwareMonitorLib/Hardware/PowerMonitor/SharedSerialPort.cs index 84ea4e5..9a257ab 100644 --- a/LibreHardwareMonitorLib/Hardware/PowerMonitor/SharedSerialPort.cs +++ b/LibreHardwareMonitorLib/Hardware/PowerMonitor/SharedSerialPort.cs @@ -31,7 +31,7 @@ internal sealed class SharedSerialPort : SerialPort public new void Open() { _hasMutex = Mutexes.WaitUsbSensors(MutexTimeout); - if (_hasMutex) + if (_hasMutex && !IsOpen) { base.Open(); } @@ -39,14 +39,20 @@ internal sealed class SharedSerialPort : SerialPort public new void Close() { - if (_hasMutex) + if (!_hasMutex) + { + return; + } + + try { if (IsOpen) { - BaseStream.Flush(); - BaseStream.Close(); + base.Close(); } - + } + finally + { _hasMutex = false; Mutexes.ReleaseUsbSensors(); } diff --git a/LibreHardwareMonitorLib/Hardware/PowerMonitor/WireViewPro2.cs b/LibreHardwareMonitorLib/Hardware/PowerMonitor/WireViewPro2.cs index 94f5632..18a0158 100644 --- a/LibreHardwareMonitorLib/Hardware/PowerMonitor/WireViewPro2.cs +++ b/LibreHardwareMonitorLib/Hardware/PowerMonitor/WireViewPro2.cs @@ -42,6 +42,7 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor private readonly string _portName; private readonly List _sensors = []; + private readonly object _serialPortSync = new(); private SharedSerialPort _serialPort; public WireViewPro2(string portName, ISettings settings, int baud = 115200) @@ -66,7 +67,7 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor public VendorDataStruct? VendorData { get; private set; } - public int ConfigVersion => VendorData?.FwVersion > 2 ? 1 : 0; + public int ConfigVersion { get; private set; } public static List TryFindDevices(ISettings settings) { @@ -147,7 +148,7 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor public DeviceData GetDeviceData() { SensorStruct? sensorValues = null; - DeviceConfigStructV2? config = null; + DeviceConfigStructV3? config = null; try { @@ -173,7 +174,7 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor return null; } - public DeviceConfigStructV2? ReadConfig() + public DeviceConfigStructV3? ReadConfig() { if (!IsConnected) { @@ -190,6 +191,9 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor case 1: size = Marshal.SizeOf(); break; + case 2: + size = Marshal.SizeOf(); + break; default: return null; } @@ -204,16 +208,19 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor switch (ConfigVersion) { case 0: - var s = BytesToStructure(buf); - return StructureConversion.ConvertConfigV1ToV2(s); + var s1 = BytesToStructure(buf); + return StructureConversion.ConvertConfigV1ToV3(s1); case 1: - return BytesToStructure(buf); + var s2 = BytesToStructure(buf); + return StructureConversion.ConvertConfigV2ToV3(s2); + case 2: + return BytesToStructure(buf); default: return null; } } - public void WriteConfig(DeviceConfigStructV2 config) + public void WriteConfig(DeviceConfigStructV3 config) { if (!IsConnected) { @@ -225,10 +232,14 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor switch (ConfigVersion) { case 0: - var s = StructureConversion.ConvertConfigV2ToV1(config); - payload = StructureToBytes(s); + var s1 = StructureConversion.ConvertConfigV3ToV1(config); + payload = StructureToBytes(s1); break; case 1: + var s2 = StructureConversion.ConvertConfigV3ToV2(config); + payload = StructureToBytes(s2); + break; + case 2: payload = StructureToBytes(config); break; default: @@ -238,26 +249,29 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor byte[] frame = new byte[64]; frame[0] = (byte)UsbCmd.CMD_WRITE_CONFIG; - try + lock (_serialPortSync) { - _serialPort.Open(); - _serialPort.DiscardInBuffer(); - - const int maxPayloadPerFrame = 62; - - for (byte offset = 0; offset < payload.Length; offset += maxPayloadPerFrame) + try { - int bytesToWrite = Math.Min(maxPayloadPerFrame, payload.Length - offset); + _serialPort.Open(); + _serialPort.DiscardInBuffer(); - frame[1] = offset; - Buffer.BlockCopy(payload, offset, frame, 2, bytesToWrite); + const int maxPayloadPerFrame = 62; - _serialPort.Write(frame, 0, bytesToWrite + 2); + for (byte offset = 0; offset < payload.Length; offset += maxPayloadPerFrame) + { + int bytesToWrite = Math.Min(maxPayloadPerFrame, payload.Length - offset); + + frame[1] = offset; + Buffer.BlockCopy(payload, offset, frame, 2, bytesToWrite); + + _serialPort.Write(frame, 0, bytesToWrite + 2); + } + } + finally + { + _serialPort.Close(); } - } - finally - { - _serialPort.Close(); } } @@ -582,6 +596,18 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor vendorData.Value.ProductId == ProductID) { VendorData = vendorData.Value; + + var configVersion = ReadConfigVersion(); + if (configVersion == null) + { + IsConnected = false; + return; + } + else + { + ConfigVersion = configVersion.Value; + } + UniqueID = ReadUniqueID(); IsConnected = true; @@ -615,22 +641,25 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor private void Disconnect() { - if (!IsConnected) + lock (_serialPortSync) { - return; + if (!IsConnected) + { + return; + } + + if (_serialPort != null) + { + _serialPort.Close(); + _serialPort.Dispose(); + _serialPort = null; + } + + IsConnected = false; + + VendorData = null; + UniqueID = null; } - - if (_serialPort != null) - { - _serialPort.Close(); - _serialPort.Dispose(); - _serialPort = null; - } - - IsConnected = false; - - VendorData = null; - UniqueID = null; } private bool ReadWelcomeMessage(bool sendCmd = false) @@ -670,7 +699,7 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor return bytes == null ? null : BytesToStructure(bytes); } - private DeviceData MapSensorStructure(SensorStruct sensorStruct, DeviceConfigStructV2 config) + private DeviceData MapSensorStructure(SensorStruct sensorStruct, DeviceConfigStructV3 config) { var deviceData = new DeviceData { @@ -711,6 +740,13 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor return deviceData; } + private int? ReadConfigVersion() + { + var bytes = SendCmd(UsbCmd.CMD_READ_CONFIG, 4); + + return bytes == null ? null : bytes[2]; + } + private byte[] SendCmd(UsbCmd cmd, int responseSize = 0, bool rts = false) { return SendData(new[] { (byte)cmd }, responseSize, rts); @@ -718,16 +754,15 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor private byte[] SendData(byte[] data, int responseSize = 0, bool rts = false) { - if (_serialPort == null) + lock (_serialPortSync) { - return null; - } + if (_serialPort == null) + { + return null; + } - byte[] buf = null; - - try - { - lock (_serialPort) + byte[] buf = null; + try { _serialPort.Open(); _serialPort.DiscardInBuffer(); @@ -751,13 +786,13 @@ public sealed class WireViewPro2 : Hardware, IPowerMonitor _serialPort.RtsEnable = false; } } - } - finally - { - _serialPort.Close(); - } + finally + { + _serialPort.Close(); + } - return buf; + return buf; + } } private byte[] ReadExact(int size) diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/DeviceData.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/DeviceData.cs index 4e97d21..b0921d4 100644 --- a/LibreHardwareMonitorLib/Interop/PowerMonitor/DeviceData.cs +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/DeviceData.cs @@ -41,5 +41,5 @@ public sealed class DeviceData public ushort FaultLog { get; set; } - public DeviceConfigStructV2 Config; + public DeviceConfigStructV3 Config; } diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs index ccb0096..920b2bd 100644 --- a/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/StructureConversion.cs @@ -1,32 +1,10 @@ namespace LibreHardwareMonitor.Interop.PowerMonitor; +using WVP2C = WireViewPro2Constants; + 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; - } + //1 <-> 2 public static DeviceConfigStructV2 ConvertConfigV1ToV2(DeviceConfigStructV1 configV1) { @@ -54,4 +32,127 @@ public static class StructureConversion }; return configV2; } + + 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; + } + + //1 <-> 3 + + public static DeviceConfigStructV3 ConvertConfigV1ToV3(DeviceConfigStructV1 configV1) + { + DeviceConfigStructV2 configV2 = ConvertConfigV1ToV2(configV1); + return ConvertConfigV2ToV3(configV2); + } + + public static DeviceConfigStructV1 ConvertConfigV3ToV1(DeviceConfigStructV3 configV3) + { + DeviceConfigStructV2 configV2 = ConvertConfigV3ToV2(configV3); + return ConvertConfigV2ToV1(configV2); + } + + //2 <-> 3 + + public static DeviceConfigStructV3 ConvertConfigV2ToV3(DeviceConfigStructV2 configV2) + { + DeviceConfigStructV3 configV3 = new DeviceConfigStructV3 + { + 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, + Average = configV2.Average, + Ui = new UiConfigStructV2 + { + DefaultScreen = Screen.ScreenMain, + CurrentScale = configV2.Ui.CurrentScale, + PowerScale = configV2.Ui.PowerScale, + DisplayRotation = configV2.Ui.DisplayRotation, + TimeoutMode = configV2.Ui.TimeoutMode, + CycleScreens = configV2.Ui.CycleScreens, + CycleTime = configV2.Ui.CycleTime, + Timeout = configV2.Ui.Timeout, + PrimaryColor = configV2.Ui.Theme == Theme.ThemeTg1 ? WVP2C.THEME_PRIMARY_COLOR_TG1 : configV2.Ui.Theme == Theme.ThemeTg2 ? WVP2C.THEME_PRIMARY_COLOR_TG2 : WVP2C.THEME_PRIMARY_COLOR_TG3, + SecondaryColor = configV2.Ui.Theme == Theme.ThemeTg1 ? WVP2C.THEME_SECONDARY_COLOR_TG1 : configV2.Ui.Theme == Theme.ThemeTg2 ? WVP2C.THEME_SECONDARY_COLOR_TG2 : WVP2C.THEME_SECONDARY_COLOR_TG3, + HighlightColor = configV2.Ui.Theme == Theme.ThemeTg1 ? WVP2C.THEME_HIGHLIGHT_COLOR_TG1 : configV2.Ui.Theme == Theme.ThemeTg2 ? WVP2C.THEME_HIGHLIGHT_COLOR_TG2 : WVP2C.THEME_HIGHLIGHT_COLOR_TG3, + BackgroundColor = configV2.Ui.Theme == Theme.ThemeTg1 ? WVP2C.THEME_BACKGROUND_COLOR_TG1 : configV2.Ui.Theme == Theme.ThemeTg2 ? WVP2C.THEME_BACKGROUND_COLOR_TG2 : WVP2C.THEME_BACKGROUND_COLOR_TG3, + BackgroundBitmapId = configV2.Ui.Theme == Theme.ThemeTg1 ? (byte)THEME_BACKGROUND.ThermalGrizzlyOrange : configV2.Ui.Theme == Theme.ThemeTg2 ? (byte)THEME_BACKGROUND.ThermalGrizzlyDark : (byte)THEME_BACKGROUND.Disabled, + FanBitmapId = configV2.Ui.Theme == Theme.ThemeTg1 ? (byte)THEME_FAN.ThermalGrizzlyOrange : configV2.Ui.Theme == Theme.ThemeTg2 ? (byte)THEME_FAN.ThermalGrizzlyDark : (byte)THEME_FAN.ThermalGrizzlyBlackWhite, + DisplayInversion = DISPLAY_INVERSION.DISPLAY_INVERSION_OFF // Default off + } + }; + return configV3; + } + + public static DeviceConfigStructV2 ConvertConfigV3ToV2(DeviceConfigStructV3 configV3) + { + DeviceConfigStructV2 configV2 = new DeviceConfigStructV2 + { + Crc = configV3.Crc, + Version = configV3.Version, + FriendlyName = configV3.FriendlyName, + FanConfig = configV3.FanConfig, + BacklightDuty = configV3.BacklightDuty, + FaultDisplayEnable = configV3.FaultDisplayEnable, + FaultBuzzerEnable = configV3.FaultBuzzerEnable, + FaultSoftPowerEnable = configV3.FaultSoftPowerEnable, + FaultHardPowerEnable = configV3.FaultHardPowerEnable, + TsFaultThreshold = configV3.TsFaultThreshold, + OcpFaultThreshold = configV3.OcpFaultThreshold, + WireOcpFaultThreshold = configV3.WireOcpFaultThreshold, + OppFaultThreshold = configV3.OppFaultThreshold, + CurrentImbalanceFaultThreshold = configV3.CurrentImbalanceFaultThreshold, + CurrentImbalanceFaultMinLoad = configV3.CurrentImbalanceFaultMinLoad, + ShutdownWaitTime = configV3.ShutdownWaitTime, + LoggingInterval = configV3.LoggingInterval, + Average = configV3.Average, + Ui = new UiConfigStructV1 + { + Theme = configV3.Ui.BackgroundBitmapId == (int)THEME_BACKGROUND.ThermalGrizzlyOrange ? Theme.ThemeTg1 : configV3.Ui.BackgroundBitmapId == (int)THEME_BACKGROUND.ThermalGrizzlyDark ? Theme.ThemeTg2 : Theme.ThemeTg3, // Infer theme from bitmap (best effort) + CurrentScale = configV3.Ui.CurrentScale, + PowerScale = configV3.Ui.PowerScale, + DisplayRotation = configV3.Ui.DisplayRotation, + TimeoutMode = configV3.Ui.TimeoutMode, + CycleScreens = configV3.Ui.CycleScreens, + CycleTime = configV3.Ui.CycleTime, + Timeout = configV3.Ui.Timeout + } + }; + return configV2; + } } diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Constants.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Constants.cs new file mode 100644 index 0000000..934ee68 --- /dev/null +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Constants.cs @@ -0,0 +1,20 @@ +namespace LibreHardwareMonitor.Interop.PowerMonitor +{ + internal class WireViewPro2Constants + { + public const uint THEME_PRIMARY_COLOR_TG1 = 0xFFFFFFFF; // White + public const uint THEME_SECONDARY_COLOR_TG1 = 0xFF646464; // Light gray + public const uint THEME_HIGHLIGHT_COLOR_TG1 = 0xFFE64121; // TG orange + public const uint THEME_BACKGROUND_COLOR_TG1 = 0xFF000000; // Black + + public const uint THEME_PRIMARY_COLOR_TG2 = 0xFFFFFFFF; // White + public const uint THEME_SECONDARY_COLOR_TG2 = 0xFF646464; // Light gray + public const uint THEME_HIGHLIGHT_COLOR_TG2 = 0xFFBEBEBE; // TG light gray + public const uint THEME_BACKGROUND_COLOR_TG2 = 0xFF000000; // Black + + public const uint THEME_PRIMARY_COLOR_TG3 = 0xFF969696; // Grey + public const uint THEME_SECONDARY_COLOR_TG3 = 0xFF505050; // Lighter gray + public const uint THEME_HIGHLIGHT_COLOR_TG3 = 0xFFFFFFFF; // White + public const uint THEME_BACKGROUND_COLOR_TG3 = 0xFF000000; // Black + } +} diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs index 0ed786a..4161044 100644 --- a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Enums.cs @@ -95,6 +95,15 @@ public enum TimeoutMode : byte TimeoutModeSleep = 2 } +public enum Screen : byte +{ + ScreenMain = 0, + ScreenSimple = 1, + ScreenCurrent = 2, + ScreenTemp = 3, + ScreenStatus = 4 +} + public enum NVM_CMD : byte { NVM_CMD_NONE, @@ -128,5 +137,25 @@ public enum AVG : byte AVG_354MS, AVG_709MS, AVG_1417MS, - AVG_NUM +} + +public enum DISPLAY_INVERSION : byte +{ + DISPLAY_INVERSION_OFF, + DISPLAY_INVERSION_ON, + DISPLAY_INVERSION_NUM +} + +public enum THEME_BACKGROUND : byte +{ + ThermalGrizzlyOrange = 1, + ThermalGrizzlyDark = 2, + Disabled = 255 +} + +public enum THEME_FAN : byte +{ + ThermalGrizzlyOrange = 0x64, // Bitmap 4 + 6 + ThermalGrizzlyDark = 0x75, // Bitmap 5 + 7 + ThermalGrizzlyBlackWhite = 0x98, // Bitmap 8 + 9 } diff --git a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs index 8fd3d9e..5118eb7 100644 --- a/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs +++ b/LibreHardwareMonitorLib/Interop/PowerMonitor/WireViewPro2Structures.cs @@ -56,7 +56,7 @@ public struct FanConfigStruct } [StructLayout(LayoutKind.Sequential, Pack = 4)] -public struct UiConfigStruct +public struct UiConfigStructV1 { public CurrentScale CurrentScale; public PowerScale PowerScale; @@ -68,6 +68,26 @@ public struct UiConfigStruct public byte Timeout; } +[StructLayout(LayoutKind.Sequential, Pack = 4)] +public struct UiConfigStructV2 +{ + public Screen DefaultScreen; + public CurrentScale CurrentScale; + public PowerScale PowerScale; + public DisplayRotation DisplayRotation; + public TimeoutMode TimeoutMode; + public byte CycleScreens; // bitmask of SCREEN_* + public byte CycleTime; // seconds + public byte Timeout; // seconds + public uint PrimaryColor; + public uint SecondaryColor; + public uint HighlightColor; + public uint BackgroundColor; + public byte BackgroundBitmapId; + public byte FanBitmapId; + public DISPLAY_INVERSION DisplayInversion; +} + [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)] public struct DeviceConfigStructV1 { @@ -92,7 +112,7 @@ public struct DeviceConfigStructV1 public byte CurrentImbalanceFaultMinLoad; // A public byte ShutdownWaitTime; // seconds public byte LoggingInterval; // seconds - public UiConfigStruct Ui; + public UiConfigStructV1 Ui; } [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)] @@ -120,5 +140,33 @@ public struct DeviceConfigStructV2 public byte ShutdownWaitTime; // seconds public byte LoggingInterval; // seconds public AVG Average; - public UiConfigStruct Ui; + public UiConfigStructV1 Ui; +} + +[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)] +public struct DeviceConfigStructV3 +{ + 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 UiConfigStructV2 Ui; }