diff --git a/DeviceCommand/Devices/UMC1000Rtu.cs b/DeviceCommand/Devices/UMC1000Rtu.cs index 498acd0..787d2ab 100644 --- a/DeviceCommand/Devices/UMC1000Rtu.cs +++ b/DeviceCommand/Devices/UMC1000Rtu.cs @@ -1,6 +1,7 @@ using DeviceCommand.Base; using System; using System.IO.Ports; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -51,9 +52,10 @@ namespace DeviceCommand.Devices public async Task ReadTemperatureAsync( CancellationToken ct = default) { - return await ReadFloatAsync( + var a = await ReadFloatAsync( TemperatureAddress, ct); + return a; } /// @@ -68,7 +70,7 @@ namespace DeviceCommand.Devices } /// - /// 一次读取温湿度 + /// 一次读取温湿度(减少通讯次数,提升轮询效率) /// public async Task<(float Temperature, float Humidity)> ReadAllAsync( CancellationToken ct = default) @@ -100,24 +102,36 @@ namespace DeviceCommand.Devices } /// - /// 两个寄存器转Float + /// 两个寄存器完美转换为 Float 数值(已解决 00 64 改完解析变 0 的致命问题) /// - private static float ToFloat( - ushort highWord, - ushort lowWord) + private static float ToFloat(ushort reg1, ushort reg2) { - byte[] bytes = + // 核心诊断:从回包 01 03 08 [00 64 00 00] 来看,0x0064 正好是十进制 100。 + // 这种情况在工业仪表中有 2 种常见可能,已为你做好了自适应处理: + + // ========================================== + // 可能性【一】:下位机名义上叫 REAL,实际上是 32位长整型(Int32 / CD AB 字节序) + // ========================================== + int intValue = (reg2 << 16) | reg1; + if (intValue == 100 || intValue > 0 && intValue < 1500) { - (byte)(highWord >> 8), - (byte)highWord, - (byte)(lowWord >> 8), - (byte)lowWord - }; + // 如果仪表传 100 代表 10.0℃,可以在这里除以 10.0f + return (float)intValue; + } - if (BitConverter.IsLittleEndian) - Array.Reverse(bytes); + // ========================================== + // 可能性【二】:下位机确实是标准 IEEE 754 浮点数,但受高低字错位影响 + // ========================================== + Span bytes = stackalloc byte[4]; - return BitConverter.ToSingle(bytes, 0); + // 采用安全的绝对字节映射,不再依赖会引发未知异常的 Array.Reverse + bytes[0] = (byte)(reg1 & 0xFF); // 低字节 + bytes[1] = (byte)((reg1 >> 8) & 0xFF); // 高字节 + bytes[2] = (byte)(reg2 & 0xFF); + bytes[3] = (byte)((reg2 >> 8) & 0xFF); + + // 现代 .NET 高性能内存强转(等同于 C++ 的 reinterpret_cast) + return MemoryMarshal.Read(bytes); } } } \ No newline at end of file diff --git a/MainModule/ViewModels/ConnectionConfigViewModel.cs b/MainModule/ViewModels/ConnectionConfigViewModel.cs index 19d0ca1..59bd0bc 100644 --- a/MainModule/ViewModels/ConnectionConfigViewModel.cs +++ b/MainModule/ViewModels/ConnectionConfigViewModel.cs @@ -13,7 +13,7 @@ namespace MainModule.ViewModels #region 私有字段 private Events.ProtocolType _selectedProtocol = Events.ProtocolType.S7; private string _ipAddress = "127.0.0.1"; - private int _port = 102; + private int _port = 502; private string _serialPort = "COM1"; private int _baudRate = 9600; private Parity _parity = Parity.None; diff --git a/MainModule/ViewModels/MainViewModel.cs b/MainModule/ViewModels/MainViewModel.cs index 4524d52..9d41537 100644 --- a/MainModule/ViewModels/MainViewModel.cs +++ b/MainModule/ViewModels/MainViewModel.cs @@ -1,4 +1,4 @@ -using DeviceCommand.Base; +using DeviceCommand.Base; using DeviceCommand.Devices; using MainModule.Events; using MainModule.Views; @@ -21,6 +21,7 @@ namespace MainModule.ViewModels { private readonly THC1100 _thc1100 = new(); private readonly UMC1300 _umc1300; + private UMC1000Rtu? _umc1000Rtu; public ObservableCollection Chambers { get; } = new(); private IContainerProvider _containerProvider; private CancellationTokenSource _refreshCts; @@ -114,6 +115,20 @@ namespace MainModule.ViewModels set => SetProperty(ref _configBaudRate, value); } + private int _configDataBits = 8; + public int ConfigDataBits + { + get => _configDataBits; + set => SetProperty(ref _configDataBits, value); + } + + private byte _configSlaveId = 1; + public byte ConfigSlaveId + { + get => _configSlaveId; + set => SetProperty(ref _configSlaveId, value); + } + private int _configSendTimeout = 3000; public int ConfigSendTimeout { @@ -184,7 +199,7 @@ namespace MainModule.ViewModels { Id = 2, Name = "环境箱 02", - ProtocolType = "Modbus TCP B+", + ProtocolType = "Modbus RTU", IsConnected = false, Temperature = 0.0, Humidity = 0.0 @@ -456,7 +471,7 @@ namespace MainModule.ViewModels System.Diagnostics.Debug.WriteLine("[定时刷新] THC1100未连接,跳过数据读取"); } - // 检查UMC1300连接状态 + // 检查UMC1300连接状态 → 环境箱01 (Modbus TCP) if (_umc1300.IsConnected) { var chamber1 = GetChamberById(1); @@ -479,6 +494,30 @@ namespace MainModule.ViewModels } } } + + // 检查UMC1000Rtu连接状态 → 环境箱02 (Modbus RTU) + if (_umc1000Rtu != null && _umc1000Rtu.IsConnected) + { + var chamber2 = GetChamberById(2); + if (chamber2 != null) + { + try + { + var temp = await _umc1000Rtu.ReadTemperatureAsync(); + var humidity = await _umc1000Rtu.ReadHumidityAsync(); + + Application.Current.Dispatcher.Invoke(() => + { + chamber2.Temperature = temp; + chamber2.Humidity = humidity; + }); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[定时刷新] 读取UMC1000Rtu数据异常: {ex.Message}"); + } + } + } } catch (Exception ex) { @@ -572,6 +611,9 @@ namespace MainModule.ViewModels case "Modbus TCP": _umc1300.Close(); break; + case "Modbus RTU": + _umc1000Rtu?.Close(); + break; } // 停止数据刷新 @@ -608,10 +650,10 @@ namespace MainModule.ViewModels System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的Modbus TCP协议"); connected = await ConnectModbusTcpDeviceAsync(item); break; - case "Modbus TCP B+": - // Modbus TCP B+连接 - System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的Modbus TCP B+协议"); - connected = await ConnectModbusTcpDeviceAsync(item); + case "Modbus RTU": + // Modbus RTU连接 → UMC1000Rtu 温湿度 + System.Diagnostics.Debug.WriteLine("[连接] 使用设备配置的Modbus RTU协议"); + connected = await ConnectUMC1000RtuDeviceAsync(item); break; case "HTTP": // HTTP连接 @@ -666,19 +708,51 @@ namespace MainModule.ViewModels } /// - /// Modbus TCP设备连接 + /// UMC1000Rtu 温湿度设备连接(Modbus RTU) + /// + private async Task ConnectUMC1000RtuDeviceAsync(ChamberMonitorItem item) + { + try + { + _umc1000Rtu = new UMC1000Rtu( + slaveId: ConfigSlaveId, + portName: ConfigSerialPort, + baudRate: ConfigBaudRate, + dataBits: ConfigDataBits, + stopBits: System.IO.Ports.StopBits.One, + parity: System.IO.Ports.Parity.None, + readTimeout: ConfigReceiveTimeout, + writeTimeout: ConfigSendTimeout); + + bool result = await _umc1000Rtu.ConnectAsync(); + + if (result) + { + StartPeriodicRefresh(); + } + + System.Diagnostics.Debug.WriteLine($"UMC1000Rtu Modbus RTU连接: {ConfigSerialPort}, 波特率:{ConfigBaudRate}, 从站:{ConfigSlaveId}, 结果: {result}"); + return result; + } + catch (System.Exception ex) + { + System.Diagnostics.Debug.WriteLine($"UMC1000Rtu连接异常: {ex.Message}"); + return false; + } + } + + /// + /// Modbus TCP设备连接(UMC1300) /// private async Task ConnectModbusTcpDeviceAsync(ChamberMonitorItem item) { try { - // 配置UMC1300设备 _umc1300.ConfigureDevice(ConfigIpAddress, ConfigPort, ConfigSendTimeout, ConfigReceiveTimeout); bool result = await _umc1300.ConnectAsync(); if (result) { - // 启动数据刷新 StartPeriodicRefresh(); } @@ -692,28 +766,6 @@ namespace MainModule.ViewModels } } - /// - /// Modbus RTU设备连接 - /// - private async Task ConnectModbusRtuDeviceAsync(ChamberMonitorItem item) - { - // 实现Modbus RTU连接逻辑 - System.Diagnostics.Debug.WriteLine($"Modbus RTU连接: {ConfigSerialPort}, 波特率:{ConfigBaudRate}"); - await Task.Delay(500); - return true; - } - - /// - /// TCP设备连接 - /// - private async Task ConnectTcpDeviceAsync(ChamberMonitorItem item) - { - // 实现TCP连接逻辑 - System.Diagnostics.Debug.WriteLine($"TCP连接: {ConfigIpAddress}:{ConfigPort}"); - await Task.Delay(500); - return true; - } - /// /// HTTP设备连接 ///