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.entity.PlcBaseData; import com.mes.entity.PlcAddress; 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.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * PLC测试写入服务 * 模拟PLC行为,向PLC写入测试数据,用于测试MES程序 * * @author huang * @date 2025/10/29 */ @Slf4j @Service public class PlcTestWriteService { @Resource private PlcAddressService plcAddressService; @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; // 当前使用的项目标识 private String currentProjectId = "vertical"; // 缓存不同项目的S7Serializer实例 private final ConcurrentMap serializerCache = new ConcurrentHashMap<>(); /** * 模拟PLC发送请求字(触发MES任务下发) */ public boolean simulatePlcRequest() { return simulatePlcRequest(currentProjectId); } /** * 模拟PLC发送请求字(触发MES任务下发)- 支持指定项目 */ public boolean simulatePlcRequest(String projectId) { try { PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); if (config == null) { log.error("项目配置不存在: projectId={}", projectId); return false; } EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); if (s7Serializer == null) { log.error("无法创建S7Serializer: projectId={}", projectId); return false; } return simulatePlcRequestInternal(projectId, config, s7Serializer); } catch (Exception e) { log.error("模拟PLC请求字失败", e); return false; } } private boolean simulatePlcRequestInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); if (currentData == null) { log.error("读取PLC数据失败,返回null: projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); return false; } if (currentData.getOnlineState() == OFF) { log.info("当前PLC联机模式为0,停止联机"); return false; } else if (currentData.getPlcReport() == ON) { log.info("当前上片PLC汇报字为1,重置为0"); currentData.setPlcReport(OFF); } currentData.setPlcRequest(ON); s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); log.info("模拟PLC发送请求字成功:plcRequest=1, projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); return true; } /** * 模拟PLC任务完成汇报 */ public boolean simulatePlcReport() { return simulatePlcReport(currentProjectId); } /** * 模拟PLC任务完成汇报 - 支持指定项目 */ public boolean simulatePlcReport(String projectId) { try { // 获取项目配置(数据库实体) PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); if (config == null) { log.error("项目配置不存在: projectId={}", projectId); return false; } // 获取对应的S7Serializer EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); if (s7Serializer == null) { log.error("无法创建S7Serializer: projectId={}", projectId); return false; } return simulatePlcReportInternal(projectId, config, s7Serializer); } catch (Exception e) { log.error("模拟PLC任务完成汇报失败", e); return false; } } private boolean simulatePlcReportInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); if (currentData == null) { log.error("读取PLC数据失败,返回null: projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); return false; } currentData.setPlcReport(ON); currentData.setPlcRequest(OFF); currentData.setMesGlassCount(10); s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); log.info("模拟PLC任务完成汇报:plcReport=1, mesGlassCount=10, projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); return true; } /** * 模拟PLC发送联机状态 */ public boolean simulateOnlineStatus(int onlineState) { return simulateOnlineStatus(onlineState, currentProjectId); } /** * 模拟PLC发送联机状态 - 支持指定项目 */ public boolean simulateOnlineStatus(int onlineState, String projectId) { try { // 获取项目配置(数据库实体) PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); if (config == null) { log.error("项目配置不存在: projectId={}", projectId); return false; } // 获取对应的S7Serializer EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); if (s7Serializer == null) { log.error("无法创建S7Serializer: projectId={}", projectId); return false; } return simulateOnlineStatusInternal(onlineState, projectId, config, s7Serializer); } catch (Exception e) { log.error("模拟PLC联机状态失败", e); return false; } } private boolean simulateOnlineStatusInternal(int onlineState, String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { PlcBaseData currentData = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); if (currentData == null) { log.error("读取PLC数据失败,返回null: projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); return false; } currentData.setOnlineState(onlineState); s7Serializer.write(currentData, config.getDbArea(), config.getBeginIndex()); log.info("模拟PLC联机状态:onlineState={}, projectId={}, dbArea={}, beginIndex={}", onlineState, projectId, config.getDbArea(), config.getBeginIndex()); return true; } /** * 重置PLC所有状态 */ public boolean resetPlc() { return resetPlc(currentProjectId); } /** * 重置PLC所有状态 - 支持指定项目 */ public boolean resetPlc(String projectId) { try { // 获取项目配置(数据库实体) PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); if (config == null) { log.error("项目配置不存在: projectId={}", projectId); return false; } EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); if (s7Serializer == null) { log.error("无法创建S7Serializer: projectId={}", projectId); return false; } return resetPlcInternal(projectId, config, s7Serializer); } catch (Exception e) { log.error("重置PLC状态失败", e); return false; } } private boolean resetPlcInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { PlcBaseData resetData = new PlcBaseData(); resetData.setPlcRequest(OFF); resetData.setPlcReport(OFF); resetData.setMesSend(OFF); resetData.setMesConfirm(OFF); resetData.setOnlineState(ON); resetData.setMesGlassCount(0); resetData.setAlarmInfo(OFF); s7Serializer.write(resetData, config.getDbArea(), config.getBeginIndex()); log.info("PLC状态已重置, projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); return true; } /** * 读取PLC当前状态 */ public PlcBaseData readPlcStatus() { return readPlcStatus(currentProjectId); } /** * 读取PLC当前状态 - 支持指定项目 */ public PlcBaseData readPlcStatus(String projectId) { try { // 获取项目配置(数据库实体) PlcAddress config = plcAddressService.getProjectConfigWithMapping(projectId); if (config == null) { log.error("项目配置不存在: projectId={}", projectId); return null; } EnhancedS7Serializer s7Serializer = getSerializerForProject(projectId, config); if (s7Serializer == null) { log.error("无法创建S7Serializer: projectId={}", projectId); return null; } return readPlcStatusInternal(projectId, config, s7Serializer); } catch (Exception e) { log.error("读取PLC状态失败", e); return null; } } private PlcBaseData readPlcStatusInternal(String projectId, PlcAddress config, EnhancedS7Serializer s7Serializer) throws Exception { PlcBaseData data = s7Serializer.read(PlcBaseData.class, config.getDbArea(), config.getBeginIndex()); if (data == null) { log.error("读取PLC状态返回null: projectId={}, dbArea={}, beginIndex={}", projectId, config.getDbArea(), config.getBeginIndex()); } return data; } /** * 设置当前项目标识 */ public void setCurrentProjectId(String projectId) { this.currentProjectId = projectId; } /** * 获取当前项目标识 */ public String getCurrentProjectId() { return this.currentProjectId; } /** * 获取项目对应的S7Serializer实例 * 如果不存在,则创建一个新的实例并缓存 * * @param projectId 项目标识 * @param config 项目配置 * @return S7Serializer实例 */ private EnhancedS7Serializer getSerializerForProject(String projectId, PlcAddress config) { return serializerCache.computeIfAbsent(projectId, id -> { // 解析PLC类型 EPlcType plcType = EPlcType.S1200; // 默认值 if (config != null && config.getPlcType() != null) { try { plcType = EPlcType.valueOf(config.getPlcType()); } catch (IllegalArgumentException e) { log.warn("未知的PLC类型: {}, 使用默认类型 S1200", config.getPlcType()); } } // 创建S7PLC实例 String plcIp = (config != null && config.getPlcIp() != null) ? config.getPlcIp() : "192.168.10.21"; S7PLC s7Plc = new S7PLC(plcType, plcIp); // 创建并返回EnhancedS7Serializer实例 return EnhancedS7Serializer.newInstance(s7Plc); }); } /** * 清除指定项目的S7Serializer缓存 * * @param projectId 项目标识 */ public void clearSerializerCache(String projectId) { serializerCache.remove(projectId); log.info("已清除项目 {} 的S7Serializer缓存", projectId); } /** * 清除所有S7Serializer缓存 */ public void clearAllSerializerCache() { serializerCache.clear(); log.info("已清除所有S7Serializer缓存"); } /** * 根据设备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 { String projectId = resolveProjectId(device); PlcAddress config = buildPlcAddressFromDevice(device); EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); return simulatePlcRequestInternal(projectId, config, s7Serializer); } 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 { String projectId = resolveProjectId(device); PlcAddress config = buildPlcAddressFromDevice(device); EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); return simulatePlcReportInternal(projectId, config, s7Serializer); } 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 { String projectId = resolveProjectId(device); PlcAddress config = buildPlcAddressFromDevice(device); EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); return resetPlcInternal(projectId, config, s7Serializer); } 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 { String projectId = resolveProjectId(device); PlcAddress config = buildPlcAddressFromDevice(device); EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); PlcBaseData data = readPlcStatusInternal(projectId, config, s7Serializer); if (data == null) { return null; } String json = objectMapper.writeValueAsString(data); return objectMapper.readValue(json, MAP_TYPE); } 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 { // 从设备配置中获取项目标识 String projectId = resolveProjectId(device); // 获取对应的S7Serializer(使用设备配置) EnhancedS7Serializer s7Serializer = getSerializerForDevice(device); // 使用动态数据服务写入字段(基于DeviceConfig) plcDynamicDataService.writePlcData(device, fieldValues, s7Serializer); log.info("写入PLC字段成功: deviceId={}, projectId={}, fields={}", deviceId, projectId, 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) { String cacheKey = "device:" + (device.getId() != null ? device.getId() : resolveProjectId(device)); return serializerCache.computeIfAbsent(cacheKey, id -> { // 解析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实例 return EnhancedS7Serializer.newInstance(s7Plc); }); } private PlcAddress buildPlcAddressFromDevice(DeviceConfig device) { Map plcConfig = getPlcConfigParams(device); String dbArea = plcConfig.get("dbArea") != null ? String.valueOf(plcConfig.get("dbArea")) : "DB12"; int beginIndex = plcConfig.get("beginIndex") != null ? parseInteger(plcConfig.get("beginIndex")) : 0; 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"; } String plcType = device.getPlcType(); if (plcType == null || plcType.isEmpty()) { log.warn("设备未配置PLC类型,使用默认S1200, deviceId={}", device.getId()); plcType = EPlcType.S1200.name(); } String addressMapping = resolveAddressMapping(device); PlcAddress config = new PlcAddress(); config.setProjectId(resolveProjectId(device)); config.setDbArea(dbArea); config.setBeginIndex(beginIndex); config.setPlcIp(plcIp); config.setPlcType(plcType); config.setAddressMapping(addressMapping); return config; } private String resolveAddressMapping(DeviceConfig device) { Map mapping = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); if (!mapping.isEmpty()) { try { return objectMapper.writeValueAsString(mapping); } catch (Exception e) { log.warn("序列化configJson字段映射失败, deviceId={}", device.getId(), e); } } Map extraParams = parseExtraParams(device); Object addressMapping = extraParams.get("addressMapping"); if (addressMapping instanceof String) { return (String) addressMapping; } if (addressMapping != null) { try { return objectMapper.writeValueAsString(addressMapping); } catch (Exception e) { log.warn("序列化extraParams.addressMapping失败, deviceId={}", device.getId(), e); } } throw new IllegalStateException("设备未配置PLC字段映射, deviceId=" + device.getId()); } 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()); } }