package com.mes.plc.client.impl; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType; import com.github.xingshuangs.iot.protocol.s7.service.S7PLC; import com.mes.device.entity.DeviceConfig; import com.mes.plc.client.PlcClient; import com.mes.service.PlcDynamicDataService; import com.mes.s7.enhanced.EnhancedS7Serializer; import lombok.extern.slf4j.Slf4j; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * S7协议PLC客户端实现 *

* 基于现有的S7库实现PlcClient接口 *

* * @author huang * @date 2025/12/19 */ @Slf4j public class S7PlcClient implements PlcClient { // PLC类型 private final EPlcType plcType; // PLC IP地址 private final String plcIp; // PLC端口 private final int plcPort; // 设备配置 private final DeviceConfig device; // 模块名称 private final String moduleName; // S7 PLC实例 private S7PLC s7Plc; // S7序列化器 private EnhancedS7Serializer s7Serializer; // 连接状态 private boolean connected = false; // 超时时间(毫秒) private int timeout = 5000; // PLC类型映射 private static final Map PLC_TYPE_MAP = new ConcurrentHashMap<>(); // PLC动态数据服务 private PlcDynamicDataService plcDynamicDataService; /** * 设置PLC动态数据服务 * * @param plcDynamicDataService PLC动态数据服务实例 */ public void setPlcDynamicDataService(PlcDynamicDataService plcDynamicDataService) { this.plcDynamicDataService = plcDynamicDataService; } static { // 初始化PLC类型映射(支持多种格式) // S7_1200 格式 PLC_TYPE_MAP.put("S7_1200", EPlcType.S1200); PLC_TYPE_MAP.put("S7_1500", EPlcType.S1500); PLC_TYPE_MAP.put("S7_200", EPlcType.S200); PLC_TYPE_MAP.put("S7_300", EPlcType.S300); PLC_TYPE_MAP.put("S7_400", EPlcType.S400); // S1200 格式(不带下划线) PLC_TYPE_MAP.put("S1200", EPlcType.S1200); PLC_TYPE_MAP.put("S1500", EPlcType.S1500); PLC_TYPE_MAP.put("S200", EPlcType.S200); PLC_TYPE_MAP.put("S300", EPlcType.S300); PLC_TYPE_MAP.put("S400", EPlcType.S400); } /** * 构造函数 * * @param device 设备配置 * @param s7Serializer S7序列化器 */ public S7PlcClient(DeviceConfig device, EnhancedS7Serializer s7Serializer) { this.device = device; this.plcIp = device.getPlcIp(); this.plcPort = device.getPlcPort() != null ? device.getPlcPort() : 102; this.moduleName = device.getModuleName() != null ? device.getModuleName() : "DB1"; this.s7Serializer = s7Serializer; // 解析PLC类型 String plcTypeValue = device.getPlcType(); this.plcType = PLC_TYPE_MAP.getOrDefault(plcTypeValue, EPlcType.S1200); } @Override public boolean connect() { try { if (s7Plc != null && isConnected()) { return true; } // 创建S7PLC实例 this.s7Plc = new S7PLC(this.plcType, this.plcIp, this.plcPort); // 连接PLC this.s7Plc.connect(); this.connected = true; log.info("S7 PLC连接成功: {}:{},类型: {}", this.plcIp, this.plcPort, this.plcType); return true; } catch (Exception e) { log.error("S7 PLC连接失败: {}:{}", this.plcIp, this.plcPort, e); this.connected = false; return false; } } @Override public void disconnect() { try { if (this.s7Plc != null) { log.info("S7 PLC断开连接: {}:{}", this.plcIp, this.plcPort); } } catch (Exception e) { log.error("S7 PLC断开连接失败: {}:{}", this.plcIp, this.plcPort, e); } finally { this.connected = false; this.s7Plc = null; } } @Override public Map readAllData() { if (!isConnected() && !connect()) { log.error("S7 PLC未连接,无法读取数据: {}:{}", this.plcIp, this.plcPort); return Collections.emptyMap(); } try { if (this.plcDynamicDataService == null) { log.error("PLC动态数据服务未初始化,无法读取数据: {}:{}", this.plcIp, this.plcPort); return Collections.emptyMap(); } // 使用PlcDynamicDataService读取所有PLC数据 Map data = this.plcDynamicDataService.readAllPlcData(this.device, this.s7Serializer); if (data == null) { log.warn("PLC动态数据服务返回空数据: {}:{}", this.plcIp, this.plcPort); return new HashMap<>(); } log.info("S7 PLC读取所有数据成功: {}:{}, dataSize={}", this.plcIp, this.plcPort, data.size()); return data; } catch (Exception e) { log.error("S7 PLC读取所有数据失败: {}:{}", this.plcIp, this.plcPort, e); this.connected = false; return Collections.emptyMap(); } } @Override public Map readData(String... fields) { if (!isConnected() && !connect()) { log.error("S7 PLC未连接,无法读取数据: {}:{}", this.plcIp, this.plcPort); return Collections.emptyMap(); } try { if (this.plcDynamicDataService == null) { log.error("PLC动态数据服务未初始化,无法读取数据: {}:{}", this.plcIp, this.plcPort); return Collections.emptyMap(); } if (fields == null || fields.length == 0) { return readAllData(); } // 使用PlcDynamicDataService读取指定字段PLC数据 Map data = this.plcDynamicDataService.readPlcData(this.device, Arrays.asList(fields), this.s7Serializer); if (data == null) { log.warn("PLC动态数据服务返回空数据: {}:{}", this.plcIp, this.plcPort); return new HashMap<>(); } log.info("S7 PLC读取指定字段数据成功: {}:{}, fields={}", this.plcIp, this.plcPort, Arrays.toString(fields)); return data; } catch (Exception e) { log.error("S7 PLC读取数据失败: {}:{}", this.plcIp, this.plcPort, e); this.connected = false; return Collections.emptyMap(); } } @Override public boolean writeData(Map data) { if (!isConnected() && !connect()) { log.error("S7 PLC未连接,无法写入数据: {}:{}", this.plcIp, this.plcPort); return false; } try { if (this.plcDynamicDataService == null) { log.error("PLC动态数据服务未初始化,无法写入数据: {}:{}", this.plcIp, this.plcPort); return false; } if (data == null || data.isEmpty()) { log.warn("写入数据为空,跳过操作: {}:{}", this.plcIp, this.plcPort); return true; } // 使用PlcDynamicDataService写入PLC数据 this.plcDynamicDataService.writePlcData(this.device, data, this.s7Serializer); log.info("S7 PLC写入数据成功: {}:{}, fields={}", this.plcIp, this.plcPort, data.keySet()); return true; } catch (Exception e) { log.error("S7 PLC写入数据失败: {}:{}", this.plcIp, this.plcPort, e); this.connected = false; return false; } } @Override public boolean isConnected() { try { // S7PLC不支持直接检查连接状态,使用内部标志 return this.connected && this.s7Plc != null; } catch (Exception e) { this.connected = false; return false; } } @Override public String getPlcType() { return this.plcType.name(); } @Override public int getTimeout() { return this.timeout; } @Override public void setTimeout(int timeout) { this.timeout = timeout; } }