huang
16 小时以前 9c489617b002e71859597097c9d1d2f1b9fc0e56
mes-processes/mes-plcSend/src/main/java/com/mes/plc/client/impl/ModbusPlcClient.java
@@ -1,14 +1,15 @@
package com.mes.plc.client.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mes.connect.modbus.ModbusTcpClient;
import com.mes.device.entity.DeviceConfig;
import com.mes.device.util.ConfigJsonHelper;
import com.mes.plc.client.PlcClient;
import lombok.extern.slf4j.Slf4j;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.io.IOException;
import java.util.*;
/**
 * Modbus协议PLC客户端实现
@@ -21,34 +22,34 @@
 */
@Slf4j
public class ModbusPlcClient implements PlcClient {
    // PLC IP地址
    private final String plcIp;
    // PLC端口
    private final int plcPort;
    // 从站地址
    private final int unitId;
    // 设备配置
    private final DeviceConfig device;
    // Modbus客户端实例
    private ModbusTcpClient modbusClient;
    // 连接状态
    private boolean connected = false;
    // 超时时间(毫秒)
    private int timeout = 5000;
    // ObjectMapper用于JSON解析
    private final ObjectMapper objectMapper = new ObjectMapper();
    // 地址映射缓存:字段名 -> Modbus地址
    private Map<String, String> addressMappingCache;
    /**
     * 构造函数
     *
@@ -58,7 +59,7 @@
        this.device = device;
        this.plcIp = device.getPlcIp();
        this.plcPort = device.getPlcPort() != null ? device.getPlcPort() : 502;
        // 从配置中获取从站地址,默认1
        int unitIdValue = 1;
        try {
@@ -75,8 +76,11 @@
            log.warn("解析unitId失败,使用默认值1: deviceId={}", device.getId(), e);
        }
        this.unitId = unitIdValue;
        // 初始化地址映射
        this.addressMappingCache = loadAddressMapping();
    }
    /**
     * 解析设备的extraParams
     *
@@ -87,27 +91,124 @@
        if (extraParamsJson == null || extraParamsJson.isEmpty()) {
            return new HashMap<>();
        }
        try {
            // 这里简化处理,实际项目中应该使用Jackson或Gson解析
            // 由于项目中已有ObjectMapper,这里暂时返回空Map,后续完善
            return new HashMap<>();
            TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
            return objectMapper.readValue(extraParamsJson, typeRef);
        } catch (Exception e) {
            log.error("解析extraParams失败: {}", extraParamsJson, e);
            return null;
            return new HashMap<>();
        }
    }
    /**
     * 加载地址映射配置(从configJson或extraParams.addressMapping)
     *
     * @return 字段名 -> Modbus地址的映射
     */
    private Map<String, String> loadAddressMapping() {
        Map<String, String> mapping = new HashMap<>();
        try {
            // 1. 优先从configJson获取
            Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper);
            if (!configParams.isEmpty()) {
                for (Map.Entry<String, Object> entry : configParams.entrySet()) {
                    String fieldName = entry.getKey();
                    Object addressObj = entry.getValue();
                    if (addressObj != null) {
                        mapping.put(fieldName, String.valueOf(addressObj));
                    }
                }
                if (!mapping.isEmpty()) {
                    log.info("从configJson加载Modbus地址映射成功: deviceId={}, count={}", device.getId(), mapping.size());
                    return mapping;
                }
            }
            // 2. 从extraParams.addressMapping获取
            Map<String, Object> extraParams = parseExtraParams(device.getExtraParams());
            Object addressMappingObj = extraParams.get("addressMapping");
            if (addressMappingObj != null) {
                if (addressMappingObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> addrMap = (Map<String, Object>) addressMappingObj;
                    for (Map.Entry<String, Object> entry : addrMap.entrySet()) {
                        mapping.put(entry.getKey(), String.valueOf(entry.getValue()));
                    }
                } else if (addressMappingObj instanceof String) {
                    // 如果是JSON字符串,解析它
                    TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
                    Map<String, Object> addrMap = objectMapper.readValue((String) addressMappingObj, typeRef);
                    for (Map.Entry<String, Object> entry : addrMap.entrySet()) {
                        mapping.put(entry.getKey(), String.valueOf(entry.getValue()));
                    }
                }
                if (!mapping.isEmpty()) {
                    log.info("从extraParams.addressMapping加载Modbus地址映射成功: deviceId={}, count={}", device.getId(), mapping.size());
                    return mapping;
                }
            }
            log.warn("未找到Modbus地址映射配置: deviceId={}", device.getId());
        } catch (Exception e) {
            log.error("加载Modbus地址映射失败: deviceId={}", device.getId(), e);
        }
        return mapping;
    }
    /**
     * 获取字段对应的Modbus地址
     *
     * @param fieldName 字段名
     * @return Modbus地址(格式:功能码.寄存器地址,如 "3.40001")
     */
    private String getModbusAddress(String fieldName) {
        String address = addressMappingCache.get(fieldName);
        if (address == null || address.isEmpty()) {
            log.warn("字段 {} 未找到Modbus地址映射: deviceId={}", fieldName, device.getId());
            return null;
        }
        return address;
    }
    /**
     * 推断数据类型(根据字段名或值)
     */
    private String inferDataType(String fieldName, Object value) {
        if (value == null) {
            // 根据字段名推断
            String lowerName = fieldName.toLowerCase();
            if (lowerName.contains("float") || lowerName.contains("real")) {
                return "float";
            } else if (lowerName.contains("string") || lowerName.contains("str") || lowerName.contains("id")) {
                return "string";
            }
            return "int"; // 默认int
        }
        // 根据值类型推断
        if (value instanceof Float || value instanceof Double) {
            return "float";
        } else if (value instanceof String) {
            return "string";
        } else if (value instanceof Number) {
            return "int";
        }
        return "int";
    }
    @Override
    public boolean connect() {
        try {
            if (modbusClient != null && isConnected()) {
                return true;
            }
            // 创建Modbus客户端实例
            this.modbusClient = new ModbusTcpClient(this.plcIp, this.plcPort, this.unitId);
            // 连接PLC
            this.modbusClient.connect();
            this.connected = true;
@@ -119,7 +220,7 @@
            return false;
        }
    }
    @Override
    public void disconnect() {
        try {
@@ -134,46 +235,125 @@
            this.modbusClient = null;
        }
    }
    @Override
    public Map<String, Object> readAllData() {
        if (!isConnected() && !connect()) {
            log.error("Modbus PLC未连接,无法读取数据: {}:{}", this.plcIp, this.plcPort);
            return Collections.emptyMap();
        }
        try {
            // TODO: 实现Modbus读取所有数据
            // 这里暂时返回空Map,后续完善
            log.warn("Modbus readAllData未实现,返回空Map");
            return new HashMap<>();
            if (addressMappingCache.isEmpty()) {
                log.warn("Modbus地址映射为空,无法读取所有数据: deviceId={}", device.getId());
                return Collections.emptyMap();
            }
            // 读取所有配置的字段
            Map<String, Object> result = new HashMap<>();
            for (String fieldName : addressMappingCache.keySet()) {
                try {
                    Object value = readFieldValue(fieldName);
                    if (value != null) {
                        result.put(fieldName, value);
                    }
                } catch (Exception e) {
                    log.warn("读取字段失败: fieldName={}, deviceId={}, error={}", fieldName, device.getId(), e.getMessage());
                }
            }
            log.info("Modbus读取所有数据成功: deviceId={}, fieldCount={}", device.getId(), result.size());
            return result;
        } catch (Exception e) {
            log.error("Modbus PLC读取所有数据失败: {}:{}", this.plcIp, this.plcPort, e);
            this.connected = false;
            return Collections.emptyMap();
        }
    }
    @Override
    public Map<String, Object> readData(String... fields) {
        if (!isConnected() && !connect()) {
            log.error("Modbus PLC未连接,无法读取数据: {}:{}", this.plcIp, this.plcPort);
            return Collections.emptyMap();
        }
        if (fields == null || fields.length == 0) {
            return readAllData();
        }
        try {
            // TODO: 实现Modbus读取指定字段数据
            // 这里暂时返回空Map,后续完善
            log.warn("Modbus readData未实现,返回空Map");
            return new HashMap<>();
            Map<String, Object> result = new HashMap<>();
            for (String fieldName : fields) {
                if (fieldName == null || fieldName.isEmpty()) {
                    continue;
                }
                try {
                    Object value = readFieldValue(fieldName);
                    if (value != null) {
                        result.put(fieldName, value);
                    }
                } catch (Exception e) {
                    log.warn("读取字段失败: fieldName={}, deviceId={}, error={}", fieldName, device.getId(), e.getMessage());
                }
            }
            log.info("Modbus读取指定字段数据成功: deviceId={}, fields={}, resultCount={}",
                    device.getId(), Arrays.toString(fields), result.size());
            return result;
        } catch (Exception e) {
            log.error("Modbus PLC读取数据失败: {}:{}", this.plcIp, this.plcPort, e);
            this.connected = false;
            return Collections.emptyMap();
        }
    }
    /**
     * 读取单个字段的值
     */
    private Object readFieldValue(String fieldName) throws IOException {
        String address = getModbusAddress(fieldName);
        if (address == null) {
            return null;
        }
        // 根据字段名推断数据类型(优先从配置中获取)
        String dataType = inferDataType(fieldName, null);
        // 从extraParams中获取字段的数据类型配置
        Map<String, Object> extraParams = parseExtraParams(device.getExtraParams());
        @SuppressWarnings("unchecked")
        Map<String, Object> fieldConfigs = (Map<String, Object>) extraParams.get("fieldConfigs");
        if (fieldConfigs != null) {
            @SuppressWarnings("unchecked")
            Map<String, Object> fieldConfig = (Map<String, Object>) fieldConfigs.get(fieldName);
            if (fieldConfig != null && fieldConfig.get("dataType") != null) {
                dataType = String.valueOf(fieldConfig.get("dataType"));
            }
        }
        // 根据数据类型读取
        switch (dataType.toLowerCase()) {
            case "float":
            case "real":
                return modbusClient.readFloat(address);
            case "string":
            case "str":
                // 字符串需要指定长度,默认32字符
                int stringLength = 32;
                if (fieldConfigs != null) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> fieldConfig = (Map<String, Object>) fieldConfigs.get(fieldName);
                    if (fieldConfig != null && fieldConfig.get("length") != null) {
                        stringLength = ((Number) fieldConfig.get("length")).intValue();
                    }
                }
                return modbusClient.readString(address, stringLength);
            case "int":
            case "integer":
            case "word":
            default:
                return modbusClient.readRegister(address);
        }
    }
@@ -183,21 +363,102 @@
            log.error("Modbus PLC未连接,无法写入数据: {}:{}", this.plcIp, this.plcPort);
            return false;
        }
        if (data == null || data.isEmpty()) {
            log.warn("写入数据为空,跳过操作: deviceId={}", device.getId());
            return true;
        }
        try {
            // TODO: 实现Modbus写入数据
            // 这里暂时返回true,后续完善
            log.warn("Modbus writeData未实现,返回成功");
            int successCount = 0;
            int failCount = 0;
            for (Map.Entry<String, Object> entry : data.entrySet()) {
                String fieldName = entry.getKey();
                Object value = entry.getValue();
                if (value == null) {
                    continue; // 跳过null值
                }
                try {
                    writeFieldValue(fieldName, value);
                    successCount++;
                } catch (Exception e) {
                    log.error("写入字段失败: fieldName={}, value={}, deviceId={}, error={}",
                            fieldName, value, device.getId(), e.getMessage());
                    failCount++;
                }
            }
            if (failCount > 0) {
                log.warn("Modbus写入数据部分失败: deviceId={}, success={}, fail={}",
                        device.getId(), successCount, failCount);
                return false;
            }
            log.info("Modbus写入数据成功: deviceId={}, fieldCount={}", device.getId(), successCount);
            return true;
        } catch (Exception e) {
            log.error("Modbus PLC写入数据失败: {}:{}", this.plcIp, this.plcPort, e);
            this.connected = false;
            return false;
        }
    }
    /**
     * 写入单个字段的值
     */
    private void writeFieldValue(String fieldName, Object value) throws IOException {
        String address = getModbusAddress(fieldName);
        if (address == null) {
            throw new IllegalArgumentException("字段 " + fieldName + " 未找到Modbus地址映射");
        }
        // 根据值类型推断数据类型
        String dataType = inferDataType(fieldName, value);
        // 从extraParams中获取字段的数据类型配置
        Map<String, Object> extraParams = parseExtraParams(device.getExtraParams());
        @SuppressWarnings("unchecked")
        Map<String, Object> fieldConfigs = (Map<String, Object>) extraParams.get("fieldConfigs");
        if (fieldConfigs != null) {
            @SuppressWarnings("unchecked")
            Map<String, Object> fieldConfig = (Map<String, Object>) fieldConfigs.get(fieldName);
            if (fieldConfig != null && fieldConfig.get("dataType") != null) {
                dataType = String.valueOf(fieldConfig.get("dataType"));
            }
        }
        // 根据数据类型写入
        switch (dataType.toLowerCase()) {
            case "float":
            case "real":
                float floatValue;
                if (value instanceof Number) {
                    floatValue = ((Number) value).floatValue();
                } else {
                    floatValue = Float.parseFloat(String.valueOf(value));
                }
                modbusClient.writeFloat(address, floatValue);
                break;
            case "string":
            case "str":
                String stringValue = String.valueOf(value);
                modbusClient.writeString(address, stringValue);
                break;
            case "int":
            case "integer":
            case "word":
            default:
                int intValue;
                if (value instanceof Number) {
                    intValue = ((Number) value).intValue();
                } else {
                    intValue = Integer.parseInt(String.valueOf(value));
                }
                modbusClient.writeRegister(address, intValue);
                break;
        }
    }
@@ -214,17 +475,17 @@
            return false;
        }
    }
    @Override
    public String getPlcType() {
        return "MODBUS";
    }
    @Override
    public int getTimeout() {
        return this.timeout;
    }
    @Override
    public void setTimeout(int timeout) {
        this.timeout = timeout;