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:
# type: com.alibaba.druid.pool.DruidDataSource
# 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
# password: fastbee
shardingsphere:
@ -113,7 +113,7 @@ spring:
mqtt:
username: 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}
default-topic: test # 默认主题
timeout: 30 # 超时时间
@ -121,7 +121,7 @@ spring:
clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)
# EMQX API配置需要在运行emqx之后去EMQX管理后台手动创建API Key和Secret,并配置到application.yml中
emqx:
host: localhost # EMQX服务器地址
host: 1.116.124.85 # EMQX服务器地址
port: 18083
ApiKey: emqx.key.123 # EMQX API Key
ApiSecret: emqx.api.123

View File

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

View File

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

View File

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

View File

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

View File

@ -344,4 +344,13 @@
</if>
</where>
</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>