# Modbus TCP 快速入门指南 ## 1. 配置设备连接 在 `PetWashControl/Services/ConfigurationService.cs` 中修改设备连接参数: ```csharp // Modbus TCP 配置 public string ModbusIpAddress { get; set; } = "192.168.1.10"; // 修改为实际设备IP public int ModbusPort { get; set; } = 502; // 默认502端口 public byte ModbusSlaveId { get; set; } = 1; // 从站ID,根据设备配置 ``` ## 2. 系统自动连接 系统启动时会自动连接 Modbus TCP 设备: ```csharp // MainViewModel.InitializeAsync() 中自动执行 var modbusConnected = await _modbusService.ConnectAsync(); ``` 连接状态会显示在界面上: - ✅ "系统就绪,设备已连接" - 连接成功 - ⚠️ "系统就绪,设备未连接" - 连接失败 ## 3. 使用 Modbus 服务 ### 在 MainViewModel 中访问服务 ```csharp // _modbusService 已在构造函数中初始化 private readonly ModbusService _modbusService; ``` ### 常用操作示例 #### 读取设备状态 ```csharp try { // 读取设备状态寄存器(地址0) var status = await _modbusService.ReadHoldingRegistersAsync(0, 1); _logger.LogInfo($"设备状态: {status[0]}"); } catch (Exception ex) { _logger.LogError("读取设备状态失败", ex); } ``` #### 控制设备门 ```csharp try { // 打开门(线圈地址0) await _modbusService.WriteSingleCoilAsync(0, true); IsDoorOpen = true; _logger.LogInfo("设备门已打开"); // 关闭门 await _modbusService.WriteSingleCoilAsync(0, false); IsDoorOpen = false; _logger.LogInfo("设备门已关闭"); } catch (Exception ex) { _logger.LogError("控制门失败", ex); } ``` #### 读取温度传感器 ```csharp try { // 读取温度传感器(输入寄存器100-101) var temps = await _modbusService.ReadInputRegistersAsync(100, 2); WaterTemperature = temps[0] / 10.0; // 假设数据需要除以10 RoomTemperature = temps[1] / 10.0; _logger.LogInfo($"水温: {WaterTemperature}°C, 室温: {RoomTemperature}°C"); } catch (Exception ex) { _logger.LogError("读取温度失败", ex); } ``` #### 启动洗护流程 ```csharp try { // 写入控制寄存器(地址200-201) ushort[] controlData = new ushort[] { 1, // 启动命令 30 // 运行时长(分钟) }; await _modbusService.WriteMultipleRegistersAsync(200, controlData); _logger.LogInfo("洗护流程已启动"); } catch (Exception ex) { _logger.LogError("启动洗护失败", ex); } ``` #### 读取液位传感器 ```csharp try { // 读取三个液位传感器(输入寄存器300-302) var levels = await _modbusService.ReadInputRegistersAsync(300, 3); Shampoo1Level = levels[0]; Shampoo2Level = levels[1]; Shampoo3Level = levels[2]; _logger.LogInfo($"液位: {Shampoo1Level}%, {Shampoo2Level}%, {Shampoo3Level}%"); } catch (Exception ex) { _logger.LogError("读取液位失败", ex); } ``` ## 4. 集成到现有流程 ### 在支付成功后打开门 修改 `SimulatePaymentAsync` 方法: ```csharp [RelayCommand] private async Task SimulatePaymentAsync() { if (CurrentOrder == null) return; try { StatusMessage = "正在处理支付..."; await Task.Delay(1500); // 确认支付 CurrentOrder = await _apiService.ConfirmPaymentAsync(CurrentOrder.Id); // 通过 Modbus 打开设备门 if (IsModbusConnected) { await _modbusService.WriteSingleCoilAsync(0, true); _logger.LogInfo("通过 Modbus 打开设备门"); } IsDoorOpen = true; StatusMessage = "支付成功!设备门已打开,请将宠物放入"; MessageBox.Show("支付成功!\n\n设备门已自动打开\n请将宠物放入设备后关闭门开始洗护", "支付成功", MessageBoxButton.OK, MessageBoxImage.Information); CurrentView = "Idle"; ViewChanged?.Invoke("Idle"); } catch (Exception ex) { _logger.LogError("支付失败", ex); StatusMessage = $"支付失败: {ex.Message}"; } } ``` ### 在关门时启动洗护 修改 `CloseDoorAsync` 方法: ```csharp [RelayCommand] private async Task CloseDoorAsync() { if (CurrentOrder == null || !IsDoorOpen) return; try { _logger.LogInfo($"关门,订单ID: {CurrentOrder.Id}"); // 通过 Modbus 关闭设备门 if (IsModbusConnected) { await _modbusService.WriteSingleCoilAsync(0, false); _logger.LogInfo("通过 Modbus 关闭设备门"); } // 通过MQTT发送关门状态 await _mqttService.PublishAsync("device/status", new { status = "door_closed", orderId = CurrentOrder.Id, timestamp = DateTime.Now }); CurrentOrder = await _apiService.UpdateOrderStatusAsync(CurrentOrder.Id, OrderStatus.DoorClosed); IsDoorOpen = false; StatusMessage = "门已关闭,清洗即将开始..."; // 通过 Modbus 启动洗护流程 if (IsModbusConnected) { int totalMinutes = CalculateTotalWashTime(); ushort[] controlData = new ushort[] { 1, (ushort)totalMinutes }; await _modbusService.WriteMultipleRegistersAsync(200, controlData); _logger.LogInfo($"通过 Modbus 启动洗护流程,时长: {totalMinutes}分钟"); } CurrentView = "Washing"; ViewChanged?.Invoke("Washing"); IsWashing = true; _ = SimulateWashingProcessAsync(); } catch (Exception ex) { _logger.LogError("关门失败", ex); StatusMessage = $"关门失败: {ex.Message}"; } } private int CalculateTotalWashTime() { return FirstSprayWaterTime + SprayShampoo1Time + AfterShampoo1SprayTime + SprayShampoo2Time + AfterShampoo2SprayTime + SprayShampoo3Time + AfterShampoo3SprayTime + HotAirTime + ColdAirTime + UvSterilizationTime; } ``` ### 在洗护过程中读取实时数据 修改 `SimulateWashingProcessAsync` 方法,添加实时数据读取: ```csharp private async Task SimulateWashingProcessAsync() { try { var steps = new[] { ("第一次冲水", FirstSprayWaterTime * 60), // ... 其他步骤 }; for (int stepIndex = 0; stepIndex < steps.Length; stepIndex++) { var (stepName, duration) = steps[stepIndex]; CurrentStep = stepName; for (int i = 0; i <= duration; i++) { if (!IsWashing) return; // 从 Modbus 读取实时温度 if (IsModbusConnected && i % 10 == 0) // 每10秒读取一次 { try { var temps = await _modbusService.ReadInputRegistersAsync(100, 2); WaterTemperature = temps[0] / 10.0; RoomTemperature = temps[1] / 10.0; } catch (Exception ex) { _logger.LogError("读取实时温度失败", ex); // 失败时使用模拟数据 UpdateTemperatures(stepName, i, duration); } } else { UpdateTemperatures(stepName, i, duration); } await Task.Delay(100); } } await CompleteWashingAsync(); } catch (Exception ex) { _logger.LogError("洗护流程失败", ex); } } ``` ## 5. 监控连接状态 系统会自动监控 Modbus 连接状态: ```csharp // 连接状态变化时自动触发 private void OnModbusConnectionStatusChanged(bool isConnected) { Application.Current.Dispatcher.Invoke(() => { IsModbusConnected = isConnected; if (isConnected) { _logger.LogInfo("Modbus TCP 设备已连接"); StatusMessage = "系统就绪,设备已连接"; } else { _logger.LogWarning("Modbus TCP 设备连接断开"); StatusMessage = "警告:设备连接断开"; } }); } ``` 在界面中绑定连接状态: ```xml ``` ## 6. 错误处理最佳实践 ```csharp // 始终使用 try-catch 包装 Modbus 操作 try { // 检查连接状态 if (!IsModbusConnected) { _logger.LogWarning("Modbus 未连接,跳过设备控制"); // 使用备用方案或提示用户 return; } // 执行 Modbus 操作 await _modbusService.WriteSingleCoilAsync(0, true); // 记录成功日志 _logger.LogInfo("Modbus 操作成功"); } catch (TimeoutException ex) { _logger.LogError("Modbus 操作超时", ex); StatusMessage = "设备响应超时,请检查连接"; } catch (InvalidOperationException ex) { _logger.LogError("Modbus 未连接", ex); StatusMessage = "设备未连接"; } catch (Exception ex) { _logger.LogError("Modbus 操作失败", ex); StatusMessage = $"设备控制失败: {ex.Message}"; } ``` ## 7. 测试步骤 ### 测试连接 1. 启动应用程序 2. 查看日志输出: ``` [INFO] 正在连接 Modbus TCP 设备: 192.168.1.10:502 [INFO] Modbus TCP 连接成功: 192.168.1.10:502 ``` 3. 界面显示 "系统就绪,设备已连接" ### 测试读取 ```csharp // 在 InitializeAsync 后添加测试代码 var testData = await _modbusService.ReadHoldingRegistersAsync(0, 1); _logger.LogInfo($"测试读取成功,值: {testData[0]}"); ``` ### 测试写入 ```csharp // 测试写入寄存器 await _modbusService.WriteSingleRegisterAsync(0, 100); var verify = await _modbusService.ReadHoldingRegistersAsync(0, 1); _logger.LogInfo($"测试写入成功,验证值: {verify[0]}"); ``` ## 8. 常见问题 ### Q: 连接失败怎么办? A: 检查以下几点: 1. 设备 IP 地址是否正确 2. 网络是否连通(ping 测试) 3. 设备是否启用 Modbus TCP 服务 4. 防火墙是否阻止 502 端口 ### Q: 读写操作失败? A: 确认: 1. 寄存器地址是否正确 2. 从站 ID 是否匹配 3. 数据格式是否符合设备要求 4. 查看详细错误日志 ### Q: 如何调试 Modbus 通信? A: 1. 查看日志文件中的详细信息 2. 使用 Modbus 测试工具(如 Modbus Poll)验证设备 3. 使用网络抓包工具(如 Wireshark)分析通信数据 ## 9. 下一步 - 根据实际设备的 Modbus 地址映射表修改代码 - 实现完整的设备控制逻辑 - 添加更多的错误处理和用户提示 - 进行完整的集成测试 详细文档请参考:`MODBUS_INTEGRATION.md`