package com.mes.service; import com.fasterxml.jackson.core.type.TypeReference; 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.device.service.DeviceConfigService; import com.mes.device.util.ConfigJsonHelper; import com.mes.service.PlcDynamicDataService; import com.mes.s7.enhanced.EnhancedS7Serializer; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * PLC测试写入服务 * * 基于DeviceConfig的新API,用于模拟PLC行为进行测试 * * 推荐使用:DevicePlcOperationService(生产环境) * * @author huang * @date 2025/10/29 */ @Slf4j @Service public class PlcTestWriteService { @Resource private DeviceConfigService deviceConfigService; @Resource private PlcDynamicDataService plcDynamicDataService; private final ObjectMapper objectMapper = new ObjectMapper(); private static final TypeReference> MAP_TYPE = new TypeReference>() {}; private static final int ON = 1; private static final int OFF = 0; // 缓存不同设备的S7Serializer实例 private final ConcurrentMap serializerCache = new ConcurrentHashMap<>(); // ==================== 基于DeviceConfig的新API(推荐使用) ==================== /** * 根据设备ID模拟PLC发送请求字 * * @param deviceId 设备ID * @return 是否成功 */ public boolean simulatePlcRequestByDevice(Long deviceId) { DeviceConfig device = deviceConfigService.getDeviceById(deviceId); if (device == null) { log.error("设备不存在: deviceId={}", deviceId); return false; } try { EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); if (s7Serializer == null) { log.error("获取S7Serializer失败: deviceId={}", deviceId); return false; } // 使用PlcDynamicDataService读取数据(支持addressMapping) Map currentData = plcDynamicDataService.readAllPlcData(device, s7Serializer); if (currentData == null || currentData.isEmpty()) { log.error("读取PLC数据失败,返回空: deviceId={}", deviceId); return false; } // 检查联机状态 Object onlineStateObj = currentData.get("onlineState"); Integer onlineState = null; if (onlineStateObj != null) { if (onlineStateObj instanceof Number) { onlineState = ((Number) onlineStateObj).intValue(); } else { try { String strValue = String.valueOf(onlineStateObj); if (!strValue.isEmpty() && !"null".equalsIgnoreCase(strValue)) { onlineState = Integer.parseInt(strValue); } } catch (NumberFormatException e) { log.warn("解析onlineState失败: deviceId={}, value={}", deviceId, onlineStateObj, e); } } } if (onlineState != null && onlineState == OFF) { log.info("当前PLC联机模式为0,停止联机: deviceId={}", deviceId); return false; } // 检查汇报字,如果为1则重置为0 Object plcReportObj = currentData.get("plcReport"); Integer plcReport = null; if (plcReportObj != null) { if (plcReportObj instanceof Number) { plcReport = ((Number) plcReportObj).intValue(); } else { try { String strValue = String.valueOf(plcReportObj); if (!strValue.isEmpty() && !"null".equalsIgnoreCase(strValue)) { plcReport = Integer.parseInt(strValue); } } catch (NumberFormatException e) { log.warn("解析plcReport失败: deviceId={}, value={}", deviceId, plcReportObj, e); } } } if (plcReport != null && plcReport == ON) { log.info("当前上片PLC汇报字为1,重置为0: deviceId={}", deviceId); currentData.put("plcReport", OFF); } // 设置请求字为1 currentData.put("plcRequest", ON); // 使用PlcDynamicDataService写入数据 plcDynamicDataService.writePlcData(device, currentData, s7Serializer); log.info("模拟PLC发送请求字成功:plcRequest=1, deviceId={}", deviceId); return true; } catch (Exception e) { log.error("根据设备模拟PLC请求字失败: deviceId={}", deviceId, e); return false; } } /** * 根据设备ID模拟PLC任务完成汇报 * * @param deviceId 设备ID * @return 是否成功 */ public boolean simulatePlcReportByDevice(Long deviceId) { DeviceConfig device = deviceConfigService.getDeviceById(deviceId); if (device == null) { log.error("设备不存在: deviceId={}", deviceId); return false; } try { EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); if (s7Serializer == null) { log.error("获取S7Serializer失败: deviceId={}", deviceId); return false; } // 使用PlcDynamicDataService读取数据 Map currentData = plcDynamicDataService.readAllPlcData(device, s7Serializer); if (currentData == null || currentData.isEmpty()) { log.error("读取PLC数据失败,返回空: deviceId={}", deviceId); return false; } // 设置汇报字为1,请求字清0 currentData.put("plcReport", ON); currentData.put("plcRequest", OFF); currentData.put("mesGlassCount", 10); // 使用PlcDynamicDataService写入数据 plcDynamicDataService.writePlcData(device, currentData, s7Serializer); log.info("模拟PLC任务完成汇报:plcReport=1, mesGlassCount=10, deviceId={}", deviceId); return true; } catch (Exception e) { log.error("根据设备模拟PLC汇报失败: deviceId={}", deviceId, e); return false; } } /** * 根据设备ID重置PLC所有状态 * * @param deviceId 设备ID * @return 是否成功 */ public boolean resetPlcByDevice(Long deviceId) { DeviceConfig device = deviceConfigService.getDeviceById(deviceId); if (device == null) { log.error("设备不存在: deviceId={}", deviceId); return false; } try { EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); if (s7Serializer == null) { log.error("获取S7Serializer失败: deviceId={}", deviceId); return false; } // 构建重置数据 Map resetData = new HashMap<>(); resetData.put("plcRequest", OFF); resetData.put("plcReport", OFF); resetData.put("mesSend", OFF); resetData.put("mesConfirm", OFF); resetData.put("onlineState", ON); resetData.put("mesGlassCount", 0); resetData.put("alarmInfo", OFF); // 使用PlcDynamicDataService写入数据 plcDynamicDataService.writePlcData(device, resetData, s7Serializer); log.info("PLC状态已重置, deviceId={}", deviceId); return true; } catch (Exception e) { log.error("根据设备重置PLC状态失败: deviceId={}", deviceId, e); return false; } } /** * 根据设备ID读取PLC当前状态 * * @param deviceId 设备ID * @return PLC状态数据 */ public Map readPlcStatusByDevice(Long deviceId) { DeviceConfig device = deviceConfigService.getDeviceById(deviceId); if (device == null) { log.error("设备不存在: deviceId={}", deviceId); return null; } try { EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); if (s7Serializer == null) { log.error("获取S7Serializer失败: deviceId={}", deviceId); return null; } // 使用PlcDynamicDataService读取所有数据(支持addressMapping) Map data = plcDynamicDataService.readAllPlcData(device, s7Serializer); return data; } catch (Exception e) { log.error("读取设备PLC状态失败: deviceId={}", deviceId, e); return null; } } /** * 根据设备ID写入PLC字段 * * @param deviceId 设备ID * @param fieldValues 字段名->值 的Map * @return 是否成功 */ public boolean writeFieldsByDevice(Long deviceId, Map fieldValues) { DeviceConfig device = deviceConfigService.getDeviceById(deviceId); if (device == null) { log.error("设备不存在: deviceId={}", deviceId); return false; } try { // 获取对应的S7Serializer(使用设备配置) EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); if (s7Serializer == null) { log.error("获取S7Serializer失败: deviceId={}", deviceId); return false; } // 使用动态数据服务写入字段(基于DeviceConfig) plcDynamicDataService.writePlcData(device, fieldValues, s7Serializer); log.info("写入PLC字段成功: deviceId={}, fields={}", deviceId, fieldValues.keySet()); return true; } catch (Exception e) { log.error("写入PLC字段失败: deviceId={}", deviceId, e); return false; } } /** * 获取设备对应的S7Serializer实例 * * @param device 设备配置 * @return S7Serializer实例 */ private EnhancedS7Serializer getSerializerForDevice(DeviceConfig device) { if (device == null) { log.error("设备配置为空,无法创建S7Serializer"); return null; } try { String cacheKey; if (device.getId() != null) { cacheKey = "device:" + device.getId(); } else { try { cacheKey = "device:" + resolveProjectId(device); } catch (Exception e) { cacheKey = "device:" + (device.getDeviceCode() != null ? device.getDeviceCode() : "unknown"); } } return serializerCache.computeIfAbsent(cacheKey, id -> { try { // 解析PLC类型(仅取实体字段) EPlcType plcType = EPlcType.S1200; String plcTypeValue = device.getPlcType(); if (plcTypeValue == null || plcTypeValue.isEmpty()) { log.warn("设备未配置PLC类型,使用默认类型S1200, deviceId={}", device.getId()); } else { try { plcType = EPlcType.valueOf(plcTypeValue); } catch (IllegalArgumentException e) { log.warn("未知的PLC类型: {}, 使用默认类型 S1200", plcTypeValue); } } // 创建S7PLC实例(仅取实体字段) String plcIp = device.getPlcIp(); if (plcIp == null || plcIp.isEmpty()) { log.warn("设备未配置PLC IP,使用默认 192.168.10.21, deviceId={}", device.getId()); plcIp = "192.168.10.21"; } S7PLC s7Plc = new S7PLC(plcType, plcIp); // 创建并返回EnhancedS7Serializer实例 EnhancedS7Serializer serializer = EnhancedS7Serializer.newInstance(s7Plc); if (serializer == null) { log.error("创建EnhancedS7Serializer失败: deviceId={}, plcIp={}, plcType={}", device.getId(), plcIp, plcType); } return serializer; } catch (Exception e) { log.error("创建S7Serializer异常: deviceId={}", device.getId(), e); return null; } }); } catch (Exception e) { log.error("获取S7Serializer失败: deviceId={}", device.getId(), e); return null; } } private Map parseExtraParams(DeviceConfig device) { if (device.getExtraParams() == null || device.getExtraParams().trim().isEmpty()) { return Collections.emptyMap(); } try { return objectMapper.readValue(device.getExtraParams(), MAP_TYPE); } catch (Exception e) { log.warn("解析设备extraParams失败, deviceId={}", device.getId(), e); return Collections.emptyMap(); } } @SuppressWarnings("unchecked") private Map getPlcConfigParams(DeviceConfig device) { Map extraParams = parseExtraParams(device); Object plcConfig = extraParams.get("plcConfig"); if (plcConfig instanceof Map) { return (Map) plcConfig; } if (plcConfig instanceof String) { try { return objectMapper.readValue((String) plcConfig, MAP_TYPE); } catch (Exception e) { log.warn("解析extraParams.plcConfig失败, deviceId={}", device.getId(), e); } } return Collections.emptyMap(); } private int parseInteger(Object value) { if (value instanceof Number) { return ((Number) value).intValue(); } try { return Integer.parseInt(String.valueOf(value)); } catch (NumberFormatException ex) { log.warn("无法解析整型值: {}", value); return 0; } } /** * 从设备配置中解析项目标识 * * @param device 设备配置 * @return 项目标识 */ private String resolveProjectId(DeviceConfig device) { if (device == null) { throw new IllegalArgumentException("设备信息为空"); } // 1. 优先使用实体上的projectId if (device.getProjectId() != null) { return String.valueOf(device.getProjectId()); } // 2. 从extraParams中读取 Map extraParams = parseExtraParams(device); Object plcProjectId = extraParams.get("plcProjectId"); if (plcProjectId != null) { return String.valueOf(plcProjectId); } // 3. 兼容旧结构:configJson或extraParams内嵌 Map configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); Object legacyProjectId = configParams.get("plcProjectId"); if (legacyProjectId != null) { return String.valueOf(legacyProjectId); } // 最后使用设备编号 if (device.getDeviceCode() != null && !device.getDeviceCode().isEmpty()) { return device.getDeviceCode(); } throw new IllegalStateException("无法解析设备的PLC项目标识, deviceId=" + device.getId()); } }