From a4583c26a6a40267ff1b19efd4d3dc326298058b Mon Sep 17 00:00:00 2001 From: zhugongping Date: Sun, 7 Dec 2025 17:13:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:modbus=E8=A7=A3=E6=9E=90=E5=A4=B1=E8=B4=A5b?= =?UTF-8?q?ug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fastbee/helper/ModbusProtocolHelper.java | 36 ++++++ .../fastbee/modbus/codec/ModbusProtocol.java | 103 ++++++++++-------- .../modbustcp/codec/ModbusTcpProtocol.java | 12 +- .../iot/cache/impl/ModbusConfigCacheImpl.java | 20 ++-- 4 files changed, 105 insertions(+), 66 deletions(-) create mode 100644 fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/helper/ModbusProtocolHelper.java diff --git a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/helper/ModbusProtocolHelper.java b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/helper/ModbusProtocolHelper.java new file mode 100644 index 00000000..1e9bff31 --- /dev/null +++ b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/helper/ModbusProtocolHelper.java @@ -0,0 +1,36 @@ +package com.fastbee.helper; + +import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem; +import com.fastbee.common.utils.DateUtils; +import com.fastbee.iot.domain.ModbusConfig; + +import java.util.List; +import java.util.function.Function; + +/** + * @Date: 2025-12-06 + * @Author: zhugongping + **/ +public class ModbusProtocolHelper { + + + /** + * 构建简单数据项 + * + * @param simpleItemList + * @param bitOrderFunction + * @param ioConfigs + */ + public static void buildSimpleItem(List simpleItemList, + Function bitOrderFunction, + List ioConfigs) { + for (ModbusConfig config : ioConfigs) { + ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem(); + simpleItem.setId(config.getIdentifier()); + simpleItem.setValue(bitOrderFunction.apply(config)); + simpleItem.setTs(DateUtils.getNowDate()); + simpleItemList.add(simpleItem); + } + } + +} diff --git a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java index a0210654..c5749511 100644 --- a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java +++ b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbus/codec/ModbusProtocol.java @@ -18,6 +18,7 @@ import com.fastbee.common.utils.DateUtils; import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.gateway.CRC16Utils; import com.fastbee.common.utils.modbus.BitUtils; +import com.fastbee.helper.ModbusProtocolHelper; import com.fastbee.iot.cache.IModbusConfigCache; import com.fastbee.iot.domain.Device; import com.fastbee.iot.domain.ModbusConfig; @@ -231,7 +232,7 @@ public class ModbusProtocol implements IProtocol { simpleItemList.add(simpleItem); } } else { - //原数据字符串 + // 原数据字符串(保持完整,不截取) String dataHex = sourceStr.substring(6, sourceStr.length() - 4); //处理多个情况 Map> modbusConfigMap = modbusConfigCache.getModbusConfigCacheByProductId(productId); @@ -239,71 +240,77 @@ public class ModbusProtocol implements IProtocol { log.warn("寄存器数据不存在"); return; } + + // 获取起始地址(数据从该地址开始) + int startAddress = Integer.parseInt(address); + int isReadOnly = message.getCode() == 3 ? 1 : 0; + + // 获取所有相关的配置 List modbusConfigList = new ArrayList<>(); for (Map.Entry> entry : modbusConfigMap.entrySet()) { modbusConfigList.addAll(entry.getValue()); } - int isReadOnly = message.getCode() == 3 ? 1 : 0; - Map configMap = modbusConfigList.stream() - .filter(config -> config.getType() == 2 && config.getIsReadonly() == isReadOnly) - .collect(Collectors.toMap(ModbusConfig::getAddress, Function.identity())); - int registerAd = Integer.parseInt(address); - // 需要根据首地址值以及数据长度动态截取数据 例如数据:0103 0800 3800 4500 5600 7801 e9 - int startIndex = registerAd * 4; // 每个寄存器占4个十六进制字符 - if (startIndex > 0 && startIndex < dataHex.length()) { - dataHex = dataHex.substring(startIndex); - log.debug("截取后数据(起始地址{}): {}", registerAd, dataHex); - } else if (startIndex >= dataHex.length()) { - log.error("起始地址{}超出数据范围! 数据长度: {}", registerAd, dataHex.length() / 4); - return; + + // 处理IO类型(type == 1):按位处理 + Map> ioConfigMap = modbusConfigList.stream() + .filter(config -> config.getType() == 1 && config.getIsReadonly() == isReadOnly) + .collect(Collectors.groupingBy(ModbusConfig::getAddress)); + + // 遍历数据数组,处理IO类型 + for (int i = 0; i < length; i++) { + int currentAddress = startAddress + i; + List ioConfigs = ioConfigMap.get(currentAddress); + if (!CollectionUtils.isEmpty(ioConfigs) && ioConfigs.size() > 1) { + // 处理IO类型,按位取值 + int index = i; + Function bitOrderFunction = config -> String.valueOf(BitUtils.deter(data[index], config.getBitOrder())); + ModbusProtocolHelper.buildSimpleItem(simpleItemList, bitOrderFunction, ioConfigs); + } } - for (int i = 0; i < length; ) { // 移除i++,手动控制循环 - ModbusConfig modbusConfig = configMap.get(registerAd); - if (Objects.isNull(modbusConfig)) { - // 处理可能是 03按位运行情况,判断是否有寄存器,而且寄存器对应物模型多个 - List dataToIoList = modbusConfigMap.get(registerAd); - if (!CollectionUtils.isEmpty(dataToIoList) && dataToIoList.size() > 1) { - for (ModbusConfig config : dataToIoList) { - int result = BitUtils.deter(data[i], config.getBitOrder()); - ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem(); - simpleItem.setId(config.getIdentifier()); - simpleItem.setValue(result + ""); - simpleItem.setTs(DateUtils.getNowDate()); - simpleItemList.add(simpleItem); - } - } else { - log.warn("寄存器地址:{},不存在", registerAd); - } - registerAd += 1; - i += 1; // 单个寄存器,i增加1 + + // 处理数据寄存器类型(type == 2):根据每个配置的address动态定位数据 + List dataConfigList = modbusConfigList.stream() + .filter(config -> config.getType() == 2 && config.getIsReadonly() == isReadOnly) + .collect(Collectors.toList()); + + for (ModbusConfig modbusConfig : dataConfigList) { + int configAddress = modbusConfig.getAddress(); + Integer quantity = modbusConfig.getQuantity(); + + // 计算该配置在dataHex中的位置(相对于起始地址) + // 每个寄存器占4个十六进制字符 + int offset = (configAddress - startAddress) * 4; + int hexLength = quantity * 4; // 根据quantity确定需要读取的数据长度 + + // 检查数据是否足够 + if (offset < 0 || offset + hexLength > dataHex.length()) { + log.warn("配置地址{}的数据超出范围,起始地址:{},需要{}个字符,实际可用:{}", + configAddress, startAddress, hexLength, + offset < 0 ? 0 : dataHex.length() - offset); continue; } - //个数 - Integer quantity = modbusConfig.getQuantity(); + + // 从dataHex的对应位置提取数据 + String handleHex = dataHex.substring(offset, offset + hexLength); + ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem(); simpleItem.setId(modbusConfig.getIdentifier()); simpleItem.setTs(DateUtils.getNowDate()); + if (quantity == 1) { - //将identity与address替换 - String handleHex = dataHex.substring(0, 4); + // 单个寄存器 String value = CaculateUtils.handleToUnSign16(Long.parseLong(handleHex, 16) + "", modbusConfig.getDataType()); simpleItem.setValue(value); - simpleItemList.add(simpleItem); - dataHex = dataHex.substring(4); - i += 1; // 单个寄存器,i增加1 } else if (quantity == 2) { - //这里做数据处理 - String handleHex = dataHex.substring(0, 8); + // 浮点数,2个寄存器 String value = CaculateUtils.parseValue(modbusConfig.getDataType(), handleHex); simpleItem.setValue(value); - simpleItemList.add(simpleItem); - //处理dataHex - dataHex = dataHex.substring(8); - i += 2; // 浮点数,i增加2 + } else { + log.warn("不支持的quantity值:{},配置地址:{}", quantity, configAddress); + continue; } - //寄存器地址根据quantity增加 - registerAd += quantity; + simpleItemList.add(simpleItem); } } } diff --git a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java index 4a03306c..4f68853c 100644 --- a/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java +++ b/fastbee-protocol/fastbee-protocol-collect/src/main/java/com/fastbee/modbustcp/codec/ModbusTcpProtocol.java @@ -18,6 +18,7 @@ import com.fastbee.common.utils.CaculateUtils; import com.fastbee.common.utils.DateUtils; import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.modbus.BitUtils; +import com.fastbee.helper.ModbusProtocolHelper; import com.fastbee.iot.cache.IModbusConfigCache; import com.fastbee.iot.domain.ModbusConfig; import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; @@ -284,14 +285,9 @@ public class ModbusTcpProtocol implements IProtocol { //处理可能是 03按位运行情况,判断是否有寄存器,而且寄存器对应物模型多个 List dataToIoList = modbusConfigMap.get(registerAd); if (!CollectionUtils.isEmpty(dataToIoList) && dataToIoList.size() > 1) { - for (ModbusConfig config : dataToIoList) { - int result = BitUtils.deter(data[i], config.getBitOrder()); - ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem(); - simpleItem.setId(config.getIdentifier()); - simpleItem.setValue(result + ""); - simpleItem.setTs(DateUtils.getNowDate()); - simpleItemList.add(simpleItem); - } + int index = i; + Function bitOrderFunction = config -> String.valueOf(BitUtils.deter(data[index], config.getBitOrder())); + ModbusProtocolHelper.buildSimpleItem(simpleItemList, bitOrderFunction, dataToIoList); } else { log.warn("寄存器地址:{},不存在", registerAd); } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java index b8bb8e59..e2f647c8 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/cache/impl/ModbusConfigCacheImpl.java @@ -46,16 +46,16 @@ public class ModbusConfigCacheImpl implements IModbusConfigCache { */ @Override public Map> getModbusConfigCacheByProductId(Long productId){ - Map> resultMap = new HashMap<>(); - String modbusKey = RedisKeyBuilder.buildModbusKey(productId); - Map map = redisCache.hashEntity(modbusKey); - if (!CollectionUtils.isEmpty(map)){ - for (Map.Entry entry : map.entrySet()) { - List modbusConfigList = JSON.parseArray(entry.getValue(), ModbusConfig.class); - resultMap.put(Integer.parseInt(entry.getKey()), modbusConfigList); - } - return resultMap; - } +// Map> resultMap = new HashMap<>(); +// String modbusKey = RedisKeyBuilder.buildModbusKey(productId); +// Map map = redisCache.hashEntity(modbusKey); +// if (!CollectionUtils.isEmpty(map)){ +// for (Map.Entry entry : map.entrySet()) { +// List modbusConfigList = JSON.parseArray(entry.getValue(), ModbusConfig.class); +// resultMap.put(Integer.parseInt(entry.getKey()), modbusConfigList); +// } +// return resultMap; +// } return setModbusConfigCacheByProductId(productId); }