# 多 PLC 协议支持 ## 概述 YZWater3 基于 HslCommunication 库实现了多 PLC 协议支持,通过统一的 `IPlcDriver` 接口抽象,可以轻松切换不同品牌的 PLC。 ## 支持的协议 | 协议 | 品牌 | 默认端口 | 说明 | |------|------|----------|------| | **Siemens S7** | 西门子 | 102 | S200/S200Smart/S300/S400/S1200/S1500 | | **Mitsubishi MC** | 三菱 | 6000 | Q 系列、iQ-R/iQ-F | | **Omron Fins** | 欧姆龙 | 9600 | CJ/CS/CP 系列 | | **Modbus TCP** | 通用 | 502 | 仪表、传感器、其他 PLC | | **Allen-Bradley** | AB | 44818 | ControlLogix/CompactLogix | ## 使用方法 ### 方式一:通过工厂创建 ```csharp using YZWater.Core.Services.PlcDrivers; // 创建西门子 S7 驱动 var driver = PlcDriverFactory.Create( PlcProtocol.SiemensS7, ipAddress: "192.168.1.100", port: 102, extraConfig: new Dictionary { ["PlcSeries"] = "S200Smart" } ); // 连接 var result = await driver.ConnectAsync(); if (!result.IsSuccess) { Console.WriteLine($"连接失败: {result.Message}"); return; } // 读取数据 var floatResult = await driver.ReadFloatAsync("VD100"); if (floatResult.IsSuccess) { float value = floatResult.Content; Console.WriteLine($"VD100 = {value}"); } // 写入数据 await driver.WriteFloatAsync("VD100", 123.45f); // 断开 driver.Disconnect(); driver.Dispose(); ``` ### 方式二:直接创建驱动 ```csharp // 西门子 S7 var siemens = new SiemensS7Driver(SiemensPLCS.S1200); siemens.SetConnection("192.168.1.100", 102); // 三菱 MC var mitsubishi = new MitsubishiMcDriver(); mitsubishi.SetConnection("192.168.1.101", 6000); // 欧姆龙 Fins var omron = new OmronFinsDriver(); omron.SetConnection("192.168.1.102", 9600); omron.SetNodeNumbers(sa1: 0, da1: 0); // 设置节点号 // Modbus TCP var modbus = new ModbusTcpDriver(); modbus.SetConnection("192.168.1.103", 502, station: 1); modbus.SetAddressStart(true); // 地址从 0 开始 // AB EtherNet/IP var ab = new AllenBradleyDriver(); ab.SetConnection("192.168.1.104", 44818); ``` ## 配置示例 ### yzwater-config.json ```json { "PlcProtocol": "SiemensS7", "PlcIp": "192.168.1.100", "PlcPort": 102, "PlcExtraConfig": { "PlcSeries": "S200Smart" } } ``` ### 不同协议的配置 **西门子 S7:** ```json { "PlcProtocol": "SiemensS7", "PlcIp": "192.168.1.100", "PlcPort": 102, "PlcExtraConfig": { "PlcSeries": "S1200" } } ``` **三菱 MC:** ```json { "PlcProtocol": "MitsubishiMC", "PlcIp": "192.168.1.101", "PlcPort": 6000 } ``` **欧姆龙 Fins:** ```json { "PlcProtocol": "OmronFins", "PlcIp": "192.168.1.102", "PlcPort": 9600, "PlcExtraConfig": { "SA1": 0, "DA1": 0 } } ``` **Modbus TCP:** ```json { "PlcProtocol": "ModbusTcp", "PlcIp": "192.168.1.103", "PlcPort": 502, "PlcExtraConfig": { "Station": 1, "AddressStartWithZero": true } } ``` **Allen-Bradley:** ```json { "PlcProtocol": "AllenBradley", "PlcIp": "192.168.1.104", "PlcPort": 44818 } ``` ## 地址格式 ### 西门子 S7 | 区域 | 地址格式 | 说明 | |------|----------|------| | I | `I0.0` | 输入位 | | Q | `Q0.0` | 输出位 | | M | `M0.0` | 中间继电器位 | | DB | `DB1.0` | 数据块字节 | | V/VD | `VD100` | 变量区(S200Smart) | ### 三菱 MC | 区域 | 地址格式 | 说明 | |------|----------|------| | D | `D100` | 数据寄存器 | | R | `R100` | 文件寄存器 | | W | `W100` | 链接寄存器 | | X | `X0` | 输入 | | Y | `Y0` | 输出 | | M | `M0` | 内部继电器 | ### 欧姆龙 Fins | 区域 | 地址格式 | 说明 | |------|----------|------| | D | `D100` | DM 区 | | C | `C100` | HR 区 | | W | `W100` | WR 区 | | H | `H100` | 保持位 | ### Modbus TCP | 区域 | 地址格式 | 说明 | |------|----------|------| | 0x | `0x100` | 线圈(000001-099999) | | 1x | `1x100` | 离散输入(100001-199999) | | 3x | `3x100` | 输入寄存器(300001-399999) | | 4x | `4x100` | 保持寄存器(400001-499999) | ### Allen-Bradley | 类型 | 地址格式 | 说明 | |------|----------|------| | Tag | `TagName` | 标签名 | | 数组 | `Tag[0]` | 数组元素 | | 结构体 | `Tag.Member` | 结构体成员 | ## 扩展新协议 实现 `IPlcDriver` 接口: ```csharp public class MyCustomDriver : IPlcDriver { public string Name => "My Custom PLC"; public bool IsConnected { get; private set; } public Task ConnectAsync() { // 实现连接逻辑 } public void Disconnect() { // 实现断开逻辑 } public Task> ReadBytesAsync(string address, ushort length) { // 实现读取逻辑 } // 实现其他接口方法... } ``` 然后在 `PlcDriverFactory` 中添加: ```csharp public static IPlcDriver Create(PlcProtocol protocol, ...) { IPlcDriver driver = protocol switch { PlcProtocol.MyCustom => new MyCustomDriver(...), // ... }; return driver; } ``` ## 注意事项 1. **线程安全**:所有驱动实例都是线程安全的 2. **连接管理**:使用完毕后必须调用 `Dispose()` 释放资源 3. **错误处理**:检查 `OperateResult.IsSuccess` 判断操作是否成功 4. **地址格式**:不同 PLC 的地址格式不同,请参考对应的文档 5. **性能优化**:批量读取时使用 `ReadBytesAsync` 而非多次读取单个值 ## 性能对比 | 协议 | 读取延迟 | 吞吐量 | 说明 | |------|----------|--------|------| | S7 | 10-50ms | 高 | 优化良好 | | MC | 10-50ms | 高 | 优化良好 | | Fins | 20-80ms | 中 | 相对较慢 | | Modbus | 50-200ms | 低 | 通用协议,开销大 | | AB | 20-100ms | 中 | 取决于标签复杂度 | ## 故障排查 ### 连接失败 1. 检查 IP 和端口是否正确 2. 检查网络连通性:`ping ` 3. 检查 PLC 是否支持该协议 4. 查看日志:`Logs/log-*.txt` ### 读取失败 1. 检查地址格式是否正确 2. 检查地址是否超出 PLC 范围 3. 检查数据类型是否匹配 4. 查看 HslCommunication 的返回信息 ### 性能问题 1. 使用批量读取而非单个读取 2. 减少轮询频率 3. 考虑使用 PLC 的主动推送(如 OPC UA) ## 参考资料 - [HslCommunication 文档](https://github.com/dathlin/HslCommunication) - [西门子 S7 协议](https://support.industry.siemens.com/) - [三菱 MC 协议](https://www.mitsubishielectric.com/fa/) - [Modbus 协议](https://modbus.org/)