fix:modbus解析失败bug修复

This commit is contained in:
zhugongping 2025-12-07 17:13:23 +08:00
parent 471c3a6a2c
commit a4583c26a6
4 changed files with 105 additions and 66 deletions

View File

@ -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<ThingsModelSimpleItem> simpleItemList,
Function<ModbusConfig, String> bitOrderFunction,
List<ModbusConfig> 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);
}
}
}

View File

@ -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<Integer, List<ModbusConfig>> 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<ModbusConfig> modbusConfigList = new ArrayList<>();
for (Map.Entry<Integer, List<ModbusConfig>> entry : modbusConfigMap.entrySet()) {
modbusConfigList.addAll(entry.getValue());
}
int isReadOnly = message.getCode() == 3 ? 1 : 0;
Map<Integer, ModbusConfig> 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<Integer, List<ModbusConfig>> 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<ModbusConfig> ioConfigs = ioConfigMap.get(currentAddress);
if (!CollectionUtils.isEmpty(ioConfigs) && ioConfigs.size() > 1) {
// 处理IO类型,按位取值
int index = i;
Function<ModbusConfig, String> 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<ModbusConfig> 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<ModbusConfig> 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);
}
}
}

View File

@ -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<ModbusConfig> 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<ModbusConfig, String> bitOrderFunction = config -> String.valueOf(BitUtils.deter(data[index], config.getBitOrder()));
ModbusProtocolHelper.buildSimpleItem(simpleItemList, bitOrderFunction, dataToIoList);
} else {
log.warn("寄存器地址:{},不存在", registerAd);
}

View File

@ -46,16 +46,16 @@ public class ModbusConfigCacheImpl implements IModbusConfigCache {
*/
@Override
public Map<Integer, List<ModbusConfig>> getModbusConfigCacheByProductId(Long productId){
Map<Integer, List<ModbusConfig>> resultMap = new HashMap<>();
String modbusKey = RedisKeyBuilder.buildModbusKey(productId);
Map<String,String> map = redisCache.hashEntity(modbusKey);
if (!CollectionUtils.isEmpty(map)){
for (Map.Entry<String, String> entry : map.entrySet()) {
List<ModbusConfig> modbusConfigList = JSON.parseArray(entry.getValue(), ModbusConfig.class);
resultMap.put(Integer.parseInt(entry.getKey()), modbusConfigList);
}
return resultMap;
}
// Map<Integer, List<ModbusConfig>> resultMap = new HashMap<>();
// String modbusKey = RedisKeyBuilder.buildModbusKey(productId);
// Map<String,String> map = redisCache.hashEntity(modbusKey);
// if (!CollectionUtils.isEmpty(map)){
// for (Map.Entry<String, String> entry : map.entrySet()) {
// List<ModbusConfig> modbusConfigList = JSON.parseArray(entry.getValue(), ModbusConfig.class);
// resultMap.put(Integer.parseInt(entry.getKey()), modbusConfigList);
// }
// return resultMap;
// }
return setModbusConfigCacheByProductId(productId);
}