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) {
@ -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);
@ -241,10 +244,20 @@ 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按位运行情况,判断是否有寄存器,而且寄存器对应物模型多个
@ -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,17 +299,18 @@ 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) {
@ -412,19 +430,38 @@ 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>