fix:寄存器首地址取值先走DB

This commit is contained in:
zhugongping 2025-12-06 17:56:01 +08:00
parent 94c49e36b1
commit 471c3a6a2c
8 changed files with 109 additions and 52 deletions

View File

@ -41,7 +41,7 @@ spring:
# slave: # slave:
# type: com.alibaba.druid.pool.DruidDataSource # type: com.alibaba.druid.pool.DruidDataSource
# driver-class-name: com.mysql.cj.jdbc.Driver # driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # url: jdbc:mysql://1.116.124.85:3306/fastbee1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root # username: root
# password: fastbee # password: fastbee
shardingsphere: shardingsphere:
@ -113,7 +113,7 @@ spring:
mqtt: mqtt:
username: fastbee # 账号 username: fastbee # 账号
password: fastbee # 密码 password: fastbee # 密码
host-url: tcp://localhost:1883 # mqtt连接tcp地址 host-url: tcp://1.116.124.85:1883 # mqtt连接tcp地址
client-id: ${random.int} # 客户端Id,不能相同,采用随机数 ${random.value} client-id: ${random.int} # 客户端Id,不能相同,采用随机数 ${random.value}
default-topic: test # 默认主题 default-topic: test # 默认主题
timeout: 30 # 超时时间 timeout: 30 # 超时时间
@ -121,7 +121,7 @@ spring:
clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)
# EMQX API配置需要在运行emqx之后去EMQX管理后台手动创建API Key和Secret,并配置到application.yml中 # EMQX API配置需要在运行emqx之后去EMQX管理后台手动创建API Key和Secret,并配置到application.yml中
emqx: emqx:
host: localhost # EMQX服务器地址 host: 1.116.124.85 # EMQX服务器地址
port: 18083 port: 18083
ApiKey: emqx.key.123 # EMQX API Key ApiKey: emqx.key.123 # EMQX API Key
ApiSecret: emqx.api.123 ApiSecret: emqx.api.123

View File

@ -51,6 +51,8 @@ server:
# Spring配置 # Spring配置
spring: spring:
# quartz:
# auto-startup: false # 上线前去掉
# 环境配置,dev=开发环境,prod=生产环境 # 环境配置,dev=开发环境,prod=生产环境
profiles: profiles:
active: dev # 环境配置,dev=开发环境,prod=生产环境 active: dev # 环境配置,dev=开发环境,prod=生产环境

View File

@ -301,8 +301,8 @@ public class CaculateUtils {
* @param bytes * @param bytes
* @return * @return
*/ */
public static Float toFloat32_CDAB(byte[] bytes) { public static Float toFloat32_DCBA(byte[] bytes) {
int intValue = ((bytes[2] & 0xFF) << 24) | ((bytes[3] & 0xFF) << 16) | ((bytes[0] & 0xFF) << 8) | ((bytes[1] & 0xFF)) ; int intValue = ((bytes[3] & 0xFF) << 24) | ((bytes[2] & 0xFF) << 16) | ((bytes[1] & 0xFF) << 8) | ((bytes[0] & 0xFF)) ;
return Float.intBitsToFloat(intValue); return Float.intBitsToFloat(intValue);
} }
@ -339,8 +339,8 @@ public class CaculateUtils {
case FLOAT_ABCD: case FLOAT_ABCD:
value = CaculateUtils.toFloat32_ABCD(bytes) + ""; value = CaculateUtils.toFloat32_ABCD(bytes) + "";
break; break;
case FLOAT_CDAB: case FLOAT_CDAB: // todo:待改枚举页面枚举
value = CaculateUtils.toFloat32_CDAB(bytes) + ""; value = CaculateUtils.toFloat32_DCBA(bytes) + "";
break; break;
} }
} }
@ -417,7 +417,7 @@ public class CaculateUtils {
System.out.println(i); System.out.println(i);
byte[] bytes1 = ByteBufUtil.decodeHexDump(hexToBytes1); byte[] bytes1 = ByteBufUtil.decodeHexDump(hexToBytes1);
float v2 = toFloat32_CDAB(bytes1); float v2 = toFloat32_DCBA(bytes1);
System.out.println("转32位浮点型-CDAB序:"+v2); System.out.println("转32位浮点型-CDAB序:"+v2);
int signedShort = -32627; // 16位有符号整形 int signedShort = -32627; // 16位有符号整形

View File

@ -141,7 +141,7 @@ public class CRC16Utils {
byte[] byte8 = ByteUtils.hexToByte(crc8); byte[] byte8 = ByteUtils.hexToByte(crc8);
int b = CRC8(byte8); int b = CRC8(byte8);
System.out.println((int) b); System.out.println((int) b);
float v = CaculateUtils.toFloat32_CDAB(bytes); float v = CaculateUtils.toFloat32_DCBA(bytes);
System.out.println(v); System.out.println(v);
} }

View File

@ -71,5 +71,7 @@ public class ModbusUtils {
public static void main(String[] args) { public static void main(String[] args) {
int modbusAddress = getModbusAddress("0101000A0001FDCA"); int modbusAddress = getModbusAddress("0101000A0001FDCA");
System.out.println(modbusAddress); System.out.println(modbusAddress);
Mparams modbusParams = getModbusParams("01030001000815cc");
System.out.println("modbusParams.getAddress() = " + modbusParams.getAddress());
} }
} }

View File

@ -11,26 +11,20 @@ import com.fastbee.common.core.mq.message.DeviceData;
import com.fastbee.common.core.mq.message.FunctionCallBackBo; import com.fastbee.common.core.mq.message.FunctionCallBackBo;
import com.fastbee.common.core.protocol.modbus.ModbusCode; import com.fastbee.common.core.protocol.modbus.ModbusCode;
import com.fastbee.common.core.redis.RedisCache; import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem; import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.enums.FunctionReplyStatus; import com.fastbee.common.enums.FunctionReplyStatus;
import com.fastbee.common.enums.ModbusDataType;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.CaculateUtils; import com.fastbee.common.utils.CaculateUtils;
import com.fastbee.common.utils.DateUtils; import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils; import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.gateway.CRC16Utils; import com.fastbee.common.utils.gateway.CRC16Utils;
import com.fastbee.common.utils.modbus.BitUtils; import com.fastbee.common.utils.modbus.BitUtils;
import com.fastbee.common.utils.modbus.ModbusUtils;
import com.fastbee.common.utils.modbus.Mparams;
import com.fastbee.iot.cache.IModbusConfigCache; import com.fastbee.iot.cache.IModbusConfigCache;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.domain.ModbusConfig; import com.fastbee.iot.domain.ModbusConfig;
import com.fastbee.iot.model.ThingsModelItem.Datatype; import com.fastbee.iot.mapper.DeviceMapper;
import com.fastbee.iot.mapper.ModbusConfigMapper;
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
import com.fastbee.iot.model.gateWay.SubDeviceListVO;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.ISubGatewayService; import com.fastbee.iot.service.ISubGatewayService;
import com.fastbee.iot.service.IThingsModelService;
import com.fastbee.modbus.model.ModbusRtu; import com.fastbee.modbus.model.ModbusRtu;
import com.fastbee.protocol.base.protocol.IProtocol; import com.fastbee.protocol.base.protocol.IProtocol;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -41,7 +35,10 @@ import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -64,6 +61,11 @@ public class ModbusProtocol implements IProtocol {
private IModbusConfigCache modbusConfigCache; private IModbusConfigCache modbusConfigCache;
@Resource @Resource
private ISubGatewayService subGatewayService; private ISubGatewayService subGatewayService;
@Resource
private DeviceMapper deviceMapper;
@Resource
private ModbusConfigMapper modbusConfigMapper;
@Override @Override
public DeviceReport decode(DeviceData deviceData, String clientId) { public DeviceReport decode(DeviceData deviceData, String clientId) {
@ -96,14 +98,14 @@ public class ModbusProtocol implements IProtocol {
case Read01: case Read01:
case Read02: case Read02:
//起始地址 //起始地址
String address0102 = getCacheModbusAddress(deviceData.getSerialNumber(),slaveId,code); String address0102 = getCacheModbusAddress(deviceData.getSerialNumber(), slaveId, code);
//读线圈或读离散型寄存器处理 //读线圈或读离散型寄存器处理
handleRead0102(productId, message, address0102, values); handleRead0102(productId, message, address0102, values);
break; break;
case Read03: case Read03:
case Read04: case Read04:
//起始地址 //起始地址
String address0304 = getCacheModbusAddress(deviceData.getSerialNumber(),slaveId,code); String address0304 = getCacheModbusAddress(deviceData.getSerialNumber(), slaveId, code);
//读输入、保持寄存器 //读输入、保持寄存器
handleRead0304(productId, message, address0304, values, hexDump); handleRead0304(productId, message, address0304, values, hexDump);
break; break;
@ -117,7 +119,7 @@ public class ModbusProtocol implements IProtocol {
report.setSources(hexDump); report.setSources(hexDump);
report.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu); report.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu);
report.setStatus(FunctionReplyStatus.SUCCESS); report.setStatus(FunctionReplyStatus.SUCCESS);
this.handleMsgReply(message,report,modbusCode); this.handleMsgReply(message, report, modbusCode);
return report; return report;
} }
@ -158,8 +160,9 @@ public class ModbusProtocol implements IProtocol {
modbusConfigList.addAll(entry.getValue()); modbusConfigList.addAll(entry.getValue());
} }
int isReadOnly = message.getCode() == 1 ? 1 : 0; int isReadOnly = message.getCode() == 1 ? 1 : 0;
Map<Integer, List<ModbusConfig>> listMap = modbusConfigList.stream().filter(config -> Map<Integer, List<ModbusConfig>> listMap = modbusConfigList.stream()
{ return config.getType() == 1 && config.getIsReadonly() == isReadOnly;}).collect(Collectors.groupingBy(ModbusConfig::getAddress)); .filter(config -> config.getType() == 1 && config.getIsReadonly() == isReadOnly)
.collect(Collectors.groupingBy(ModbusConfig::getAddress));
//分割值 //分割值
List<String> values = StringUtils.splitEvenly(hexDump, 2); List<String> values = StringUtils.splitEvenly(hexDump, 2);
int ioAd = Integer.parseInt(address); int ioAd = Integer.parseInt(address);
@ -204,7 +207,7 @@ public class ModbusProtocol implements IProtocol {
List<ModbusConfig> IOConfigList = listMap.get(1); List<ModbusConfig> IOConfigList = listMap.get(1);
if (!CollectionUtils.isEmpty(IOConfigList)) { if (!CollectionUtils.isEmpty(IOConfigList)) {
if (IOConfigList.size() > 1) { if (IOConfigList.size() > 1) {
//03按位运行情况,读寄存器需要将16进制转换为2进制,按位取值 // 03按位运行情况,读寄存器需要将16进制转换为2进制,按位取值
// 如1-4个继电器开关情况,寄存器值是0x0007 从右到左,1-4 对应 on-on-on-off // 如1-4个继电器开关情况,寄存器值是0x0007 从右到左,1-4 对应 on-on-on-off
for (ModbusConfig config : IOConfigList) { for (ModbusConfig config : IOConfigList) {
int result = BitUtils.deter(data[0], config.getBitOrder()); int result = BitUtils.deter(data[0], config.getBitOrder());
@ -241,13 +244,23 @@ public class ModbusProtocol implements IProtocol {
modbusConfigList.addAll(entry.getValue()); modbusConfigList.addAll(entry.getValue());
} }
int isReadOnly = message.getCode() == 3 ? 1 : 0; int isReadOnly = message.getCode() == 3 ? 1 : 0;
Map<Integer, ModbusConfig> configMap = modbusConfigList.stream().filter(config -> Map<Integer, ModbusConfig> configMap = modbusConfigList.stream()
{return config.getType() == 2 && config.getIsReadonly() == isReadOnly;}).collect(Collectors.toMap(ModbusConfig::getAddress, Function.identity())); .filter(config -> config.getType() == 2 && config.getIsReadonly() == isReadOnly)
.collect(Collectors.toMap(ModbusConfig::getAddress, Function.identity()));
int registerAd = Integer.parseInt(address); int registerAd = Integer.parseInt(address);
for (int i = 0; i < length; i++) { // 需要根据首地址值以及数据长度动态截取数据 例如数据: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;
}
for (int i = 0; i < length; ) { // 移除i++,手动控制循环
ModbusConfig modbusConfig = configMap.get(registerAd); ModbusConfig modbusConfig = configMap.get(registerAd);
if (Objects.isNull(modbusConfig)) { if (Objects.isNull(modbusConfig)) {
//处理可能是 03按位运行情况,判断是否有寄存器,而且寄存器对应物模型多个 // 处理可能是 03按位运行情况,判断是否有寄存器,而且寄存器对应物模型多个
List<ModbusConfig> dataToIoList = modbusConfigMap.get(registerAd); List<ModbusConfig> dataToIoList = modbusConfigMap.get(registerAd);
if (!CollectionUtils.isEmpty(dataToIoList) && dataToIoList.size() > 1) { if (!CollectionUtils.isEmpty(dataToIoList) && dataToIoList.size() > 1) {
for (ModbusConfig config : dataToIoList) { for (ModbusConfig config : dataToIoList) {
@ -262,6 +275,7 @@ public class ModbusProtocol implements IProtocol {
log.warn("寄存器地址:{},不存在", registerAd); log.warn("寄存器地址:{},不存在", registerAd);
} }
registerAd += 1; registerAd += 1;
i += 1; // 单个寄存器,i增加1
continue; continue;
} }
//个数 //个数
@ -271,9 +285,12 @@ public class ModbusProtocol implements IProtocol {
simpleItem.setTs(DateUtils.getNowDate()); simpleItem.setTs(DateUtils.getNowDate());
if (quantity == 1) { if (quantity == 1) {
//将identity与address替换 //将identity与address替换
simpleItem.setValue(CaculateUtils.handleToUnSign16(data[i] + "", modbusConfig.getDataType())); String handleHex = dataHex.substring(0, 4);
String value = CaculateUtils.handleToUnSign16(Long.parseLong(handleHex, 16) + "", modbusConfig.getDataType());
simpleItem.setValue(value);
simpleItemList.add(simpleItem); simpleItemList.add(simpleItem);
dataHex = dataHex.substring(4); dataHex = dataHex.substring(4);
i += 1; // 单个寄存器,i增加1
} else if (quantity == 2) { } else if (quantity == 2) {
//这里做数据处理 //这里做数据处理
String handleHex = dataHex.substring(0, 8); String handleHex = dataHex.substring(0, 8);
@ -282,20 +299,21 @@ public class ModbusProtocol implements IProtocol {
simpleItemList.add(simpleItem); simpleItemList.add(simpleItem);
//处理dataHex //处理dataHex
dataHex = dataHex.substring(8); dataHex = dataHex.substring(8);
//处理quantity ==2的情况,跳过一次循环 i += 2; // 浮点数,i增加2
i += 1;
} }
//寄存器地址+1
registerAd += 1; //寄存器地址根据quantity增加
registerAd += quantity;
} }
} }
} }
/** /**
* 处理下发指令设备回复消息,按照modbus协议约定会返回一样的指令 * 处理下发指令设备回复消息,按照modbus协议约定会返回一样的指令
*
* @param message 解析后数据 * @param message 解析后数据
*/ */
private void handleMsgReply(ModbusRtu message,DeviceReport report,ModbusCode code){ private void handleMsgReply(ModbusRtu message, DeviceReport report, ModbusCode code) {
//单个寄存器上报情况处理 //单个寄存器上报情况处理
String address = message.getAddress() + ""; String address = message.getAddress() + "";
List<ModbusConfig> modbusConfig = modbusConfigCache.getSingleModbusConfig(report.getProductId(), address); List<ModbusConfig> modbusConfig = modbusConfigCache.getSingleModbusConfig(report.getProductId(), address);
@ -313,13 +331,13 @@ public class ModbusProtocol implements IProtocol {
simpleItem.setId(config.getIdentifier()); simpleItem.setId(config.getIdentifier());
simpleItem.setTs(DateUtils.getNowDate()); simpleItem.setTs(DateUtils.getNowDate());
int data = message.getWriteData(); int data = message.getWriteData();
if (type == 1){ if (type == 1) {
//对于IO类型的数据做一个数据转换 0x0000 是关 0xff00 是开 对应值 1 //对于IO类型的数据做一个数据转换 0x0000 是关 0xff00 是开 对应值 1
if (data == 0xff00){ if (data == 0xff00) {
data = 1; data = 1;
} }
} }
simpleItem.setValue(data +""); simpleItem.setValue(data + "");
values.add(simpleItem); values.add(simpleItem);
report.setThingsModelSimpleItem(values); report.setThingsModelSimpleItem(values);
} }
@ -356,7 +374,7 @@ public class ModbusProtocol implements IProtocol {
this.read03(config, rtu); this.read03(config, rtu);
break; break;
case Write05: case Write05:
write05(config,message.getValue(), rtu); write05(config, message.getValue(), rtu);
break; break;
case Write06: case Write06:
write06(config, message.getValue(), rtu); write06(config, message.getValue(), rtu);
@ -411,20 +429,39 @@ public class ModbusProtocol implements IProtocol {
/** /**
* 获取modbus地址 * 获取modbus地址
*/ */
private String getCacheModbusAddress(String serialNumber,int salveId,int code) { private String getCacheModbusAddress(String serialNumber, int salveId, int code) {
String key = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber); // String key = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber);
Set<String> commandSet = redisCache.zRange(key, 0, -1); // Set<String> commandSet = redisCache.zRange(key, 0, -1);
if (commandSet.size() == 0) { //
throw new ServiceException("No cache modbus address found"); // // 1. 遍历匹配指令
// for (String command : commandSet) {
// try {
// Mparams params = ModbusUtils.getModbusParams(command);
// redisCache.zRem(key, command); // 删除缓存
// if (params.getSlaveId() == salveId && params.getCode() == code) {
// return String.valueOf(params.getAddress());
// }
// } catch (Exception e) {
// log.warn("解析指令异常: {}", e.getMessage());
// }
// }
// 2. 无匹配时查询数据库
try {
Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber);
if (device == null) return "0";
List<ModbusConfig> configs = modbusConfigMapper.selectModbusConfigListByProductId(device.getProductId());
if (CollectionUtils.isEmpty(configs)) return "0";
return configs.stream()
.mapToInt(ModbusConfig::getAddress)
.min()
.orElse(0) + "";
} catch (Exception e) {
log.error("数据库查询异常: {}", e.getMessage());
return "0";
} }
for (String command : commandSet) {
Mparams params = ModbusUtils.getModbusParams(command);
redisCache.zRem(key, command);
if (params.getSlaveId() == salveId && params.getCode() == code){
return params.getAddress()+"";
}
}
throw new ServiceException("No cache modbus address found");
} }
} }

View File

@ -90,4 +90,11 @@ public interface ModbusConfigMapper
* @return * @return
*/ */
public List<ModbusConfig> selectShortListByProductId(ModbusConfig config); public List<ModbusConfig> selectShortListByProductId(ModbusConfig config);
/**
* 根据产品id获取modbus配置
* @param productId
* @return
*/
List<ModbusConfig> selectModbusConfigListByProductId(Long productId);
} }

View File

@ -344,4 +344,13 @@
</if> </if>
</where> </where>
</select> </select>
<select id="selectModbusConfigListByProductId" resultType="com.fastbee.iot.domain.ModbusConfig">
<include refid="selectModbusConfigShortVo"/>
<where>
<if test="productId != null">
and product_id = #{productId}
</if>
</where>
</select>
</mapper> </mapper>