Browse Source

1、netty添加工厂策略。

dev
3067418132@qq.com 3 weeks ago
parent
commit
e43244596a
  1. 39
      mh-common/src/main/java/com/mh/common/model/response/ParseResult.java
  2. 495
      mh-framework/src/main/java/com/mh/framework/netty/NewEchoServerHandler.java
  3. 40
      mh-system/src/main/java/com/mh/system/service/strategy/DeviceParserFactory.java
  4. 83
      mh-system/src/main/java/com/mh/system/service/strategy/HeatPumpParser.java
  5. 23
      mh-system/src/main/java/com/mh/system/service/strategy/IDeviceParser.java
  6. 73
      mh-system/src/main/java/com/mh/system/service/strategy/LiquidParser.java
  7. 92
      mh-system/src/main/java/com/mh/system/service/strategy/MeterParser.java
  8. 79
      mh-system/src/main/java/com/mh/system/service/strategy/TempHumidityParser.java
  9. 90
      mh-system/src/main/java/com/mh/system/service/strategy/WaterParser.java

39
mh-common/src/main/java/com/mh/common/model/response/ParseResult.java

@ -0,0 +1,39 @@
package com.mh.common.model.response;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author LJF
* @version 1.0
* @project EEMCS
* @description 定义统一的数据解析结果对象
* @date 2026-04-28 11:41:34
*/
@Data
public class ParseResult {
private boolean success;
private BigDecimal value;
private String tagName;
private int quality;
private String errorMessage;
public static ParseResult success(BigDecimal value, String tagName) {
ParseResult result = new ParseResult();
result.setSuccess(true);
result.setValue(value);
result.setTagName(tagName);
result.setQuality(0);
return result;
}
public static ParseResult fail(String errorMessage) {
ParseResult result = new ParseResult();
result.setSuccess(false);
result.setErrorMessage(errorMessage);
return result;
}
}

495
mh-framework/src/main/java/com/mh/framework/netty/NewEchoServerHandler.java

@ -0,0 +1,495 @@
package com.mh.framework.netty;
import com.alibaba.fastjson2.JSONObject;
import com.mh.common.constant.Constants;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.core.redis.RedisCache;
import com.mh.common.model.request.AdvantechDatas;
import com.mh.common.model.request.AdvantechReceiver;
import com.mh.common.model.response.ParseResult;
import com.mh.common.utils.DateUtils;
import com.mh.common.utils.NettyTools;
import com.mh.common.utils.SendOrderUtils;
import com.mh.common.utils.spring.SpringUtils;
import com.mh.framework.netty.session.ServerSession;
import com.mh.framework.netty.session.SessionMap;
import com.mh.framework.netty.task.CallbackTask;
import com.mh.framework.netty.task.CallbackTaskScheduler;
import com.mh.framework.rabbitmq.producer.SendMsgByTopic;
import com.mh.system.service.device.ICollectionParamsManageService;
import com.mh.system.service.device.IGatewayManageService;
import com.mh.system.service.strategy.DeviceParserFactory;
import com.mh.system.service.strategy.IDeviceParser;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class NewEchoServerHandler extends ChannelInboundHandlerAdapter {
private final IGatewayManageService gatewayManageService = SpringUtils.getBean(IGatewayManageService.class);
private final ICollectionParamsManageService collectionParamsManageService = SpringUtils.getBean(ICollectionParamsManageService.class);
private final SendMsgByTopic sendMsgByTopic = SpringUtils.getBean(SendMsgByTopic.class);
private final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
private final DeviceParserFactory parserFactory = SpringUtils.getBean(DeviceParserFactory.class);
private int idleCount = 1;
// ✅ 优化1:使用AtomicInteger保证线程安全
private final AtomicInteger num = new AtomicInteger(0);
private volatile int size = 0;
private String receiveStr = "";
private volatile List<CollectionParamsManage> deviceCodeParamList;
// ✅ 优化2:采集状态追踪(用于监控和调试)
private volatile long lastCollectTime = 0;
private volatile int totalCollectCount = 0;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("客户端连接: {}", ctx.channel().remoteAddress());
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
if (obj instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) obj;
if (IdleState.READER_IDLE.equals(event.state())) {
log.warn("读取超时,第{}次未收到设备响应", idleCount);
receiveStr = "";
// ✅ 优化3:使用原子操作递增num,避免卡在同一个设备上
incrementNum();
// 检查是否有控制指令正在执行
if (redisCache.hasKey("order_send")) {
log.warn("读取超时且有控制指令在执行,跳过发送采集指令, num={}", num.get());
return;
}
// 继续发送下一个采集指令
sendCollectOrder(ctx);
}
} else {
super.userEventTriggered(ctx, obj);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
buf.clear();
if (bytes.length <= 1024) {
receiveStr = receiveStr + bytesToHexString(bytes);
receiveStr = receiveStr.replace("null", "").replace(" ", "");
}
} catch (Exception e) {
log.error("channelRead异常", e);
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
receiveStr = receiveStr.toUpperCase();
try {
if (isHeartbeat(receiveStr)) {
// 处理心跳
handleHeartbeat(ctx);
} else if (shouldParseData(receiveStr)) {
// 处理数据解析
handleDataParsing(ctx);
} else if (isControlResponse(receiveStr)) {
// 处理控制返回
handleControlResponse(ctx);
}
} catch (Exception e) {
log.error("数据处理异常: {}", receiveStr, e);
} finally {
ctx.flush();
}
}
private boolean isHeartbeat(String data) {
return data.length() == 8 && data.startsWith("2400");
}
private boolean shouldParseData(String data) {
return (data.length() == 18 || data.length() == 12 || data.length() == 14)
&& !redisCache.hasKey("order_send_read");
}
private boolean isControlResponse(String data) {
return data.length() == 16 || (data.length() > 20 && data.length() < 100);
}
private void handleHeartbeat(ChannelHandlerContext ctx) throws InterruptedException {
dealSession(ctx);
idleCount = 1;
// String port = receiveStr.substring(4, 8);
gatewayManageService.updateGatewayStatus(receiveStr);
if (!redisCache.hasKey(receiveStr)) {
collectionParamsManageService.createDtuCollectionParams();
}
List<CollectionParamsManage> cachedList = redisCache.getCacheObject(receiveStr);
if (cachedList != null) {
deviceCodeParamList = cachedList;
size = deviceCodeParamList.size();
receiveStr = "";
// ✅ 优化4:心跳包重置num为0
num.set(0);
if (size > 0 && idleCount < 2) {
Thread.sleep(200);
sendCollectOrder(ctx);
idleCount++;
} else {
ctx.channel().close();
}
}
}
private void handleDataParsing(ChannelHandlerContext ctx) throws InterruptedException {
idleCount = 1;
if (redisCache.hasKey("order_send_read") && redisCache.hasKey("order_send_register")) {
handleSpecialReadResponse();
} else {
nextSendOrder(ctx);
}
}
private void handleSpecialReadResponse() {
Object orderSendRegister = redisCache.getCacheObject("order_send_register");
String[] split = String.valueOf(orderSendRegister).split("_");
CollectionParamsManage params = new CollectionParamsManage();
params.setDataType(Integer.valueOf(split[4]));
params.setParamType(split[3]);
params.setOtherName(split[5]);
params.setQuality("0");
parseAndSendData(receiveStr, params);
redisCache.deleteObject("order_send_read");
}
private void handleControlResponse(ChannelHandlerContext ctx) throws InterruptedException {
idleCount = 1;
nextSendOrder(ctx);
controlOrder(ctx);
}
private void nextSendOrder(ChannelHandlerContext ctx) throws InterruptedException {
// 先解析当前接收到的数据(如果不是控制响应)
if (receiveStr.length() != 16 && receiveStr.length() < 20) {
parseAndSendData(receiveStr, deviceCodeParamList.get(num.get()));
}
// 清空接收字符串
receiveStr = "";
// 判断是否有远程指令发送
if (redisCache.hasKey("order_send")) {
log.warn("有远程设置指令发送,暂停采集,但保持num递增以维持轮询顺序, currentNum={}", num.get());
// ✅ 优化5:即使有控制指令,也要递增num,避免数据采集断层
incrementNum();
// 不发送下一个采集指令,等待控制指令完成
return;
}
// 正常情况:递增num并发送下一个采集指令
incrementNum();
sendCollectOrder(ctx);
}
/**
* 核心方法使用策略模式解析数据
*/
private void parseAndSendData(String rawData, CollectionParamsManage params) {
try {
if (!parserFactory.hasParser(params.getParamType(), params.getMtType())) {
log.warn("未找到对应的解析器, paramType: {}", params.getParamType());
return;
}
IDeviceParser parser = parserFactory.getParser(params.getParamType(), params.getMtType());
ParseResult result = parser.parse(rawData, params);
if (!result.isSuccess()) {
log.warn("数据解析失败: {}, 原因: {}", rawData, result.getErrorMessage());
return;
}
sendDataToMQ(result, params);
} catch (Exception e) {
log.error("数据解析异常: {}", rawData, e);
}
}
private void sendDataToMQ(ParseResult result, CollectionParamsManage params) {
AdvantechReceiver receiver = new AdvantechReceiver();
receiver.setTs(DateUtils.dateToString(new Date(), Constants.DATE_FORMAT));
List<AdvantechDatas> dataList = new ArrayList<>();
AdvantechDatas data = new AdvantechDatas();
data.setValue(result.getValue());
data.setTag(result.getTagName());
data.setQuality(result.getQuality());
dataList.add(data);
receiver.setD(dataList);
sendMsgByTopic.sendToDeviceMQ(JSONObject.toJSONString(receiver));
// 特殊业务逻辑:贵宾楼水箱温度
if ("2".equals(params.getGatewayId()) && params.getOtherName().contains("实际温度")) {
sendExtraTemperatureData(result.getValue());
}
}
private void sendExtraTemperatureData(BigDecimal temperature) {
AdvantechReceiver receiver = new AdvantechReceiver();
receiver.setTs(DateUtils.dateToString(new Date(), Constants.DATE_FORMAT));
List<AdvantechDatas> dataList = new ArrayList<>();
AdvantechDatas data = new AdvantechDatas();
data.setValue(temperature);
data.setTag("贵宾楼水箱-温度");
data.setQuality(0);
dataList.add(data);
receiver.setD(dataList);
sendMsgByTopic.sendToDeviceMQ(JSONObject.toJSONString(receiver));
}
private void controlOrder(ChannelHandlerContext ctx) {
if (!redisCache.hasKey("order_send")) {
return;
}
Object orderSend = redisCache.getCacheObject("order_send");
String orderSendStr = String.valueOf(orderSend);
String orderSendRegisterStr = "";
if (redisCache.hasKey("order_send_register")) {
Object orderSendRegister = redisCache.getCacheObject("order_send_register");
String[] split = String.valueOf(orderSendRegister).split("_");
orderSendRegisterStr = split[1];
} else {
orderSendRegisterStr = orderSendStr.substring(4, 8);
}
String readOrder = buildReadOrder(orderSendStr, orderSendRegisterStr);
if (readOrder != null && !readOrder.isEmpty()) {
redisCache.setCacheObject("order_send_read", readOrder, 10, TimeUnit.SECONDS);
ctx.writeAndFlush(com.mh.common.utils.ModbusUtils.createByteBuf(readOrder));
log.info("发送读取指令: {}", readOrder);
NettyTools.setReceiveMsg("order_wait", receiveStr);
redisCache.deleteObject("order_send");
// ⚠ 关键修复:不要在这里清空receiveStr,让后续流程处理
// receiveStr = ""; // 删除这行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// ✅ 新增:控制指令发送后,立即恢复采集轮询
resumeCollectionAfterControl(ctx);
}
}
/**
* 控制指令完成后恢复数据采集
* 确保num不会因控制指令而停滞
*/
private void resumeCollectionAfterControl(ChannelHandlerContext ctx) {
// ✅ 优化6:递增num到下一个设备
incrementNum();
log.info("控制指令完成,继续采集下一个设备,num={}, totalCollectCount={}", num.get(), totalCollectCount);
// 延迟后发送下一个采集指令
try {
Thread.sleep(500); // 等待控制指令响应稳定
sendCollectOrder(ctx);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("恢复采集中断", e);
}
}
private String buildReadOrder(String orderSendStr, String registerAddr) {
if (receiveStr.contains(orderSendStr)) {
return com.mh.common.utils.ModbusUtils.createReadOrder(
orderSendStr.substring(0, 2), "03", registerAddr, "1"
);
} else if (receiveStr.substring(0, 8).equals(orderSendStr.substring(0, 8))) {
return com.mh.common.utils.ModbusUtils.createReadOrder(
orderSendStr.substring(0, 2), "03", registerAddr, "2"
);
}
return null;
}
private void dealSession(ChannelHandlerContext ctx) {
String deviceCode = receiveStr + ctx.channel().remoteAddress();
String meterNum = receiveStr;
ServerSession session = new ServerSession(ctx.channel(), deviceCode);
CallbackTaskScheduler.add(new CallbackTask<Boolean>() {
@Override
public Boolean execute() throws Exception {
return action(session, deviceCode, ctx);
}
@Override
public void onBack(Boolean result) {
if (result) {
log.info("设备保存会话: {}", session.getSessionId());
} else {
SessionMap.inst().updateSession(deviceCode, session, meterNum);
}
}
@Override
public void onException(Throwable t) {
log.error("设备登录异常: {}", session.getSessionId(), t);
ServerSession.closeSession(ctx);
}
});
}
private boolean action(ServerSession session, String deviceCode, ChannelHandlerContext ctx) {
checkUser(deviceCode, session);
session.bind();
return true;
}
private boolean checkUser(String deviceCode, ServerSession session) {
if (SessionMap.inst().hasLogin(deviceCode)) {
log.warn("设备已经登录: {}", deviceCode);
return false;
}
return true;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error("通道异常", cause);
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("客户端断开: {}", ctx.channel().remoteAddress());
ctx.close();
}
private String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
/**
* 优化7原子性地递增num处理循环边界
* 使用CAS操作保证线程安全
*/
private void incrementNum() {
int currentNum;
int nextNum;
do {
currentNum = num.get();
nextNum = currentNum + 1;
if (nextNum >= size) {
nextNum = 0; // 循环回到第一个设备
log.debug("num循环重置: {} -> 0, size={}", currentNum, size);
}
} while (!num.compareAndSet(currentNum, nextNum));
// 更新采集统计
lastCollectTime = System.currentTimeMillis();
totalCollectCount++;
log.debug("num递增: {} -> {}, totalCollectCount={}", currentNum, nextNum, totalCollectCount);
}
/**
* 优化8统一的采集指令发送方法
* 包含边界检查和状态验证
*/
private void sendCollectOrder(ChannelHandlerContext ctx) {
if (deviceCodeParamList == null || deviceCodeParamList.isEmpty()) {
log.warn("设备参数列表为空,无法发送采集指令");
return;
}
int currentIndex = num.get();
if (currentIndex < 0 || currentIndex >= size) {
log.error("num索引越界: {}, size={}, 重置为0", currentIndex, size);
num.set(0);
currentIndex = 0;
}
try {
Thread.sleep(1000); // 控制采集频率
SendOrderUtils.sendAllOrder(deviceCodeParamList.get(currentIndex), ctx, currentIndex, size);
log.debug("发送采集指令: num={}, deviceName={}", currentIndex,
deviceCodeParamList.get(currentIndex).getOtherName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("发送采集指令中断", e);
} catch (Exception e) {
log.error("发送采集指令异常, num={}", currentIndex, e);
}
}
/**
* 优化9获取当前采集状态用于监控
*/
public CollectionStats getCollectionStats() {
CollectionStats stats = new CollectionStats();
stats.setCurrentNum(num.get());
stats.setSize(size);
stats.setTotalCollectCount(totalCollectCount);
stats.setLastCollectTime(lastCollectTime);
stats.setDeviceCodeParamListSize(deviceCodeParamList != null ? deviceCodeParamList.size() : 0);
return stats;
}
/**
* 采集状态DTO内部类
*/
@lombok.Data
public static class CollectionStats {
private int currentNum;
private int size;
private int totalCollectCount;
private long lastCollectTime;
private int deviceCodeParamListSize;
}
}

40
mh-system/src/main/java/com/mh/system/service/strategy/DeviceParserFactory.java

@ -0,0 +1,40 @@
package com.mh.system.service.strategy;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class DeviceParserFactory {
@Autowired
private List<IDeviceParser> parsers;
private final Map<String, IDeviceParser> parserMap = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
for (IDeviceParser parser : parsers) {
log.info("注册设备解析器: {}", parser.getParserName());
parserMap.put(parser.getClass().getSimpleName(), parser);
}
}
public IDeviceParser getParser(String paramType, String mtType) {
return parsers.stream()
.filter(parser -> parser.support(paramType, mtType))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未找到支持的解析器, paramType: " + paramType));
}
public boolean hasParser(String paramType, String mtType) {
return parsers.stream()
.anyMatch(parser -> parser.support(paramType, mtType));
}
}

83
mh-system/src/main/java/com/mh/system/service/strategy/HeatPumpParser.java

@ -0,0 +1,83 @@
package com.mh.framework.netty.parser.impl;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.model.response.ParseResult;
import com.mh.common.utils.CRC16;
import com.mh.common.utils.ExchangeStringUtil;
import com.mh.system.service.strategy.IDeviceParser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;
/**
* @project EEMCS
* @description 热泵数据解析器
* @author LJF
* @date 2026-04-28 15:43:49
* @version 1.0
*/
@Slf4j
@Component
public class HeatPumpParser implements IDeviceParser {
private static final List<String> SUPPORTED_PARAM_TYPES =
Arrays.asList("2", "5", "12", "14", "48");
@Override
public ParseResult parse(String rawData, CollectionParamsManage params) {
try {
String dataStr = rawData.replace(" ", "").toUpperCase();
if (!validateCRC(dataStr)) {
log.warn("热泵数据CRC校验失败: {}", rawData);
return ParseResult.fail("CRC校验失败");
}
String data = extractData(dataStr);
int dataType = params.getDataType();
BigDecimal value = convertValue(data, dataType, params.getParamType());
return ParseResult.success(value, params.getOtherName());
} catch (Exception e) {
log.error("热泵数据解析失败", e);
return ParseResult.fail("解析异常: " + e.getMessage());
}
}
@Override
public boolean support(String paramType, String mtType) {
return SUPPORTED_PARAM_TYPES.contains(paramType);
}
private boolean validateCRC(String dataStr) {
String checkStr = dataStr.substring(0, dataStr.length() - 4);
byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr);
int checkNum = CRC16.CRC16_MODBUS(strOrder);
String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum));
checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2);
return checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4));
}
private String extractData(String dataStr) {
return dataStr.substring(dataStr.length() - 8, dataStr.length() - 6)
+ dataStr.substring(dataStr.length() - 6, dataStr.length() - 4);
}
private BigDecimal convertValue(String data, int dataType, String paramType) {
DecimalFormat decimalFormat = new DecimalFormat("0.00");
if (dataType == 3) {
String floatValue = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));
return new BigDecimal(floatValue);
} else if (dataType == 2) {
return new BigDecimal(ExchangeStringUtil.hexToDec(data));
}
throw new IllegalArgumentException("不支持的数据类型: " + dataType);
}
}

23
mh-system/src/main/java/com/mh/system/service/strategy/IDeviceParser.java

@ -0,0 +1,23 @@
package com.mh.system.service.strategy;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.model.response.ParseResult;
/**
* @author LJF
* @version 1.0
* @project EEMCS
* @description 策略接口
* @date 2026-04-28 15:43:49
*/
public interface IDeviceParser {
ParseResult parse(String rawData, CollectionParamsManage params);
boolean support(String paramType, String mtType);
default String getParserName() {
return this.getClass().getSimpleName();
}
}

73
mh-system/src/main/java/com/mh/system/service/strategy/LiquidParser.java

@ -0,0 +1,73 @@
package com.mh.system.service.strategy;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.model.response.ParseResult;
import com.mh.common.utils.CRC16;
import com.mh.common.utils.ExchangeStringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* @project EEMCS
* @description 液位计数据解析器
* @author LJF
* @date 2026-04-28 15:43:49
* @version 1.0
*/
@Slf4j
@Component
public class LiquidParser implements IDeviceParser {
@Override
public ParseResult parse(String rawData, CollectionParamsManage params) {
try {
String dataStr = rawData.replace(" ", "").toUpperCase();
if (!validateCRC(dataStr)) {
log.warn("液位计数据CRC校验失败: {}", rawData);
return ParseResult.fail("CRC校验失败");
}
String data = extractData(dataStr);
BigDecimal value = convertValue(data, params.getDigits());
if (value.intValue() > 100) {
return ParseResult.fail("液位数据超出范围");
}
return ParseResult.success(value, params.getOtherName());
} catch (Exception e) {
log.error("液位计数据解析失败", e);
return ParseResult.fail("解析异常: " + e.getMessage());
}
}
@Override
public boolean support(String paramType, String mtType) {
return "11".equals(paramType);
}
private boolean validateCRC(String dataStr) {
String checkStr = dataStr.substring(0, dataStr.length() - 4);
byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr);
int checkNum = CRC16.CRC16_MODBUS(strOrder);
String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum));
checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2);
return checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4));
}
private String extractData(String dataStr) {
return dataStr.substring(dataStr.length() - 8, dataStr.length() - 4);
}
private BigDecimal convertValue(String data, int digits) {
BigDecimal bigDecimal = new BigDecimal(ExchangeStringUtil.hexToDec(data));
return bigDecimal.divide(
new BigDecimal((int) Math.pow(10, digits)), 3, RoundingMode.HALF_UP
);
}
}

92
mh-system/src/main/java/com/mh/system/service/strategy/MeterParser.java

@ -0,0 +1,92 @@
package com.mh.system.service.strategy;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.model.response.ParseResult;
import com.mh.common.utils.CRC16;
import com.mh.common.utils.ExchangeStringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
/**
* @project EEMCS
* @description 电表数据解析器
* @author LJF
* @date 2026-04-28 15:43:49
* @version 1.0
*/
@Slf4j
@Component
public class MeterParser implements IDeviceParser {
@Override
public ParseResult parse(String rawData, CollectionParamsManage params) {
try {
String dataStr = rawData.replace(" ", "").toUpperCase();
if (!validateCRC(dataStr)) {
log.warn("电表数据CRC校验失败: {}", rawData);
return ParseResult.fail("CRC校验失败");
}
String data = extractData(dataStr);
int dataType = params.getDataType();
BigDecimal value = convertValue(data, dataType, params.getDigits());
if (value.intValue() > 99999999) {
return ParseResult.fail("数据超出范围");
}
return ParseResult.success(value, params.getOtherName());
} catch (Exception e) {
log.error("电表数据解析失败", e);
return ParseResult.fail("解析异常: " + e.getMessage());
}
}
@Override
public boolean support(String paramType, String mtType) {
return "16".equals(paramType);
}
private boolean validateCRC(String dataStr) {
String checkStr = dataStr.substring(0, dataStr.length() - 4);
byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr);
int checkNum = CRC16.CRC16_MODBUS(strOrder);
String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum));
checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2);
return checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4));
}
private String extractData(String dataStr) {
return dataStr.substring(dataStr.length() - 8, dataStr.length() - 6)
+ dataStr.substring(dataStr.length() - 6, dataStr.length() - 4)
+ dataStr.substring(dataStr.length() - 12, dataStr.length() - 10)
+ dataStr.substring(dataStr.length() - 10, dataStr.length() - 8);
}
private BigDecimal convertValue(String data, int dataType, int digits) {
DecimalFormat decimalFormat = new DecimalFormat("0.00");
switch (dataType) {
case 3: // IEEE754浮点型
String floatValue = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));
return new BigDecimal(floatValue);
case 2: // 整型
return new BigDecimal(ExchangeStringUtil.hexToDec(data));
case 4: // 带小数位的整型
String hexData = data.substring(6, 8) + data.substring(4, 6)
+ data.substring(2, 4) + data.substring(0, 2);
BigDecimal bigDecimal = new BigDecimal(ExchangeStringUtil.hexToDec(hexData));
return bigDecimal.divide(
new BigDecimal((int) Math.pow(10, digits)), 2, RoundingMode.HALF_UP
);
default:
throw new IllegalArgumentException("不支持的数据类型: " + dataType);
}
}
}

79
mh-system/src/main/java/com/mh/system/service/strategy/TempHumidityParser.java

@ -0,0 +1,79 @@
package com.mh.system.service.strategy;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.model.response.ParseResult;
import com.mh.common.utils.CRC16;
import com.mh.common.utils.ExchangeStringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* @project EEMCS
* @description 温湿度传感器数据解析器
* @author LJF
* @date 2026-04-28 15:43:49
* @version 1.0
*/
@Slf4j
@Component
public class TempHumidityParser implements IDeviceParser {
@Override
public ParseResult parse(String rawData, CollectionParamsManage params) {
try {
String dataStr = rawData.replace(" ", "").toUpperCase();
if (!validateCRC(dataStr)) {
log.warn("温湿度传感器数据CRC校验失败: {}", rawData);
return ParseResult.fail("CRC校验失败");
}
String data = extractData(dataStr);
BigDecimal value = convertValue(data, params.getParamType(), params.getDigits());
return ParseResult.success(value, params.getOtherName());
} catch (Exception e) {
log.error("温湿度传感器数据解析失败", e);
return ParseResult.fail("解析异常: " + e.getMessage());
}
}
@Override
public boolean support(String paramType, String mtType) {
return "13".equals(paramType) || "35".equals(paramType)
|| ("12".equals(paramType) && "8".equals(mtType));
}
private boolean validateCRC(String dataStr) {
String checkStr = dataStr.substring(0, dataStr.length() - 4);
byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr);
int checkNum = CRC16.CRC16_MODBUS(strOrder);
String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum));
checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2);
return checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4));
}
private String extractData(String dataStr) {
return dataStr.substring(dataStr.length() - 8, dataStr.length() - 4);
}
private BigDecimal convertValue(String data, String paramType, int digits) {
BigDecimal bigDecimal = new BigDecimal(ExchangeStringUtil.hexToDec(data));
if ("12".equals(paramType)) {
bigDecimal = bigDecimal.multiply(new BigDecimal(550))
.divide(new BigDecimal(1650), 10, RoundingMode.HALF_UP)
.subtract(new BigDecimal(40));
} else if ("35".equals(paramType)) {
bigDecimal = bigDecimal.multiply(new BigDecimal(100));
}
return bigDecimal.divide(
new BigDecimal((int) Math.pow(10, digits)), 3, RoundingMode.HALF_UP
);
}
}

90
mh-system/src/main/java/com/mh/system/service/strategy/WaterParser.java

@ -0,0 +1,90 @@
package com.mh.system.service.strategy;
import com.mh.common.core.domain.entity.CollectionParamsManage;
import com.mh.common.model.response.ParseResult;
import com.mh.common.utils.CRC16;
import com.mh.common.utils.ExchangeStringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
/**
* @project EEMCS
* @description 水表数据解析
* @author LJF
* @date 2026-04-28 15:43:49
* @version 1.0
*/
@Slf4j
@Component
public class WaterParser implements IDeviceParser {
@Override
public ParseResult parse(String rawData, CollectionParamsManage params) {
try {
String dataStr = rawData.replace(" ", "").toUpperCase();
if (!validateCRC(dataStr)) {
log.warn("水表数据CRC校验失败: {}", rawData);
return ParseResult.fail("CRC校验失败");
}
String data = extractData(dataStr);
int dataType = params.getDataType();
BigDecimal value = convertValue(data, dataType, params.getDigits());
if (value.intValue() > 99999999) {
return ParseResult.fail("数据超出范围");
}
return ParseResult.success(value, params.getOtherName());
} catch (Exception e) {
log.error("水表数据解析失败", e);
return ParseResult.fail("解析异常: " + e.getMessage());
}
}
@Override
public boolean support(String paramType, String mtType) {
return "18".equals(paramType);
}
private boolean validateCRC(String dataStr) {
String checkStr = dataStr.substring(0, dataStr.length() - 4);
byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(checkStr);
int checkNum = CRC16.CRC16_MODBUS(strOrder);
String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum));
checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2);
return checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4));
}
private String extractData(String dataStr) {
return dataStr.substring(dataStr.length() - 8, dataStr.length() - 6)
+ dataStr.substring(dataStr.length() - 6, dataStr.length() - 4)
+ dataStr.substring(dataStr.length() - 12, dataStr.length() - 10)
+ dataStr.substring(dataStr.length() - 10, dataStr.length() - 8);
}
private BigDecimal convertValue(String data, int dataType, int digits) {
DecimalFormat decimalFormat = new DecimalFormat("0.00");
switch (dataType) {
case 3: // IEEE754浮点型
String floatValue = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));
return new BigDecimal(floatValue);
case 2: // 累计读数
String hexData = data.substring(6, 8) + data.substring(4, 6)
+ data.substring(2, 4) + data.substring(0, 2);
BigDecimal bigDecimal = new BigDecimal(ExchangeStringUtil.hexToDec(hexData));
return bigDecimal.divide(
new BigDecimal((int) Math.pow(10, digits)), 2, RoundingMode.HALF_UP
);
default:
throw new IllegalArgumentException("不支持的数据类型: " + dataType);
}
}
}
Loading…
Cancel
Save