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;
}
}