package com.mes.service.impl; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.xingshuangs.iot.common.enums.EDataType; import com.github.xingshuangs.iot.protocol.s7.serializer.S7Parameter; import com.mes.device.entity.DeviceConfig; import com.mes.device.util.ConfigJsonHelper; import com.mes.entity.PlcAddress; import com.mes.s7.enhanced.EnhancedS7Serializer; import com.mes.service.PlcDynamicDataService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * PLC动态数据读写服务实现 * 通过PlcAddress中的addressMapping配置动态读写任意字段组合 * * @author huang * @date 2025/11/05 */ @Slf4j @Service public class PlcDynamicDataServiceImpl implements PlcDynamicDataService { private final ObjectMapper objectMapper = new ObjectMapper(); private static final TypeReference> MAP_TYPE = new TypeReference>() {}; /** * 根据PlcAddress配置和字段名称读取PLC数据 * * @param config PLC地址映射配置 * @param fieldNames 要读取的字段名称列表 * @param s7Serializer S7序列化器 * @return 字段名->值 的Map */ @Override public Map readPlcData(PlcAddress config, List fieldNames, EnhancedS7Serializer s7Serializer) { if (config == null || config.getAddressMapping() == null) { throw new IllegalArgumentException("PlcAddress配置或addressMapping不能为空"); } try { // 解析addressMapping JSON配置 JSONObject addressMapping = JSONObject.parseObject(config.getAddressMapping()); // 构建S7Parameter列表 List parameters = buildS7Parameters(config, addressMapping, fieldNames); // 从PLC读取数据 List results = s7Serializer.read(parameters); // 将结果转换为Map Map resultMap = new HashMap<>(); for (int i = 0; i < fieldNames.size() && i < results.size(); i++) { String fieldName = fieldNames.get(i); Object value = results.get(i).getValue(); resultMap.put(fieldName, value); } return resultMap; } catch (Exception e) { log.error("读取PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,详细错误: {}", config.getPlcIp(), e.getMessage(), e); return new HashMap<>(); } } /** * 根据PlcAddress配置和数据Map写入PLC * * @param config PLC地址映射配置 * @param dataMap 字段名->值 的Map * @param s7Serializer S7序列化器 */ @Override public void writePlcData(PlcAddress config, Map dataMap, EnhancedS7Serializer s7Serializer) { if (config == null || config.getAddressMapping() == null) { throw new IllegalArgumentException("PlcAddress配置或addressMapping不能为空"); } try { // 解析addressMapping JSON配置 JSONObject addressMapping = JSONObject.parseObject(config.getAddressMapping()); // 构建S7Parameter列表,并填充值 List parameters = buildS7ParametersWithValues(config, addressMapping, dataMap); // 写入PLC s7Serializer.write(parameters); } catch (Exception e) { log.error("写入PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,详细错误: {}", config.getPlcIp(), e.getMessage(), e); } } /** * 读取PLC所有字段 * * @param config PLC地址映射配置 * @param s7Serializer S7序列化器 * @return 所有字段的值 */ @Override public Map readAllPlcData(PlcAddress config, EnhancedS7Serializer s7Serializer) { if (config == null || config.getAddressMapping() == null) { throw new IllegalArgumentException("PlcAddress配置或addressMapping不能为空"); } // 获取所有字段名 JSONObject addressMapping = JSONObject.parseObject(config.getAddressMapping()); List allFields = new ArrayList<>(addressMapping.keySet()); // 读取所有字段 return readPlcData(config, allFields, s7Serializer); } /** * 读取单个字段 * * @param config PLC地址映射配置 * @param fieldName 字段名 * @param s7Serializer S7序列化器 * @return 字段值 */ @Override public Object readPlcField(PlcAddress config, String fieldName, EnhancedS7Serializer s7Serializer) { List fields = new ArrayList<>(); fields.add(fieldName); Map result = readPlcData(config, fields, s7Serializer); return result.get(fieldName); } /** * 写入单个字段 * * @param config PLC地址映射配置 * @param fieldName 字段名 * @param value 字段值 * @param s7Serializer S7序列化器 */ @Override public void writePlcField(PlcAddress config, String fieldName, Object value, EnhancedS7Serializer s7Serializer) { Map dataMap = new HashMap<>(); dataMap.put(fieldName, value); writePlcData(config, dataMap, s7Serializer); } /** * 构建S7Parameter列表(不包含值) * * @param config PLC地址配置 * @param addressMapping 地址映射 * @param fieldNames 字段名列表 * @return S7Parameter列表 */ private List buildS7Parameters(PlcAddress config, JSONObject addressMapping, List fieldNames) { List parameters = new ArrayList<>(); for (String fieldName : fieldNames) { if (!addressMapping.containsKey(fieldName)) { log.warn("字段 {} 在addressMapping中不存在,跳过", fieldName); continue; } // 获取字段的偏移地址 int offset = addressMapping.getInteger(fieldName); // 构建完整地址:dbArea + offset(如:DB12.2) String fullAddress = config.getDbArea() + "." + offset; // 创建S7Parameter,默认使用UINT16类型(16位无符号整数) S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); parameters.add(parameter); } return parameters; } /** * 构建S7Parameter列表(包含值) * * @param config PLC地址配置 * @param addressMapping 地址映射 * @param dataMap 字段名->值 的Map * @return S7Parameter列表 */ private List buildS7ParametersWithValues(PlcAddress config, JSONObject addressMapping, Map dataMap) { List parameters = new ArrayList<>(); for (Map.Entry entry : dataMap.entrySet()) { String fieldName = entry.getKey(); Object value = entry.getValue(); if (!addressMapping.containsKey(fieldName)) { log.warn("字段 {} 在addressMapping中不存在,跳过", fieldName); continue; } // 获取字段的偏移地址 int offset = addressMapping.getInteger(fieldName); // 构建完整地址 String fullAddress = config.getDbArea() + "." + offset; // 创建S7Parameter,设置值 S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); parameter.setValue(value); parameters.add(parameter); } return parameters; } /** * 从DeviceConfig中提取地址映射配置 * * @param device 设备配置 * @return 地址映射JSON字符串 */ private String extractAddressMapping(DeviceConfig device) { // configJson 现在仅存放字段地址映射(数组形式) Map configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); if (!configParams.isEmpty()) { try { return objectMapper.writeValueAsString(configParams); } catch (Exception e) { log.warn("序列化configJson地址映射失败, deviceId={}", device.getId(), e); } } // 其次从extraParams中获取(兼容旧结构) Map extraParams = parseExtraParams(device); Object addressMapping = extraParams.get("addressMapping"); if (addressMapping != null) { if (addressMapping instanceof String) { return (String) addressMapping; } else { try { return objectMapper.writeValueAsString(addressMapping); } catch (Exception e) { log.warn("序列化extraParams.addressMapping失败, deviceId={}", device.getId(), e); } } } throw new IllegalArgumentException("设备配置中未找到addressMapping, deviceId=" + device.getId()); } /** * 从DeviceConfig中提取dbArea * * @param device 设备配置 * @return dbArea */ private String extractDbArea(DeviceConfig device) { // 从extraParams.plcConfig中获取(新结构) Map plcConfig = getPlcConfig(device); Object dbArea = plcConfig.get("dbArea"); if (dbArea != null) { return String.valueOf(dbArea); } // 兼容旧结构:extraParams根节点 Map extraParams = parseExtraParams(device); Object legacyDbArea = extraParams.get("dbArea"); if (legacyDbArea != null) { return String.valueOf(legacyDbArea); } // 默认值 return "DB12"; } /** * 从DeviceConfig中提取beginIndex * * @param device 设备配置 * @return beginIndex */ private int extractBeginIndex(DeviceConfig device) { // 从extraParams.plcConfig中获取 Map plcConfig = getPlcConfig(device); Object beginIndex = plcConfig.get("beginIndex"); if (beginIndex != null) { return parseInteger(beginIndex); } // 兼容旧结构:extraParams根节点 Map extraParams = parseExtraParams(device); Object legacyBeginIndex = extraParams.get("beginIndex"); if (legacyBeginIndex != null) { return parseInteger(legacyBeginIndex); } // 默认值 return 0; } 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 getPlcConfig(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; } } @Override public Map readPlcData(DeviceConfig device, List fieldNames, EnhancedS7Serializer s7Serializer) { if (device == null) { throw new IllegalArgumentException("设备配置不能为空"); } String addressMapping = extractAddressMapping(device); if (addressMapping == null || addressMapping.isEmpty()) { throw new IllegalArgumentException("设备配置中addressMapping不能为空"); } try { // 解析addressMapping JSON配置 JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); // 构建S7Parameter列表 String dbArea = extractDbArea(device); List parameters = buildS7ParametersForDevice(device, dbArea, addressMappingObj, fieldNames); // 从PLC读取数据 List results = s7Serializer.read(parameters); // 将结果转换为Map Map resultMap = new HashMap<>(); for (int i = 0; i < fieldNames.size() && i < results.size(); i++) { String fieldName = fieldNames.get(i); Object value = results.get(i).getValue(); resultMap.put(fieldName, value); } return resultMap; } catch (Exception e) { log.error("读取PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,deviceId: {}, 详细错误: {}", device.getPlcIp(), device.getId(), e.getMessage(), e); return new HashMap<>(); } } @Override public void writePlcData(DeviceConfig device, Map dataMap, EnhancedS7Serializer s7Serializer) { if (device == null) { throw new IllegalArgumentException("设备配置不能为空"); } String addressMapping = extractAddressMapping(device); if (addressMapping == null || addressMapping.isEmpty()) { throw new IllegalArgumentException("设备配置中addressMapping不能为空"); } try { // 解析addressMapping JSON配置 JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); // 构建S7Parameter列表,并填充值 String dbArea = extractDbArea(device); List parameters = buildS7ParametersWithValuesForDevice(device, dbArea, addressMappingObj, dataMap); // 写入PLC s7Serializer.write(parameters); } catch (Exception e) { log.error("写入PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,deviceId: {}, 详细错误: {}", device.getPlcIp(), device.getId(), e.getMessage(), e); } } @Override public Map readAllPlcData(DeviceConfig device, EnhancedS7Serializer s7Serializer) { if (device == null) { throw new IllegalArgumentException("设备配置不能为空"); } String addressMapping = extractAddressMapping(device); if (addressMapping == null || addressMapping.isEmpty()) { throw new IllegalArgumentException("设备配置中addressMapping不能为空"); } // 获取所有字段名 JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); List allFields = new ArrayList<>(addressMappingObj.keySet()); // 读取所有字段 return readPlcData(device, allFields, s7Serializer); } @Override public Object readPlcField(DeviceConfig device, String fieldName, EnhancedS7Serializer s7Serializer) { List fields = new ArrayList<>(); fields.add(fieldName); Map result = readPlcData(device, fields, s7Serializer); return result.get(fieldName); } @Override public void writePlcField(DeviceConfig device, String fieldName, Object value, EnhancedS7Serializer s7Serializer) { Map dataMap = new HashMap<>(); dataMap.put(fieldName, value); writePlcData(device, dataMap, s7Serializer); } /** * 构建S7Parameter列表(不包含值)- 基于DeviceConfig */ private List buildS7ParametersForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, List fieldNames) { List parameters = new ArrayList<>(); for (String fieldName : fieldNames) { if (!addressMapping.containsKey(fieldName)) { log.warn("字段 {} 在addressMapping中不存在,跳过", fieldName); continue; } // 获取字段的偏移地址 int offset = addressMapping.getInteger(fieldName); // 构建完整地址:dbArea + offset(如:DB12.2) String fullAddress = dbArea + "." + offset; // 创建S7Parameter,默认使用UINT16类型(16位无符号整数) S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); parameters.add(parameter); } return parameters; } /** * 构建S7Parameter列表(包含值)- 基于DeviceConfig */ private List buildS7ParametersWithValuesForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, Map dataMap) { List parameters = new ArrayList<>(); for (Map.Entry entry : dataMap.entrySet()) { String fieldName = entry.getKey(); Object value = entry.getValue(); if (!addressMapping.containsKey(fieldName)) { log.warn("字段 {} 在addressMapping中不存在,跳过", fieldName); continue; } // 获取字段的偏移地址 int offset = addressMapping.getInteger(fieldName); // 构建完整地址 String fullAddress = dbArea + "." + offset; // 创建S7Parameter,设置值 S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); parameter.setValue(value); parameters.add(parameter); } return parameters; } }