diff --git a/mh-admin/src/main/java/com/mh/MHRunner.java b/mh-admin/src/main/java/com/mh/MHRunner.java index 9338c21..59c922c 100644 --- a/mh-admin/src/main/java/com/mh/MHRunner.java +++ b/mh-admin/src/main/java/com/mh/MHRunner.java @@ -1,14 +1,19 @@ package com.mh; +import com.mh.common.core.domain.entity.GatewayManage; import com.mh.common.core.domain.entity.MqttSubscription; import com.mh.common.utils.StringUtils; import com.mh.framework.mqtt.service.IMqttTopicService; +import com.mh.framework.netty.EchoServer; +import com.mh.system.service.device.ICollectionParamsManageService; +import com.mh.system.service.device.IGatewayManageService; import com.mh.system.service.mqtt.IMqttSubscriptionService; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import java.util.List; +import java.util.stream.Collectors; /** * @author LJF @@ -24,15 +29,41 @@ public class MHRunner implements ApplicationRunner { private final IMqttTopicService iMqttTopicService; - public MHRunner(IMqttSubscriptionService iMqttSubscriptionService, IMqttTopicService iMqttTopicService) { + private final ICollectionParamsManageService collectionParamsManageService; + + private final IGatewayManageService gatewayManageService; + + public MHRunner(IMqttSubscriptionService iMqttSubscriptionService, IMqttTopicService iMqttTopicService, ICollectionParamsManageService collectionParamsManageService, IGatewayManageService gatewayManageService) { this.iMqttSubscriptionService = iMqttSubscriptionService; this.iMqttTopicService = iMqttTopicService; + this.collectionParamsManageService = collectionParamsManageService; + this.gatewayManageService = gatewayManageService; } @Override public void run(ApplicationArguments args) throws Exception { // 初始化mqtt订阅记录 initializeMqttSubscription(); + // 生成DTU采集参数 + createDtuCollectionParams(); + // 启动netty服务端 + startNettyServer(); + } + + private void startNettyServer() { + List gatewayManages = gatewayManageService.selectGwManageList(new GatewayManage()); + if (gatewayManages != null && !gatewayManages.isEmpty()) { + // 根据端口号分组 + gatewayManages.stream().collect(Collectors.groupingBy(GatewayManage::getPort)).forEach((k, v) -> { + // 启动网关 + GatewayManage gatewayManage = v.getFirst(); + new Thread(() -> new EchoServer(gatewayManage.getPort()).start()).start(); + }); + } + } + + private void createDtuCollectionParams() { + collectionParamsManageService.createDtuCollectionParams(); } /** diff --git a/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java b/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java index 90e962d..a902d1a 100644 --- a/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java +++ b/mh-admin/src/main/java/com/mh/web/controller/device/OperationController.java @@ -99,7 +99,7 @@ public class OperationController extends BaseController { String name = mhConfig.getName(); // 获取mqtt操作队列(后期通过mqtt队列配置发送主题) log.info("发送主题:{},消息:{}", name + "/"+ controlTopic, sendOrder); -// iMqttGatewayService.publish(name + "/"+ controlTopic, sendOrder, 1); + iMqttGatewayService.publish(name + "/"+ controlTopic, sendOrder, 1); } catch (Exception e) { log.error("设备操作失败", e); return AjaxResult.error(); diff --git a/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java b/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java index 0de5633..5063052 100644 --- a/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java +++ b/mh-common/src/main/java/com/mh/common/core/domain/entity/GatewayManage.java @@ -35,6 +35,7 @@ public class GatewayManage extends BaseEntity { private int communicationType; // 通讯类型 private int grade; // 标志位(连接状态) 0:正常;1:不在线;2:异常 private Integer status; // (连接状态) 0:正常;1:不在线;2:异常 + private String heartBeat; // 心跳包 @JsonIgnore @TableField(exist = false) @@ -47,6 +48,14 @@ public class GatewayManage extends BaseEntity { @JsonInclude(JsonInclude.Include.NON_EMPTY) private Map params; + public String getHeartBeat() { + return heartBeat; + } + + public void setHeartBeat(String heartBeat) { + this.heartBeat = heartBeat; + } + public Integer getStatus() { return status; } diff --git a/mh-common/src/main/java/com/mh/common/utils/AnalysisReceiveOrder485.java b/mh-common/src/main/java/com/mh/common/utils/AnalysisReceiveOrder485.java new file mode 100644 index 0000000..3f3603e --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/AnalysisReceiveOrder485.java @@ -0,0 +1,277 @@ +package com.mh.common.utils; + +import com.mh.common.core.domain.entity.CollectionParamsManage; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author ljf + * @title : + * @description : 解析485接收的数据 + * @updateTime 2020-04-23 + * @throws : + */ +@Slf4j +public class AnalysisReceiveOrder485 { + + // 调用service + private final SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private final DecimalFormat df = new DecimalFormat("#.##"); + + //解析冷量表 + public void analysisCloudOrder485(final String dataStr1, final CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = dataStr1.replace(" ", "").toUpperCase(); + // 检验报文 + 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); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 + Date date = new Date(); + String dateStr = sdf1.format(date); + ; + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = 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); + + String registerAddr = deviceCodeParam.getRegisterAddr(); + if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { + dateStr = dateStr.substring(0, 17) + "00"; + } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { + dateStr = dateStr.substring(0, 17) + "30"; + } + try { + if (registerAddr.equals("32") || registerAddr.equals("33") || registerAddr.equals("35") || registerAddr.equals("36")) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + log.info("冷量计==>{},寄存器地址==>{},读数==>{}", cloudId, registerAddr, data); + } else if (registerAddr.equals("31") || registerAddr.equals("34")) { + long lData = Long.parseLong(ExchangeStringUtil.hexToDec(data)); + log.info("冷量计==>{},寄存器地址==>{},累计读数==>{}", cloudId, registerAddr, lData); + } + } catch (Exception e) { + log.error("保存冷量计数据失败!", e); + } + } else { + log.info("冷量计校验失败===>{}", dataStr); + } + } + + /** + * 解析水表返回的数据 + * + * @param dataStr1 + */ + public String analysisWaterOrder485(final String dataStr1, final CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = dataStr1.replace(" ", "").toUpperCase(); + // 检验报文 + 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); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 + Date date = new Date(); + String dateStr = sdf1.format(date); + ; + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = 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); + + int dataType = deviceCodeParam.getDataType(); + if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { + dateStr = dateStr.substring(0, 17) + "00"; + System.out.println("插入时间00" + dateStr); + } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { + dateStr = dateStr.substring(0, 17) + "30"; + System.out.println("插入时间30" + dateStr); + } + try { + if (dataType == 3) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + log.info("水表==>{},寄存器地址==>{},读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } else if (dataType == 2) { + data = dataStr.substring(dataStr.length() - 12, dataStr.length() - 10) + + dataStr.substring(dataStr.length() - 10, dataStr.length() - 8) + + dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4); + data = ExchangeStringUtil.hexToDec(data); + BigDecimal bigDecimal = new BigDecimal(data); + bigDecimal = bigDecimal.divide(new BigDecimal((int) Math.pow(10, deviceCodeParam.getDigits()))).setScale(2, RoundingMode.HALF_UP); // 除以1000并保留整数 + data = bigDecimal.toString(); + log.info("水表==>{},寄存器地址==>{},累计读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } + return data; + } catch (Exception e) { + log.error("保存水表数据失败!", e); + } + } else { + log.info("水表===>{}", dataStr); + return ""; + } + return ""; + } + + + /** + * 解析电表返回的数据 + * + * @param dataStr1 + */ + public String analysisMeterOrder485(final String dataStr1, final CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = dataStr1.replace(" ", "").toUpperCase(); + // 检验报文 + 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); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 + Date date = new Date(); + String dateStr = sdf1.format(date); + ; + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = 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); + + int dataType = deviceCodeParam.getDataType(); + if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { + dateStr = dateStr.substring(0, 17) + "00"; + System.out.println("插入时间00" + dateStr); + } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { + dateStr = dateStr.substring(0, 17) + "30"; + System.out.println("插入时间30" + dateStr); + } + try { + if (dataType == 3) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + log.info("电表==>{},寄存器地址==>{},读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } else if (dataType == 2) { + data = ExchangeStringUtil.hexToDec(data); + log.info("电表==>{},寄存器地址==>{},累计读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + } + return data; + } catch (Exception e) { + log.error("保存电表数据失败!", e); + } + } else { + log.info("电表===>{}", dataStr); + return ""; + } + return ""; + } + + public static int dValue(String lastDate) throws ParseException { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date lastTime = format.parse(lastDate); + long min = lastTime.getTime(); + Calendar calendar = Calendar.getInstance(); + long min1 = calendar.getTimeInMillis(); + long subtract = min1 - min; +// System.out.println("相减值: " + subtract/(1000*60)); + return (int) subtract / (1000 * 60); + } + + + // 判断是否存在寄存器地址 + public Boolean queryRegisterAddr(List stringList, String registerAddr) { + boolean flag = false; + for (int i = 0; i < stringList.size(); i++) { + if (stringList.get(i).equalsIgnoreCase(registerAddr)) { + flag = true; + break; + } + } + return flag; + } + + public String analysisHeatPumpOrder485(String receiveStr, CollectionParamsManage deviceCodeParam) { + // 去掉空格 + String dataStr = receiveStr.replace(" ", "").toUpperCase(); + // 检验报文 + 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); + + if (checkWord.equalsIgnoreCase(dataStr.substring(dataStr.length() - 4))) { + //创建SimpleDateFormat对象,指定样式 2019-05-13 22:39:30 + Date date = new Date(); + String dateStr = sdf1.format(date); + + //保留两位小数处理 + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + // 表号 + String cloudId = ExchangeStringUtil.hexToDec(dataStr.substring(0, 2)); + // 读数 + String data = ""; + data = dataStr.substring(dataStr.length() - 8, dataStr.length() - 6) + + dataStr.substring(dataStr.length() - 6, dataStr.length() - 4); + + int dataType = deviceCodeParam.getDataType(); + if (ExchangeStringUtil.isInDate(date, "00:00:00", "00:00:30")) { + dateStr = dateStr.substring(0, 17) + "00"; + System.out.println("插入时间00" + dateStr); + } else if (ExchangeStringUtil.isInDate(date, "00:00:30", "00:00:59")) { + dateStr = dateStr.substring(0, 17) + "30"; + System.out.println("插入时间30" + dateStr); + } + try { + if (dataType == 3) { + data = decimalFormat.format(Math.abs(ExchangeStringUtil.hexToSingle(data)));//十六进制字符串转IEEE754浮点型 + } else if (dataType == 2 && (deviceCodeParam.getParamType().equals("5") + || deviceCodeParam.getParamType().equals("2") + || deviceCodeParam.getParamType().equals("12") + || deviceCodeParam.getParamType().equals("14") + )) { + data = ExchangeStringUtil.hexToDec(data); + } + log.info("热泵==>{},寄存器地址==>{},读数==>{}", cloudId, deviceCodeParam.getRegisterAddr(), data); + return data; + } catch (Exception e) { + log.error("保存热泵数据失败!", e); + } + } else { + log.info("热泵===>{}", dataStr); + return ""; + } + return ""; + } +} diff --git a/mh-common/src/main/java/com/mh/common/utils/CRC16.java b/mh-common/src/main/java/com/mh/common/utils/CRC16.java new file mode 100644 index 0000000..c8b9384 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/CRC16.java @@ -0,0 +1,253 @@ +package com.mh.common.utils; + +/** + * CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或 + * CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或 + * CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0xffff,低位在前,高位在后,结果与0xFFFF异或 + * CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或 + * CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + * CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或 + * CRC16_DNP:多项式x16+x13+x12+x11+x10+x8+x6+x5+x2+1(0x3D65),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + *

+ * (1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器; + * (2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器,高八位数据不变; + * (3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位; + * (4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或; + * (5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理; + * (6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理; + * (7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换; + * (8)、最后得到的CRC寄存器内容即为:CRC码。 + *

+ * 以上计算步骤中的多项式0xA001是0x8005按位颠倒后的结果。 + * 0x8408是0x1021按位颠倒后的结果。 + * 在线校验工具 + * http://www.ip33.com/crc.html + * https://blog.csdn.net/htmlxx/article/details/17369105 + *

+ * Author:Water + * Time:2018/11/19 0019 15:03 + */ +public class CRC16 { + + /** + * CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * 0x8408是0x1021按位颠倒后的结果。 + * + * @param buffer + * @return + */ + public static int CRC16_CCITT(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0x8408; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } +// wCRCin=(wCRCin<<8)|(wCRCin>>8); +// wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + + } + + /** + * CRC-CCITT (0xFFFF) + * CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或 + * + * @param buffer + * @return + */ + public static int CRC16_CCITT_FALSE(byte[] buffer) { + int wCRCin = 0xffff; + int wCPoly = 0x1021; + for (byte b : buffer) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((wCRCin >> 15 & 1) == 1); + wCRCin <<= 1; + if (c15 ^ bit) + wCRCin ^= wCPoly; + } + } + wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + } + + /** + * CRC-CCITT (XModem) + * CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或 + * + * @param buffer + * @return + */ + public static int CRC16_XMODEM(byte[] buffer) { + int wCRCin = 0x0000; // initial value 65535 + int wCPoly = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + for (byte b : buffer) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((wCRCin >> 15 & 1) == 1); + wCRCin <<= 1; + if (c15 ^ bit) + wCRCin ^= wCPoly; + } + } + wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + } + + + /** + * CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0xffff,低位在前,高位在后,结果与0xFFFF异或 + * 0x8408是0x1021按位颠倒后的结果。 + * + * @param buffer + * @return + */ + public static int CRC16_X25(byte[] buffer) { + int wCRCin = 0xffff; + int wCPoly = 0x8408; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC-16 (Modbus) + * CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_MODBUS(byte[] buffer) { + int wCRCin = 0xffff; + int POLYNOMIAL = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= POLYNOMIAL; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x0000; + } + + /** + * CRC-16 + * CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_IBM(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x0000; + } + + /** + * CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_MAXIM(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或 + * 0xA001是0x8005按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_USB(byte[] buffer) { + int wCRCin = 0xFFFF; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC16_DNP:多项式x16+x13+x12+x11+x10+x8+x6+x5+x2+1(0x3D65),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或 + * 0xA6BC是0x3D65按位颠倒后的结果 + * + * @param buffer + * @return + */ + public static int CRC16_DNP(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xA6BC; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } +} \ No newline at end of file diff --git a/mh-common/src/main/java/com/mh/common/utils/ExchangeStringUtil.java b/mh-common/src/main/java/com/mh/common/utils/ExchangeStringUtil.java new file mode 100644 index 0000000..3e56eb9 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/ExchangeStringUtil.java @@ -0,0 +1,1350 @@ +package com.mh.common.utils; + +import com.alibaba.fastjson2.JSONObject; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import org.apache.commons.codec.binary.Hex; + +import java.io.*; +import java.math.BigInteger; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.mh.common.utils.sign.Base64.encode; + +/** + * @Auther: LJF + * @Date: 2020-02-26 15:11 + * @Description: + */ +public class ExchangeStringUtil { + + public static void main(String args[]) { + +// int ieee754Int = Integer.parseInt(str, 16); +// float realValue = Float.intBitsToFloat(ieee754Int); +// System.out.println(realValue); +// +// SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +// Date date=new Date(); +// String curDate=sdf1.format(date); +// System.out.println(curDate); +// String m=curDate.substring(0,15)+"0"; +// System.out.println(m); +// try{ +// getTodayWeather1("广州"); +// }catch (Exception e){ +// +// } + +// String str=ExchangeStringUtil.dateRoll(1,""); +// System.out.println("当前时间前1小时"+str); +// str=ExchangeStringUtil.dateRoll(2,""); +// System.out.println("当前时间前2小时"+str); +// str=ExchangeStringUtil.dateTime(5); +// System.out.println("当前时间前5分钟"+str); +// str=ExchangeStringUtil.dateTime(15); +// System.out.println("当前时间前15分钟"+str); +// str=ExchangeStringUtil.dateTime(30); +// System.out.println("当前时间前30分钟"+str); + System.out.println("--------------------------------------------------"); + } + + /** + * 获取到对应的buffer + * + * @param ctx + * @param sendStr + * @return + */ + public static ByteBuf getByteBuf(ChannelHandlerContext ctx, String sendStr) { + // byte类型的数据 +// byte[] bytes = "这里是将要写往服务端的数据".getBytes(Charset.forName("utf-8")); +// String sendStr = "5803004900021914"; // 冷量计 + // 申请一个数据结构存储信息 + ByteBuf buffer = ctx.alloc().buffer(); + // 将信息放入数据结构中 + buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制 + return buffer; + } + + /** + * double转换为String :当为整数时,只显示整数,当小数时直接显示小数 + * + * @param num + * @return + */ + public static String doubleTrans1(double num) { + if (num % 1.0 == 0) { + return String.valueOf((long) num); + } + return String.valueOf(num); + } + + /** + * 获取String中的数值 + * + * @param result + * @return + */ + public static String getNumFromString(String result) { + String regEx = "[^0-9]"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(result); + return m.replaceAll("").trim(); + } + + /** + * 获取检验位值,update by ljf on 2020-06-02 + * + * @param result + * @return + */ + //返回指令+校验码 + public static String getCRCStr(String result) { + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(result); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = ExchangeStringUtil.addZeroForNum(checkWord, 4); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + result = result + checkWord; + return result; + } + + //只返回校验码 + public static String getStrCRC16(String result) { + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(result); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = ExchangeStringUtil.addZeroForNum(checkWord, 4); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + return checkWord; + } + + public static String getMidString(String str, String beginStr, String endStr) { + String regex = beginStr + "(.*)" + endStr; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(str); + String result = ""; + while (matcher.find()) { + result = matcher.group(1); + } + return result; + } + + /** + * 字符串不足补“0” + * + * @param str + * @param strLength + * @return + */ + public static String addZeroForNum(String str, int strLength) { + int strLen = str.length(); + if (strLen < strLength) { + while (strLen < strLength) { + StringBuffer sb = new StringBuffer(); + sb.append("0").append(str);// 左补0 + // sb.append(str).append("0");//右补0 + str = sb.toString(); + strLen = str.length(); + } + } + return str; + } + + /** + * 字符串不足右边补“0” + * + * @param str + * @param strLength + * @return + */ + public static String rightAddZeroForNum(String str, int strLength) { + int strLen = str.length(); + if (strLen < strLength) { + while (strLen < strLength) { + StringBuffer sb = new StringBuffer(); +// sb.append("0").append(str);// 左补0 + sb.append(str).append("0");//右补0 + str = sb.toString(); + strLen = str.length(); + } + } + return str; + } + + /** + * ip地址转换成16进制long + * + * @param ipString + * @return + */ + public static String ipToHex(String ipString) { + String[] strings = ipString.split("\\."); + String result = ""; + for (int i = 0; i < strings.length; i++) { + result = result + ExchangeStringUtil.decToHex(strings[i]); + } + return result; + } + + /** + * ip地址转换成16进制long + * + * @param ipString + * @return + */ + public static Long ipToLong(String ipString) { + Long[] ip = new Long[4]; + int pos1 = ipString.indexOf("."); + int pos2 = ipString.indexOf(".", pos1 + 1); + int pos3 = ipString.indexOf(".", pos2 + 1); + ip[0] = Long.parseLong(ipString.substring(0, pos1)); + ip[1] = Long.parseLong(ipString.substring(pos1 + 1, pos2)); + ip[2] = Long.parseLong(ipString.substring(pos2 + 1, pos3)); + ip[3] = Long.parseLong(ipString.substring(pos3 + 1)); + return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]; + } + + public static String splitData(String str, String strStart, String strEnd) { + String tempStr; + tempStr = str.substring(str.indexOf(strStart) + 1, str.lastIndexOf(strEnd)); + return tempStr; + } + + public static String endData(String str, String strStart) { + String tempStr; + String str1 = str.substring(0, str.indexOf(strStart)); + tempStr = str.substring(str1.length() + 1); + return tempStr; + } + + // 转换位置 + public static String changePosition(String changeStr) { + StringBuffer s1 = new StringBuffer(); + for (int i = changeStr.length(); i >= 2; i = i - 2) { + s1 = s1.append(changeStr.substring(i - 2, i)); + } + return s1.toString(); + } + + // 加33 + public static String addThree(String data) { + String result = ""; + for (int i = 0; i < data.length() / 2; i++) { + BigInteger a = new BigInteger(data.substring(2 * i, 2 * (i + 1)), 16); + BigInteger b = new BigInteger("33", 16); + BigInteger c = a.add(b); + result = result + ExchangeStringUtil.decToHex(c.toString(10)); + } + + return result; + } + + // 减33 + public static String cutThree(String data) { + String result = ""; + for (int i = 0; i < data.length() / 2; i++) { + BigInteger a = new BigInteger(data.substring(2 * i, 2 * (i + 1)), 16); + BigInteger b = new BigInteger("33", 16); + BigInteger c = a.subtract(b); + result = result + ExchangeStringUtil.decToHex(c.toString(10)); + } + + return result; + } + + public static String makeChecksum(String data) { + if (data == null || data.equals("")) { + return ""; + } + int total = 0; + int len = data.length(); + int num = 0; + while (num < len) { + String s = data.substring(num, num + 2); + total += Integer.parseInt(s, 16); + num = num + 2; + } + /** + * 用256求余最大是255,即16进制的FF + */ + int mod = total % 256; + String hex = Integer.toHexString(mod); + len = hex.length(); + // 如果不够校验位的长度,补0,这里用的是两位校验 + if (len < 2) { + hex = "0" + hex; + } + return hex; + } + + /** + * 十进制数据转换为十六进制字符串数 + * + * @param dec + * @return + */ + public static String decToHex(String dec) { + BigInteger data = new BigInteger(dec, 10); + String result = data.toString(16); + if (result.length() < 2) { + result = "0" + result; + } + if ((result.length() % 2) != 0) { + result = "0" + result; + } + return result.toUpperCase(); + } + + /** + * 十六进制数据转换为十进制字符串数 + * + * @param hex + * @return + */ + public static String hexToDec(String hex) { + BigInteger data = new BigInteger(hex, 16); + return data.toString(10); + } + + public static String IntToHex(int n) { + char[] ch = new char[20]; + int nIndex = 0; + while (true) { + int m = n / 16; + int k = n % 16; + if (k == 15) + ch[nIndex] = 'F'; + else if (k == 14) + ch[nIndex] = 'E'; + else if (k == 13) + ch[nIndex] = 'D'; + else if (k == 12) + ch[nIndex] = 'C'; + else if (k == 11) + ch[nIndex] = 'B'; + else if (k == 10) + ch[nIndex] = 'A'; + else + ch[nIndex] = (char) ('0' + k); + nIndex++; + if (m == 0) + break; + n = m; + } + StringBuffer sb = new StringBuffer(); + sb.append(ch, 0, nIndex); + sb.reverse(); + String strHex = new String(""); + strHex += sb.toString(); + return strHex; + } + + // 字节数组转字符串 + public static String printHexString(byte[] b) { + + StringBuffer sbf = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + String hex = Integer.toHexString(b[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sbf.append(hex.toUpperCase() + " "); + } + return sbf.toString().trim(); + } + + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 字符串转化成为16进制字符串 + * + * @param s + * @return + */ + public static String strTo16(String s) { + String str = ""; + for (int i = 0; i < s.length(); i++) { + int ch = (int) s.charAt(i); + String s4 = Integer.toHexString(ch); + str = str + s4; + } + return str; + } + + /** + * 字符串转换成为16进制(无需Unicode编码) + * + * @param str + * @return + */ + public static String str2HexStr(String str) { + char[] chars = "0123456789ABCDEF".toCharArray(); + StringBuilder sb = new StringBuilder(""); + byte[] bs = str.getBytes(); + int bit; + for (int i = 0; i < bs.length; i++) { + bit = (bs[i] & 0x0f0) >> 4; + sb.append(chars[bit]); + bit = bs[i] & 0x0f; + sb.append(chars[bit]); + // sb.append(' '); + } + return sb.toString().trim(); + } + + /** + * 16进制转换成为string类型字符串 + * + * @param s + * @return + */ + public static String hexStringToString(String s) { + if (s == null || s.equals("")) { + return null; + } + s = s.replace(" ", ""); + byte[] baKeyword = new byte[s.length() / 2]; + for (int i = 0; i < baKeyword.length; i++) { + try { + baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16)); + } catch (Exception e) { + e.printStackTrace(); + } + } + try { + s = new String(baKeyword, "UTF-8"); + new String(); + } catch (Exception e1) { + e1.printStackTrace(); + } + return s; + } + + + /** + * 字节转十六进制 + * + * @param b 需要进行转换的byte字节 + * @return 转换后的Hex字符串 + */ + public static String byteToHex(byte b) { + String hex = Integer.toHexString(b & 0xFF); + if (hex.length() < 2) { + hex = "0" + hex; + } + return hex; + } + + /** + * hex字符串转byte数组 + * + * @param inHex 待转换的Hex字符串 + * @return 转换后的byte数组结果 + */ + public static byte[] hexToByteArray(String inHex) { + int hexlen = inHex.length(); + byte[] result; + if (hexlen % 2 == 1) { + //奇数 + hexlen++; + result = new byte[(hexlen / 2)]; + inHex = "0" + inHex; + } else { + //偶数 + result = new byte[(hexlen / 2)]; + } + int j = 0; + for (int i = 0; i < hexlen; i += 2) { + result[j] = (byte) Integer.parseInt(inHex.substring(i, i + 2), 16); + j++; + } + return result; + } + + /** + * 将十六进制的字符串转换成字节数组 + * + * @param hexString + * @return + */ + public static byte[] hexStrToBinaryStr(String hexString) { + + hexString = hexString.replaceAll(" ", ""); + + int len = hexString.length(); + int index = 0; + + + byte[] bytes = new byte[len / 2]; + + while (index < len) { + + String sub = hexString.substring(index, index + 2); + + bytes[index / 2] = (byte) Integer.parseInt(sub, 16); + + index += 2; + } + + + return bytes; + } + + /** + * 获取当前日期 : "yyyy-MM-dd HH:mm:ss" + * + * @return "yyyy-MM-dd HH:mm:ss"字符串 + */ + public static String formatDateStr_ss() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + } + + /** + * 字符串是否为空 + *

+ * 如果这个字符串为null或者trim后为空字符串则返回true,否则返回false。 + * + * @param str + * @return + */ + public static boolean isEmpty(String str) { + if (str == null || "".equals(str.trim())) + return true; + return false; + } + + /** + * 用来把mac字符串转换为long + * + * @param strMac + * @return + */ + public static long macToLong(String strMac) { + byte[] mb = new BigInteger(strMac, 16).toByteArray(); + ByteBuffer mD = ByteBuffer.allocate(mb.length); + mD.put(mb); + long mac = 0; + // 如果长度等于8代表没有补0; + if (mD.array().length == 8) { + mac = mD.getLong(0); + } else if (mD.array().length == 9) { + mac = mD.getLong(1); + } + return mac; + } + + public static byte[] getBytes(Object obj) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bout); + out.writeObject(obj); + out.flush(); + byte[] bytes = bout.toByteArray(); + bout.close(); + out.close(); + + return bytes; + } + + public static Object getObject(byte[] bytes) throws IOException, ClassNotFoundException { + ByteArrayInputStream bi = new ByteArrayInputStream(bytes); + ObjectInputStream oi = new ObjectInputStream(bi); + Object obj = oi.readObject(); + bi.close(); + oi.close(); + return obj; + } + + public static ByteBuffer getByteBuffer(Object obj) throws IOException { + byte[] bytes = ExchangeStringUtil.getBytes(obj); + ByteBuffer buff = ByteBuffer.wrap(bytes); + + return buff; + } + + /** + * byte[] 转short 2字节 + * + * @param bytes + * @return + */ + public static short bytesToshort(byte[] bytes) { + return (short) ((bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00)); + + } + + /** + * byte 转Int + * + * @param b + * @return + */ + public static int byteToInt(byte b) { + return (b) & 0xff; + } + + public static int bytesToInt(byte[] bytes) { + int addr = bytes[0] & 0xFF; + addr |= ((bytes[1] << 8) & 0xFF00); + addr |= ((bytes[2] << 16) & 0xFF0000); + addr |= ((bytes[3] << 24) & 0xFF000000); + return addr; + } + + public static byte[] intToByte(int i) { + + byte[] abyte0 = new byte[4]; + abyte0[0] = (byte) (0xff & i); + abyte0[1] = (byte) ((0xff00 & i) >> 8); + abyte0[2] = (byte) ((0xff0000 & i) >> 16); + abyte0[3] = (byte) ((0xff000000 & i) >> 24); + return abyte0; + + } + + public static byte[] LongToByte(Long i) { + + byte[] abyte0 = new byte[8]; + abyte0[0] = (byte) (0xff & i); + abyte0[1] = (byte) ((0xff00 & i) >> 8); + abyte0[2] = (byte) ((0xff0000 & i) >> 16); + abyte0[3] = (byte) ((0xff000000 & i) >> 24); + abyte0[4] = (byte) ((0xff00000000l & i) >> 32); + abyte0[5] = (byte) ((0xff0000000000l & i) >> 40); + abyte0[6] = (byte) ((0xff000000000000l & i) >> 48); + abyte0[7] = (byte) ((0xff00000000000000l & i) >> 56); + return abyte0; + + } + + /** + * 函数名称:shortChange
+ * 功能描述:short 大端转小端 + * + * @param mshort + */ + public static short shortChange(Short mshort) { + + mshort = (short) ((mshort >> 8 & 0xFF) | (mshort << 8 & 0xFF00)); + + return mshort; + } + + /** + * 函数名称:intChange
+ * 功能描述:int 大端转小端 + * + * @param mint + */ + public static int intChange(int mint) { + + mint = (int) (((mint) >> 24 & 0xFF) | ((mint) >> 8 & 0xFF00) | ((mint) << 8 & 0xFF0000) + | ((mint) << 24 & 0xFF000000)); + + return mint; + } + + /** + * 函数名称:intChange
+ * 功能描述:LONG 大端转小端 + * + * @param mlong + */ + public static long longChange(long mlong) { + + mlong = (long) (((mlong) >> 56 & 0xFF) | ((mlong) >> 48 & 0xFF00) | ((mlong) >> 24 & 0xFF0000) + | ((mlong) >> 8 & 0xFF000000) | ((mlong) << 8 & 0xFF00000000l) | ((mlong) << 24 & 0xFF0000000000l) + | ((mlong) << 40 & 0xFF000000000000l) | ((mlong) << 56 & 0xFF00000000000000l)); + + return mlong; + } + + /** + * 将byte转换为无符号的short类型 + * + * @param b 需要转换的字节数 + * @return 转换完成的short + */ + public static short byteToUshort(byte b) { + return (short) (b & 0x00ff); + } + + /** + * 将byte转换为无符号的int类型 + * + * @param b 需要转换的字节数 + * @return 转换完成的int + */ + public static int byteToUint(byte b) { + return b & 0x00ff; + } + + /** + * 将byte转换为无符号的long类型 + * + * @param b 需要转换的字节数 + * @return 转换完成的long + */ + public static long byteToUlong(byte b) { + return b & 0x00ff; + } + + /** + * 将short转换为无符号的int类型 + * + * @param s 需要转换的short + * @return 转换完成的int + */ + public static int shortToUint(short s) { + return s & 0x00ffff; + } + + /** + * 将short转换为无符号的long类型 + * + * @param s 需要转换的字节数 + * @return 转换完成的long + */ + public static long shortToUlong(short s) { + return s & 0x00ffff; + } + + /** + * 将int转换为无符号的long类型 + * + * @param i 需要转换的字节数 + * @return 转换完成的long + */ + public static long intToUlong(int i) { + return i & 0x00ffffffff; + } + + /** + * 将short转换成小端序的byte数组 + * + * @param s 需要转换的short + * @return 转换完成的byte数组 + */ + public static byte[] shortToLittleEndianByteArray(short s) { + return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(s).array(); + } + + /** + * 将int转换成小端序的byte数组 + * + * @param i 需要转换的int + * @return 转换完成的byte数组 + */ + public static byte[] intToLittleEndianByteArray(int i) { + return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(i).array(); + } + + /** + * 将long转换成小端序的byte数组 + * + * @param l 需要转换的long + * @return 转换完成的byte数组 + */ + public static byte[] longToLittleEndianByteArray(long l) { + return ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(l).array(); + } + + /** + * 将short转换成大端序的byte数组 + * + * @param s 需要转换的short + * @return 转换完成的byte数组 + */ + public static byte[] shortToBigEndianByteArray(short s) { + return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putShort(s).array(); + } + + /** + * 将int转换成大端序的byte数组 + * + * @param i 需要转换的int + * @return 转换完成的byte数组 + */ + public static byte[] intToBigEndianByteArray(int i) { + return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putInt(i).array(); + } + + /** + * 将long转换成大端序的byte数组 + * + * @param l 需要转换的long + * @return 转换完成的byte数组 + */ + public static byte[] longToBigEndianByteArray(long l) { + return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putLong(l).array(); + } + + /** + * 将short转换为16进制字符串 + * + * @param s 需要转换的short + * @param isLittleEndian 是否是小端序(true为小端序false为大端序) + * @return 转换后的字符串 + */ + public static String shortToHexString(short s, boolean isLittleEndian) { + byte byteArray[] = null; + if (isLittleEndian) { + byteArray = shortToLittleEndianByteArray(s); + } else { + byteArray = shortToBigEndianByteArray(s); + } + return byteArrayToHexString(byteArray); + } + + /** + * 将int转换为16进制字符串 + * + * @param i 需要转换的int + * @param isLittleEndian 是否是小端序(true为小端序false为大端序) + * @return 转换后的字符串 + */ + public static String intToHexString(int i, boolean isLittleEndian) { + byte byteArray[] = null; + if (isLittleEndian) { + byteArray = intToLittleEndianByteArray(i); + } else { + byteArray = intToBigEndianByteArray(i); + } + return byteArrayToHexString(byteArray); + } + + /** + * 将long转换为16进制字符串 + * + * @param l 需要转换的long + * @param isLittleEndian 是否是小端序(true为小端序false为大端序) + * @return 转换后的字符串 + */ + public static String longToHexString(long l, boolean isLittleEndian) { + byte byteArray[] = null; + if (isLittleEndian) { + byteArray = longToLittleEndianByteArray(l); + } else { + byteArray = longToBigEndianByteArray(l); + } + return byteArrayToHexString(byteArray); + } + + /** + * 将字节数组转换成16进制字符串 + * + * @param array 需要转换的字符串 + * @param toPrint 是否为了打印输出,如果为true则会每4自己添加一个空格 + * @return 转换完成的字符串 + */ +// public static String byteArrayToHexString(byte[] array, boolean toPrint) { +// if (array == null) { +// return "null"; +// } +// StringBuffer sb = new StringBuffer(); +// +// for (int i = 0; i < array.length; i++) { +// sb.append(byteToHex(array[i])); +// if (toPrint && (i + 1) % 4 == 0) { +// sb.append(" "); +// } +// } +// return sb.toString(); +// } + + /** + * 字节数组转换成String,指定长度转换长度 + * + * @param arrBytes + * @param count 转换长度 + * @param blank 要不要空格(每个byte字节,最是否用一个“ ”隔开) + * @return "" | arrBytes换成的字符串(不存在null) + */ + public static String byteArray2HexString(byte[] arrBytes, int count, boolean blank) { + String ret = ""; + if (arrBytes == null || arrBytes.length < 1) + return ret; + if (count > arrBytes.length) + count = arrBytes.length; + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < count; i++) { + ret = Integer.toHexString(arrBytes[i] & 0xFF).toUpperCase(); + if (ret.length() == 1) + builder.append("0").append(ret); + else + builder.append(ret); + if (blank) + builder.append(" "); + } + + return builder.toString(); + + } + + public static String hexStr2Str(String hexStr) { + String string = "0123456789ABCDEF"; + char[] hexs = hexStr.toCharArray(); + byte[] bytes = new byte[hexStr.length() / 2]; + int n; + for (int i = 0; i < bytes.length; i++) { + n = string.indexOf(hexs[2 * i]) * 16; + n += string.indexOf(hexs[2 * i + 1]); + bytes[i] = (byte) (n & 0xff); + } + return new String(bytes); + } + + /** + * 将指定字符串src,以每两个字符分割转换为16进制形式 如:"2B44EFD9" --> byte[]{0x2B, 0x44, 0xEF, 0xD9} + * + * @param src String + * @return null | byte[] + */ + public static byte[] HexString2Bytes(String src) { + // String strTemp = ""; + if (src == null || "".equals(src)) + return null; + StringBuilder builder = new StringBuilder(); + for (char c : src.trim().toCharArray()) { + /* 去除中间的空格 */ + if (c != ' ') { + builder.append(c); + } + } + src = builder.toString(); + byte[] ret = new byte[src.length() / 2]; + byte[] tmp = src.getBytes(); + for (int i = 0; i < src.length() / 2; i++) { + ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]); + } + return ret; + } + + /** + * 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF + * + * @param src0 byte + * @param src1 byte + * @return byte + */ + public static byte uniteBytes(byte src0, byte src1) { + byte _b0 = Byte.decode("0x" + new String(new byte[]{src0})).byteValue(); + _b0 = (byte) (_b0 << 4); + byte _b1 = Byte.decode("0x" + new String(new byte[]{src1})).byteValue(); + byte ret = (byte) (_b0 ^ _b1); + return ret; + } + + /** + * 将字节数组转换成16进制字符串 + * + * @param array 需要转换的字符串(字节间没有分隔符) + * @return 转换完成的字符串 + */ + public static String byteArrayToHexString(byte[] array) { + return byteArray2HexString(array, Integer.MAX_VALUE, false); + } + + /** + * 将字节数组转换成long类型 + * + * @param bytes 字节数据 + * @return long类型 + */ + public static long byteArrayToLong(byte[] bytes) { + return ((((long) bytes[0] & 0xff) << 24) | (((long) bytes[1] & 0xff) << 16) | (((long) bytes[2] & 0xff) << 8) + | (((long) bytes[3] & 0xff) << 0)); + } + + /** + * 合并数组 + * + * @param firstArray 第一个数组 + * @param secondArray 第二个数组 + * @return 合并后的数组 + */ + public static byte[] concat(byte[] firstArray, byte[] secondArray) { + if (firstArray == null || secondArray == null) { + if (firstArray != null) + return firstArray; + if (secondArray != null) + return secondArray; + return null; + } + byte[] bytes = new byte[firstArray.length + secondArray.length]; + System.arraycopy(firstArray, 0, bytes, 0, firstArray.length); + System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length); + return bytes; + } + + //字符转二进制 + public static String toBinary(String str) { + char[] strChar = str.toCharArray(); + String result = ""; + for (int i = 0; i < strChar.length; i++) { + result += Integer.toBinaryString(strChar[i]) + " "; + } + System.out.println(result); + return result; + } + + /** + * 将16进制转换为二进制 + * + * @param hexString + * @return + */ + public static String hexString2binaryString(String hexString) { + //16进制转10进制 + BigInteger sint = new BigInteger(hexString, 16); + //10进制转2进制 + String result = sint.toString(2); + //字符串反转 + //return new StringBuilder(result).reverse().toString(); + return result; + } + + //将十六进制转换为二进制 + public static byte[] parseHexStr2Byte(String str) { + if (str.length() < 1) + return null; + byte[] result = new byte[str.length() / 2]; + for (int i = 0; i < str.length() / 2; i++) { + int high = Integer.parseInt(str.substring(i * 2, i * 2 + 1), 16); + int low = Integer.parseInt(str.substring(i * 2 + 1, i * 2 + 2), 16); + result[i] = (byte) (high * 16 + low); + } + return result; + } + + //将二进制字符转换为十六进制 + public static String parseByte2HexStr(String str) { + int i = Integer.parseInt(str, 2); //将二进制转为十进制 + String j = Integer.toHexString(i); //将十进制转为十六进制 + return j; + } + + //将二进字节数组制转换为十六进制 + public static String parseByte2HexStr(byte binary[]) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < binary.length; i++) { + String hex = Integer.toHexString(binary[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sb.append(hex.toUpperCase()); + } + return sb.toString(); + } + + /** + * IEEE754字符串转十六进制字符串 + * + * @param f + * @author: 若非 + * @date: 2021/9/10 16:57 + */ + public static String singleToHex(float f) { + int i = Float.floatToIntBits(f); + String hex = Integer.toHexString(i); + return hex; + } + + /** + * 十六进制字符串转IEEE754浮点型 + * + * @param s + * @author: 若非 + * @date: 2021/9/10 16:57 + */ + public static float hexToSingle(String s) { + BigInteger data = new BigInteger(s, 16); + return Float.intBitsToFloat(data.intValue()); + } + + //Base64转十六进制字符串 + public static String base64ToHex(String s) { + try { + //解码 + byte[] x = Base64.getDecoder().decode(s); + return Hex.encodeHexString(x); + } catch (Exception e) { + return ""; + } + } + + //十六进制转Base64 + public static String hexToBase64(String s) { + try { + //编码 + return encode(hexStringToByteArray(s)); + } catch (Exception e) { + return ""; + } + } + + //hex进制转byte[] + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + /** + * 获取实时天气2
+ * 方 法 名: getTodayWeather
+ * + * @param Cityid 城市编码 + */ + public static Map getTodayWeather1(String Cityid) + throws IOException, NullPointerException { + // 连接中央气象台的API + String url1 = "https://free-api.heweather.net/s6/weather/now?location=" + Cityid + "&key=3c3fa198cacc4152b94b20def11b2455"; + + URL url = new URL(url1); + URLConnection connectionData = url.openConnection(); + connectionData.setConnectTimeout(1000); + Map map = new HashMap(); + try { + BufferedReader br = new BufferedReader(new InputStreamReader( + connectionData.getInputStream(), "UTF-8")); + StringBuilder sb = new StringBuilder(); + String line = null; + while ((line = br.readLine()) != null) + sb.append(line); + String datas = sb.toString(); + //截取[]转化为json格式 + datas = datas.replace(datas.substring(datas.indexOf(":") + 1, datas.indexOf(":") + 2), ""); + datas = datas.replace(datas.substring(datas.length() - 2, datas.length() - 1), ""); + JSONObject jsonData = JSONObject.parseObject(datas); + JSONObject info = jsonData.getJSONObject("HeWeather6"); + JSONObject jsonData1 = JSONObject.parseObject(info.getString("basic").toString()); + JSONObject jsonData2 = JSONObject.parseObject(info.getString("update").toString()); + JSONObject jsonData3 = JSONObject.parseObject(info.getString("now").toString()); + map.put("location", jsonData1.getString("location").toString()); + map.put("parent_city", jsonData1.getString("parent_city").toString()); + map.put("admin_area", jsonData1.getString("admin_area").toString()); + map.put("cnty", jsonData1.getString("cnty").toString()); + + String time = jsonData2.getString("loc").toString(); + + String week = strToDate(time); + + map.put("week", week); + map.put("time", jsonData2.getString("loc").toString()); + + map.put("tmp", jsonData3.getString("tmp").toString()); + map.put("wind_dir", jsonData3.getString("wind_dir").toString()); + map.put("cond_txt", jsonData3.getString("cond_txt").toString()); + map.put("cond_code", jsonData3.getString("cond_code").toString()); + System.out.println(map); + } catch (SocketTimeoutException e) { + System.out.println("连接超时"); + } catch (FileNotFoundException e) { + System.out.println("加载文件出错"); + } catch (ParseException e) { + e.printStackTrace(); + } finally { + //关闭流 +// try { +// if(br!=null){ +// br.close(); +// } +// +// } catch ( Exception e) { +// e.printStackTrace(); +// } + } + return map; + } + + /** + * 时间获得星期 + * + * @param strDate + * @return + * @throws ParseException + */ + public static String strToDate(String strDate) throws ParseException { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Calendar c = Calendar.getInstance(); + c.setTime(formatter.parse(strDate)); + SimpleDateFormat sdf = new SimpleDateFormat("EEEE"); + String week = sdf.format(c.getTime()); + return week; + } + + /** + * 字符集转码 + * + * @param url + * @return + * @throws UnsupportedEncodingException + */ + public static String urlEncode(String url) throws UnsupportedEncodingException { + if (url == null) { + return null; + } + + final String reserved_char = ";/?:@=&"; + String ret = ""; + for (int i = 0; i < url.length(); i++) { + String cs = String.valueOf(url.charAt(i)); + if (reserved_char.contains(cs)) { + ret += cs; + } else { + ret += URLEncoder.encode(cs, "utf-8"); + } + } + return ret.replace("+", "%20"); + } + + + /** + * 判断时间是否在时间段内 + * + * @param date 当前时间 yyyy-MM-dd HH:mm:ss + * @param strDateBegin 开始时间 00:00:00 + * @param strDateEnd 结束时间 00:05:00 + * @return + */ + public static boolean isInDate(Date date, String strDateBegin, + String strDateEnd) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String strDate = sdf.format(date); + // 截取当前时间时分秒 + int strDateH = Integer.parseInt(strDate.substring(11, 13)); + int strDateM = Integer.parseInt(strDate.substring(14, 16)); + int strDateS = Integer.parseInt(strDate.substring(17, 19));// + // 截取开始时间时分秒 + int strDateBeginH = Integer.parseInt(strDateBegin.substring(0, 2)); + int strDateBeginM = Integer.parseInt(strDateBegin.substring(3, 5)); + int strDateBeginS = Integer.parseInt(strDateBegin.substring(6, 8));// + // 截取结束时间时分秒 + int strDateEndH = Integer.parseInt(strDateEnd.substring(0, 2)); + int strDateEndM = Integer.parseInt(strDateEnd.substring(3, 5)); + int strDateEndS = Integer.parseInt(strDateEnd.substring(6, 8));// + +// if ((strDateH >= strDateBeginH && strDateH <= strDateEndH)) { +// // 当前时间小时数在开始时间和结束时间小时数之间 +// if (strDateH > strDateBeginH && strDateH < strDateEndH) { +// return true; +// // 当前时间小时数等于开始时间小时数,分钟数在开始和结束之间 +// } else if (strDateH == strDateBeginH && strDateM >= strDateBeginM +// && strDateM <= strDateEndM) { +// return true; +// // 当前时间小时数等于开始时间小时数,分钟数等于开始时间分钟数,秒数在开始和结束之间 +// } else if (strDateH == strDateBeginH && strDateM == strDateBeginM +// && strDateS >= strDateBeginS && strDateS <= strDateEndS) { +// return true; +// } +// // 当前时间小时数大等于开始时间小时数,等于结束时间小时数,分钟数小等于结束时间分钟数 +// else if (strDateH >= strDateBeginH && strDateH == strDateEndH +// && strDateM <= strDateEndM) { +// return true; +// // 当前时间小时数大等于开始时间小时数,等于结束时间小时数,分钟数等于结束时间分钟数,秒数小等于结束时间秒数 +// } else if (strDateH >= strDateBeginH && strDateH == strDateEndH +// && strDateM == strDateEndM && strDateS <= strDateEndS) { +// return true; +// } else { +// return false; +// } +// } else { +// return false; +// } + if (strDateS >= strDateBeginS && strDateS < strDateEndS) { + return true; + } else { + return false; + } + + } + + /** + * 当前时间向推几小时 + * + * @param ihour 小时 + * @return String + */ + public static String dateRoll(int ihour, String curDate) { + try { + DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + //获取当前时间 + LocalDateTime date = LocalDateTime.now(); + if (curDate.length() > 0) { + date = dateConvertToLocalDateTime(df2.parse(curDate)); + } + //获取当前时间的前几小时时间 + LocalDateTime localDateTime = date.minusHours(ihour); + return df1.format(localDateTime); + } catch (Exception e) { + return ""; + } + } + + //获取当前时间前几分钟 + public static String dateTime(int a, String curDate) { + try { + // 设置传入的时间格式 + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String currentTime = "";//当前时间 + Calendar cal = Calendar.getInstance(); + if (!curDate.isEmpty()) { + Date date = df.parse(curDate); + cal.setTime(date); + } + cal.add(Calendar.MINUTE, -a);// a分钟之前的时间 + //格式化指定形式的时间 + currentTime = df.format(cal.getTime());//获取到完整的时间 + return currentTime; + } catch (Exception e) { + return ""; + } + } + + //将java.util.Date 转换为java8 的java.time.LocalDateTime,默认时区为东8区 + public static LocalDateTime dateConvertToLocalDateTime(Date date) { + return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime(); + } + + + //将java8 的 java.time.LocalDateTime 转换为 java.util.Date,默认时区为东8区 + public static Date localDateTimeConvertToDate(LocalDateTime localDateTime) { + return Date.from(localDateTime.toInstant(ZoneOffset.of("+8"))); + } + +} diff --git a/mh-common/src/main/java/com/mh/common/utils/SendOrderUtils.java b/mh-common/src/main/java/com/mh/common/utils/SendOrderUtils.java new file mode 100644 index 0000000..054ca31 --- /dev/null +++ b/mh-common/src/main/java/com/mh/common/utils/SendOrderUtils.java @@ -0,0 +1,53 @@ +package com.mh.common.utils; + +import com.mh.common.core.domain.entity.CollectionParamsManage; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; + +/** + * @author ljf + * @title : + * @description : 发送指令工具类 + * @updateTime 2021-01-26 + * @throws : + */ +@Slf4j +public class SendOrderUtils { + + // 发送所有类型采集报文 + public static void sendAllOrder(CollectionParamsManage paramsManage, ChannelHandlerContext ctx, int num, int size) { + // 开始创建指令 + String mtCode = paramsManage.getMtCode(); // 采集编号 + String funCode = paramsManage.getFuncCode(); // 功能码 + String registerAddr = paramsManage.getRegisterAddr(); // 寄存器地址 + String registerNum = String.valueOf(paramsManage.getRegisterSize()); // 寄存器数量 + // 拼接指令 + String sendOrderStr = ExchangeStringUtil.addZeroForNum(ExchangeStringUtil.decToHex(mtCode), 2) + + ExchangeStringUtil.addZeroForNum(funCode, 2) + + ExchangeStringUtil.addZeroForNum(registerAddr, 4) + + ExchangeStringUtil.addZeroForNum(registerNum, 4); + byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(sendOrderStr); + int checkNum = CRC16.CRC16_MODBUS(strOrder); + String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); + checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); + sendOrderStr = sendOrderStr + checkWord; + ByteBuf buffer = getByteBuf(ctx, sendOrderStr); + // 发送数据 + ctx.channel().writeAndFlush(buffer); + log.info("sends :" + sendOrderStr + ",num:" + num + ",records:" + size); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + log.error("线程休眠异常", e); + } + } + + private static ByteBuf getByteBuf(ChannelHandlerContext ctx, String sendStr) { + // 申请一个数据结构存储信息 + ByteBuf buffer = ctx.alloc().buffer(); + // 将信息放入数据结构中 + buffer.writeBytes(ExchangeStringUtil.hexStrToBinaryStr(sendStr));//对接需要16进制 + return buffer; + } +} diff --git a/mh-framework/src/main/java/com/mh/framework/dealdata/DataProcessService.java b/mh-framework/src/main/java/com/mh/framework/dealdata/DataProcessService.java index 6fd5177..0764e6d 100644 --- a/mh-framework/src/main/java/com/mh/framework/dealdata/DataProcessService.java +++ b/mh-framework/src/main/java/com/mh/framework/dealdata/DataProcessService.java @@ -37,7 +37,7 @@ public interface DataProcessService { * @param deviceNum * @return */ - String queryInitValue(String deviceNum); + String queryInitValue(String deviceNum, String mtCode); /** * 查询上一次采集数据、时间等参数 diff --git a/mh-framework/src/main/java/com/mh/framework/dealdata/impl/DataProcessServiceImpl.java b/mh-framework/src/main/java/com/mh/framework/dealdata/impl/DataProcessServiceImpl.java index 79b5b4e..9f74ffa 100644 --- a/mh-framework/src/main/java/com/mh/framework/dealdata/impl/DataProcessServiceImpl.java +++ b/mh-framework/src/main/java/com/mh/framework/dealdata/impl/DataProcessServiceImpl.java @@ -151,31 +151,38 @@ public class DataProcessServiceImpl implements DataProcessService { } String timeString = data.getTs(); - OffsetDateTime utcDateTime; - try { - // 尝试多种常见的时间格式 + String formattedTime = ""; + // 判断是否存在TimeZone + if (!timeString.contains("T")) { + formattedTime = timeString; + } else { + OffsetDateTime utcDateTime; try { - utcDateTime = OffsetDateTime.parse(timeString, DateTimeFormatter.ISO_OFFSET_DATE_TIME); - } catch (DateTimeParseException e1) { + // 尝试多种常见的时间格式 try { - utcDateTime = OffsetDateTime.parse(timeString, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[XXX][XX][X]")); - } catch (DateTimeParseException e2) { - utcDateTime = OffsetDateTime.parse(timeString, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[XXX][XX][X]")); + utcDateTime = OffsetDateTime.parse(timeString, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } catch (DateTimeParseException e1) { + try { + utcDateTime = OffsetDateTime.parse(timeString, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[XXX][XX][X]")); + } catch (DateTimeParseException e2) { + utcDateTime = OffsetDateTime.parse(timeString, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[XXX][XX][X]")); + } } + } catch (DateTimeParseException e) { + log.error("时间格式解析异常,时间字符串: {}", timeString, e); + return; } - } catch (DateTimeParseException e) { - log.error("时间格式解析异常,时间字符串: {}", timeString, e); - return; + // 2. 转换为中国时区(UTC+8) + OffsetDateTime chinaDateTime = utcDateTime.withOffsetSameInstant( + ZoneOffset.ofHours(8) + ); + + // 3. 格式化为目标字符串 + formattedTime = chinaDateTime.format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + ); } - // 2. 转换为中国时区(UTC+8) - OffsetDateTime chinaDateTime = utcDateTime.withOffsetSameInstant( - ZoneOffset.ofHours(8) - ); - - // 3. 格式化为目标字符串 - String formattedTime = chinaDateTime.format( - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") - ); + String dString = data.getD().toString(); // 替换掉inf if (dString.contains("inf")) { @@ -227,8 +234,12 @@ public class DataProcessServiceImpl implements DataProcessService { } @Override - public String queryInitValue(String deviceNum) { - return dataProcessMapper.queryInitValue(deviceNum); + public String queryInitValue(String deviceNum, String mtCode) { + if (StringUtils.isEmpty(mtCode)) { + return dataProcessMapper.queryInitValue1(deviceNum); + } else { + return dataProcessMapper.queryInitValue(deviceNum, mtCode); + } } @Override diff --git a/mh-framework/src/main/java/com/mh/framework/netty/EchoServer.java b/mh-framework/src/main/java/com/mh/framework/netty/EchoServer.java new file mode 100644 index 0000000..dc73db8 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/EchoServer.java @@ -0,0 +1,57 @@ +package com.mh.framework.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EchoServer { + + private final int port; + + public EchoServer(int port) { + this.port = port; + } + + public void start() { + // 创建EventLoopGroup + NioEventLoopGroup bossGroup = new NioEventLoopGroup(); + NioEventLoopGroup workerGroup = new NioEventLoopGroup(); + + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .localAddress(port) + .option(ChannelOption.SO_BACKLOG, 1204) + .childHandler(new ServerChannelInitializer()); + + // 异步绑定端口 + ChannelFuture channelFuture = serverBootstrap.bind(); + + // 添加监听器处理绑定结果 + channelFuture.addListener(future -> { + if (future.isSuccess()) { + log.info("服务器启动成功,开始监听端口: {}", port); + } else { + log.error("服务器启动失败,端口: {}", port, future.cause()); + bossGroup.shutdownGracefully(); // 绑定失败立即关闭资源 + workerGroup.shutdownGracefully(); + } + }); + + // ❌ 移除 sync() 阻塞调用 + // channelFuture.channel().closeFuture().sync(); --> 删除这行 + + // 可选:添加 JVM 关闭钩子优雅关闭资源 + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("JVM 正在关闭,准备释放 Netty 资源..."); + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + log.info("Netty 资源已释放"); + })); + } + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/EchoServerHandler.java b/mh-framework/src/main/java/com/mh/framework/netty/EchoServerHandler.java new file mode 100644 index 0000000..c467ffe --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/EchoServerHandler.java @@ -0,0 +1,264 @@ +package com.mh.framework.netty; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.mh.common.constant.Constants; +import com.mh.common.core.domain.entity.CollectionParamsManage; +import com.mh.common.core.domain.entity.SysDictData; +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.utils.*; +import com.mh.common.utils.spring.SpringUtils; +import com.mh.framework.rabbitmq.producer.SendMsgByTopic; +import com.mh.system.service.device.ICollectionParamsManageService; +import com.mh.system.service.device.IGatewayManageService; +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; + +@Slf4j +public class EchoServerHandler extends ChannelInboundHandlerAdapter { + + // 调用service层的接口信息 + IGatewayManageService gatewayManageService = SpringUtils.getBean(IGatewayManageService.class); + ICollectionParamsManageService collectionParamsManageService = SpringUtils.getBean(ICollectionParamsManageService.class); + SendMsgByTopic sendMsgByTopic = SpringUtils.getBean(SendMsgByTopic.class); + + /** + * 空闲次数 + */ + private int idleCount = 1; + private int count = 0; + private List orderList; + private int num = 0; + private int size = 0; + private String IP; + private String port; + private String receiveStr = ""; + private List deviceCodeParamList; + + /** + * 客户端连接会触发 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + log.info("Channel active......"); + } + + /** + * 超时处理 + * 如果120秒没有接受客户端的心跳,就触发; + * 如果超过3次,则直接关闭; + */ + @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.info("第{}已经40秒没有接收到客户端的信息了", idleCount); + receiveStr = ""; + num = num + 1; + if (num > size - 1) { + num = 0; +// // 关闭连接 +// ctx.close(); + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num),ctx,num,size); + } else { + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + } + } + } 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);//复制内容到字节数组bytes + buf.clear(); + // 截取IP地址 + IP = ExchangeStringUtil.getMidString(ctx.channel().remoteAddress() + "", "/", ":"); + // 截取端口号 + port = ExchangeStringUtil.getMidString(ctx.channel().remoteAddress() + "", ":", ""); + if (bytes.length <= 1024) { + //将接收到的数据转为字符串,此字符串就是客户端发送的字符串 + receiveStr = receiveStr + ExchangeStringUtil.bytesToHexString(bytes);//将接收到的数据转为字符串,此字符串就是客户端发送的字符串 + receiveStr = receiveStr.replace("null", ""); //去null + receiveStr = receiveStr.replace(" ", ""); //去空格 + //log.info("channelRead接收到的数据:" + receiveStr + ",length:" + receiveStr.length()); + } + } catch (Exception e) { + log.error("channelRead异常", e); + } finally { + ReferenceCountUtil.release(msg); + } + } + + // 当前批量读取中的最后一条消息 + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + //心跳包报文: 24 00 60 95 + receiveStr = receiveStr.toUpperCase();//返回值全部变成大写 + log.info("channelReadComplete接收到的数据{}, 长度: ===> {}", receiveStr, receiveStr.length()); + //心跳包处理 + if ((receiveStr.length() == 8) && receiveStr.startsWith("24")) { +// if ((receiveStr.length() == 8) && receiveStr.startsWith("C0A801FE")) { + log.info("接收到心跳包 ===> {}", receiveStr); + idleCount = 1; + port = receiveStr.substring(4, 8);//心跳包包含网关端口(自己定义返回心跳包) + // 更新对应的网关在线情况 + gatewayManageService.updateGatewayStatus(receiveStr); + //根据端口或者IP或者心跳包查询网关对应的项目名称 + // 生成采集指令 + if (!SpringUtils.getBean(RedisCache.class).hasKey(receiveStr)) { + collectionParamsManageService.createDtuCollectionParams(); + } + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(receiveStr); + if (StringUtils.isNotNull(arrayCache)) { + deviceCodeParamList = arrayCache.toList(CollectionParamsManage.class); + } + size = deviceCodeParamList.size(); + log.info("deviceCodeParam size ===> {}", size); + // 清空receiveStr + receiveStr = ""; + num = 0; + // 发送采集报文 + if (size > 0) { + if (idleCount < 2) { + Thread.sleep(200); + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + idleCount++; + } else { + ctx.channel().close(); + } + } else { + log.info("gateway not find deviceCodeParam!"); + } + } else if (receiveStr.length() == 34 || receiveStr.length() == 36 || receiveStr.length() == 40 || receiveStr.length() == 44 || receiveStr.length() == 50) { + //电表返回数据解析 + idleCount = 1; + log.info("电表接收===> {},长度:{}", receiveStr, receiveStr.length()); + //解析采集的报文,并保存到数据库 + nextSendOrder(ctx); + } else if (receiveStr.length() == 18) { + //冷量计返回数据解析 + idleCount = 1; + log.info("水电表、热泵设置接收==>{},长度:{}", receiveStr, receiveStr.length()); + nextSendOrder(ctx); + } else if (receiveStr.length() == 12 || receiveStr.length() == 14) { + //冷水机返回数据解析 + idleCount = 1; + log.info("热泵读取接收===>{},长度:{}", receiveStr, receiveStr.length()); + nextSendOrder(ctx); + } else if (receiveStr.length() > 50 && receiveStr.length() < 100) { + idleCount = 1; + // 清空receiveStr + nextSendOrder(ctx); + } + ctx.flush(); + } + + private void nextSendOrder(ChannelHandlerContext ctx) throws InterruptedException { + // 解析采集的报文,并保存到数据库 + analysisReceiveData(receiveStr, deviceCodeParamList.get(num)); + // 清空receiveStr + receiveStr = ""; + // 判断发送的下标,如果不等于指令数组大小 + num = num + 1; + if (num > size - 1) { + num = 0; + Thread.sleep(1000); + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + log.info("------一轮采集完成,继续下一轮--------"); + } else { + // 添加一个状态值,判断是否继续发送指令 update by ljf on 2020-08-07 + if (Constants.WEB_FLAG) { + num = 0; + // 关闭连接 + receiveStr = null; + ctx.close(); + } else { + Thread.sleep(1000); + // 继续发送下一个采集指令 + SendOrderUtils.sendAllOrder(deviceCodeParamList.get(num), ctx, num, size); + } + } + } + + private void analysisReceiveData(final String receiveStr, final CollectionParamsManage deviceCodeParamEntity) { + AnalysisReceiveOrder485 analysisReceiveOrder485 = new AnalysisReceiveOrder485(); + String analysisData = ""; + switch (deviceCodeParamEntity.getParamType()) { + case "16" -> + // 电表 + analysisData = analysisReceiveOrder485.analysisMeterOrder485(receiveStr, deviceCodeParamEntity); + case "18" -> + // 水表 + analysisData = analysisReceiveOrder485.analysisWaterOrder485(receiveStr, deviceCodeParamEntity); + case "5" -> + // 热泵故障报警 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "2" -> + // 热泵启停控制 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "12" -> + // 热泵实际温度 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + case "14" -> + // 热泵读取温度设置 + analysisData = analysisReceiveOrder485.analysisHeatPumpOrder485(receiveStr, deviceCodeParamEntity); + default -> { + log.info("设备类型错误"); + return; + } + } + if (analysisData.isEmpty()) { + log.info("解析数据为空"); + return; + } + // 格式化数据,配置成研华网关 AdvantechReceiver + AdvantechReceiver advantechReceiver = new AdvantechReceiver(); + advantechReceiver.setTs(DateUtils.dateToString(new Date(), Constants.DATE_FORMAT)); + List advantechDatas = new ArrayList<>(); + AdvantechDatas datas = new AdvantechDatas(); + datas.setValue(new BigDecimal(analysisData)); + datas.setTag(deviceCodeParamEntity.getOtherName()); + advantechDatas.add(datas); + advantechReceiver.setD(advantechDatas); + sendMsgByTopic.sendToDeviceMQ(JSONObject.toJSONString(advantechReceiver)); + } + + // 异常捕捉 + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.getCause().printStackTrace(); + log.info("异常捕捉,执行ctx.close" + cause.getCause()); + ctx.close(); // 关闭该Channel + } + + // 客户端断开 + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ctx.close();// 关闭流 + log.info("客户端断开,执行ctx.close()......"); + } + +} diff --git a/mh-framework/src/main/java/com/mh/framework/netty/ServerChannelInitializer.java b/mh-framework/src/main/java/com/mh/framework/netty/ServerChannelInitializer.java new file mode 100644 index 0000000..88cde47 --- /dev/null +++ b/mh-framework/src/main/java/com/mh/framework/netty/ServerChannelInitializer.java @@ -0,0 +1,33 @@ +package com.mh.framework.netty; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.timeout.IdleStateHandler; + +import java.util.concurrent.TimeUnit; + +public class ServerChannelInitializer extends ChannelInitializer{ + + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + ChannelPipeline pipeline = socketChannel.pipeline(); + + /* LineBasedFrameDecoder的工作原理是:依次遍历ByteBuf中的可读字节, + 判断看其是否有”\n” 或 “\r\n”, 如果有就以此位置为结束位置。 + 从可读索引到结束位置的区间的字节就组成了一行。 它是以换行符为结束标志的解码器, + 支持携带结束符和不带结束符两种解码方式,同时支持配置单行的最大长度, + 如果读到了最大长度之后仍然没有发现换行符,则抛出异常,同时忽略掉之前读到的异常码流。*/ +// pipeline.addLast(new LineBasedFrameDecoder(10010)); + //字符串解码和编码 + //LineBasedFrameDecoder + StringDecoder 就是一个按行切换的文本解码器。 +// pipeline.addLast( new StringDecoder()); +// pipeline.addLast( new StringEncoder()); + // 设置读写超时操作 + // 入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式 + pipeline.addLast(new IdleStateHandler(40, 40, 40, TimeUnit.SECONDS)); + //服务器的逻辑 + pipeline.addLast("handler", new EchoServerHandler()); + } + +} diff --git a/mh-quartz/src/main/java/com/mh/quartz/task/AHUTask.java b/mh-quartz/src/main/java/com/mh/quartz/task/AHUTask.java index 24dc4bc..e5af5e5 100644 --- a/mh-quartz/src/main/java/com/mh/quartz/task/AHUTask.java +++ b/mh-quartz/src/main/java/com/mh/quartz/task/AHUTask.java @@ -1,15 +1,12 @@ package com.mh.quartz.task; import com.mh.common.config.MHConfig; -import com.mh.common.core.domain.AjaxResult; -import com.mh.common.core.domain.dto.DeviceMonitorDTO; import com.mh.common.core.domain.entity.CollectionParamsManage; import com.mh.common.core.domain.entity.CpmSpaceRelation; import com.mh.common.core.domain.entity.OrderEntity; import com.mh.common.core.domain.entity.PolicyManage; import com.mh.common.utils.DateUtils; import com.mh.framework.mqtt.service.IMqttGatewayService; -import com.mh.quartz.domain.PIDParams; import com.mh.quartz.util.AHUPIDControlUtil; import com.mh.system.service.device.ICollectionParamsManageService; import com.mh.system.service.operation.IOperationDeviceService; @@ -52,6 +49,8 @@ public class AHUTask { private final IMqttGatewayService iMqttGatewayService; + // 在 AHUTask 类中添加一个 PID 控制器成员变量 + private final Map pidControllers = new HashMap<>(); @Autowired public AHUTask(ICollectionParamsManageService collectionParamsManageService, IPolicyManageService policyManageService, ICpmSpaceRelationService cpmSpaceRelationService, IOperationDeviceService iOperationService, IMqttGatewayService iMqttGatewayService) { @@ -105,9 +104,10 @@ public class AHUTask { double backTempSet = second.get().getCurValue().doubleValue(); // 设定目标温度(夏季制冷24℃) - AHUPIDControlUtil controller = new AHUPIDControlUtil(); + // ✅ 如果没有该设备的控制器,则创建一个新的并保存起来 + AHUPIDControlUtil controller = pidControllers.computeIfAbsent(deviceLedgerId, k -> new AHUPIDControlUtil()); - System.out.println("开始模糊PID控制循环:"); + log.info("开始模糊PID控制循环,查看对象是否有变化:{}", controller); // 过滤获取当前回风温度 Optional third = collectionParamsManages @@ -121,7 +121,7 @@ public class AHUTask { double temp = third.get().getCurValue().doubleValue(); // 2. 计算水阀开度(时间间隔1秒) - double valveOpening1 = controller.compute(backTempSet, temp, 60.0); + double valveOpening1 = controller.compute(backTempSet, temp, 5); int valveOpening = new BigDecimal(valveOpening1).intValue(); // 过滤获取水阀调节参数 diff --git a/mh-quartz/src/main/java/com/mh/quartz/task/DealDataTask.java b/mh-quartz/src/main/java/com/mh/quartz/task/DealDataTask.java index 9a9c631..ccdbfc5 100644 --- a/mh-quartz/src/main/java/com/mh/quartz/task/DealDataTask.java +++ b/mh-quartz/src/main/java/com/mh/quartz/task/DealDataTask.java @@ -6,6 +6,7 @@ import com.mh.common.core.domain.entity.DeviceReport; import com.mh.common.core.redis.RedisCache; import com.mh.common.enums.ComputeEnum; import com.mh.common.utils.DateUtils; +import com.mh.common.utils.StringUtils; import com.mh.framework.dealdata.DataProcessService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -114,7 +115,10 @@ public class DealDataTask { int ratio = entity.getMtRatio(); if (ObjectUtils.isEmpty(lastData) || ObjectUtils.isEmpty(lastData.getLastValue())) { //从device_manage取出初始值 - String initValue = dataProcessService.queryInitValue(entity.getMtNum()); + String initValue = dataProcessService.queryInitValue(entity.getMtNum(), entity.getMtCode()); + if (StringUtils.isEmpty(initValue)) { + initValue = "0"; + } DeviceReport firstEntity = new DeviceReport(); firstEntity.setLastValue(initValue); firstEntity.setLastTime(entity.getCurTime()); @@ -126,7 +130,12 @@ public class DealDataTask { firstEntity.setRegisterAddr(entity.getRegisterAddr()); firstEntity.setDeviceType(deviceType); firstEntity.setRatio(ratio); - double usedValue = entity.getCurValue().doubleValue() - Double.parseDouble(initValue); + double usedValue = 0; + try { + usedValue = entity.getCurValue().doubleValue() - Double.parseDouble(initValue); + } catch (NumberFormatException e) { + log.error("数值格式解析异常:{}", e); + } firstEntity.setUsedValue(String.valueOf(usedValue)); //区分瞬时值 if ((deviceGrade >= 100 && deviceGrade < 200) || (deviceGrade >= 1200 && deviceGrade < 1300) ) { @@ -315,7 +324,7 @@ public class DealDataTask { DeviceReport hourEntity = dataProcessService.queryLastValue(key, tableType); if (ObjectUtils.isEmpty(hourEntity)) { //查询设备信息初始值 - lastValue = dataProcessService.queryInitValue(key); + lastValue = dataProcessService.queryInitValue(key, null); } else { lastValue = hourEntity.getLastValue(); lastDate = hourEntity.getLastTime(); @@ -493,7 +502,7 @@ public class DealDataTask { DeviceReport entity = deviceList.get(i); DeviceReport lastEntity = dataProcessService.queryLastValue(deviceNum, "month"); if (ObjectUtils.isEmpty(lastEntity)) { - lastValue = dataProcessService.queryInitValue(deviceNum); + lastValue = dataProcessService.queryInitValue(deviceNum, null); lastTime = entity.getCurTime(); } else { lastValue = lastEntity.getLastValue(); diff --git a/mh-quartz/src/main/java/com/mh/quartz/util/AHUPIDControlUtil.java b/mh-quartz/src/main/java/com/mh/quartz/util/AHUPIDControlUtil.java index 38756a5..1d84c84 100644 --- a/mh-quartz/src/main/java/com/mh/quartz/util/AHUPIDControlUtil.java +++ b/mh-quartz/src/main/java/com/mh/quartz/util/AHUPIDControlUtil.java @@ -15,6 +15,8 @@ public class AHUPIDControlUtil { private double kp = 2.0, ki = 0.1, kd = 0.0; private double integral = 0; private double previousError = 0; + private static final double MAX_INTEGRAL = 100; + private static final double MIN_INTEGRAL = -100; // 模糊增益修正比例 private double deltaKpScale = 0.5; @@ -44,9 +46,11 @@ public class AHUPIDControlUtil { kd = Math.max(0, Math.min(kd, 1)); // PID 计算 + // 积分项限幅 integral += error * deltaTime; + integral = Math.max(MIN_INTEGRAL, Math.min(MAX_INTEGRAL, integral)); double output = kp * error + ki * integral + kd * dError; -// System.out.println("计算输出值:" + output + ",误差:" + error + ",误差变化:" + dError); + System.out.println("计算输出值:" + output + ",误差:" + error + ",误差变化:" + dError); previousError = error; // 输出冷冻水阀开度,限制在0~100% diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java index 0ae6e50..bc384a2 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/device/CollectionParamsManageMapper.java @@ -339,4 +339,9 @@ public interface CollectionParamsManageMapper extends BaseMapper OffLine(); + + @Select("select * from collection_params_manage cpm " + + " where cpm.system_type = '1' and is_use = 0 " + + " and (param_type= '16' or param_type='18' or param_type = '5' or param_type = '2' or param_type = '12' or param_type = '14')") + List createOrderList(); } diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/DataProcessMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/DataProcessMapper.java index 0a2de36..18965dc 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/device/DataProcessMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/device/DataProcessMapper.java @@ -170,8 +170,18 @@ public interface DataProcessMapper { * @param deviceNum * @return */ - @Select("select mt_init_value from collection_params_manage where mt_num = #{deviceNum} and is_use = '0'") - String queryInitValue(@Param("deviceNum") String deviceNum); + @Select("select mt_init_value from collection_params_manage where mt_num = #{deviceNum} " + + " and mt_code = #{mtCode} and is_use = '0' limit 1") + String queryInitValue(@Param("deviceNum") String deviceNum, @Param("mtCode") String mtCode); + + /** + * 查询初始数据 + * + * @param deviceNum + * @return + */ + @Select("select mt_init_value from collection_params_manage where mt_num = #{deviceNum} and is_use = '0' limit 1") + String queryInitValue1(@Param("deviceNum") String deviceNum); @Select("select * from ${tableName} where register_id = #{registerId} and cur_time >= CURRENT_TIMESTAMP - INTERVAL '24 hours' ") List selectLineDataByCodeAndRegisterAddr(@Param("tableName") String tableName, diff --git a/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java b/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java index 2de5540..cf3b673 100644 --- a/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java +++ b/mh-system/src/main/java/com/mh/system/mapper/device/GatewayManageMapper.java @@ -19,4 +19,6 @@ public interface GatewayManageMapper extends BaseMapper { @Update("update gateway_manage set status = 0, connect_time = CURRENT_TIMESTAMP where id = #{gatewayId}") void updateOnlineStatus(@Param("gatewayId") String gatewayId); + @Update("update gateway_manage set status = 0, connect_time = CURRENT_TIMESTAMP where heart_beat = #{heartBeat}") + void updateOnlineStatusByHeartBeat(@Param("heartBeat") String heartBeat); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java b/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java index 6141d2f..8078233 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java +++ b/mh-system/src/main/java/com/mh/system/service/device/ICollectionParamsManageService.java @@ -79,4 +79,6 @@ public interface ICollectionParamsManageService { List selectMonitorListBySystemTypeAndHouseId(String systemType, String houseId); List selectListByParams(HashMap queryMap); + + void createDtuCollectionParams(); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java b/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java index d0be95e..3a7bffb 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java +++ b/mh-system/src/main/java/com/mh/system/service/device/IGatewayManageService.java @@ -22,4 +22,6 @@ public interface IGatewayManageService { int updateGateway(GatewayManage gatewayManage); int deleteGatewayByIds(String[] gatewayIds); + + void updateGatewayStatus(String heartBeat); } diff --git a/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java index 8c8b59a..ff628fc 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/device/impl/CollectionParamsManageServiceImpl.java @@ -1,5 +1,6 @@ package com.mh.system.service.device.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.mh.common.core.domain.ColumnFilter; import com.mh.common.core.domain.dto.DeviceMonitorDTO; @@ -9,8 +10,12 @@ import com.mh.common.core.domain.dto.PumpInfoDTO; import com.mh.common.core.domain.entity.ChillersEntity; import com.mh.common.core.domain.entity.CollectionParamsManage; import com.mh.common.core.domain.entity.DeviceLedger; +import com.mh.common.core.domain.entity.GatewayManage; import com.mh.common.core.domain.vo.*; +import com.mh.common.core.redis.RedisCache; +import com.mh.common.utils.CRC16; import com.mh.common.utils.DateUtils; +import com.mh.common.utils.ExchangeStringUtil; import com.mh.common.utils.StringUtils; import com.mh.system.mapper.device.CollectionParamsManageMapper; import com.mh.system.mapper.device.DataProcessMapper; @@ -27,6 +32,7 @@ import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -52,6 +58,53 @@ public class CollectionParamsManageServiceImpl implements ICollectionParamsManag @Resource private DataProcessMapper dataProcessMapper; + @Resource + private RedisCache redisCache; + + @Override + public void createDtuCollectionParams() { + List paramsManages = collectionParamsManageMapper.createOrderList(); + // 根据网关类型分组 + Map> map = paramsManages.stream().collect(Collectors.groupingBy(CollectionParamsManage::getGatewayId)); + // 查询全部的网关 + List gatewayManages = gatewayManageMapper.selectList(new QueryWrapper()); + if (gatewayManages.isEmpty()) { + return; + } + for (GatewayManage gatewayManage : gatewayManages) { + String gatewayId = gatewayManage.getId(); + String heartBeat = gatewayManage.getHeartBeat(); + if (map.containsKey(gatewayId)) { + List paramsManages1 = map.get(gatewayId); + // 删除缓存数据 + if (redisCache.hasKey(heartBeat)) { + redisCache.deleteObject(heartBeat); + } +// List sendOrder = new ArrayList<>(); +// for (CollectionParamsManage paramsManage : paramsManages1) { +// // 开始创建指令 +// String mtCode = paramsManage.getMtCode(); // 采集编号 +// String funCode = paramsManage.getMtCode(); // 功能码 +// String registerAddr = paramsManage.getRegisterAddr(); // 寄存器地址 +// String registerNum = String.valueOf(paramsManage.getRegisterSize()); // 寄存器数量 +// // 拼接指令 +// String sendOrderStr = ExchangeStringUtil.addZeroForNum(ExchangeStringUtil.intToHexString(Integer.parseInt(mtCode), true), 2) +// + ExchangeStringUtil.addZeroForNum(funCode, 2) +// + ExchangeStringUtil.addZeroForNum(registerAddr, 4) +// + ExchangeStringUtil.addZeroForNum(registerNum, 4); +// byte[] strOrder = ExchangeStringUtil.hexStrToBinaryStr(sendOrderStr); +// int checkNum = CRC16.CRC16_MODBUS(strOrder); +// String checkWord = ExchangeStringUtil.decToHex(String.valueOf(checkNum)); +// checkWord = checkWord.substring(2, 4) + checkWord.substring(0, 2); +// sendOrderStr = sendOrderStr + checkWord; +// sendOrder.add(sendOrderStr); +// } + redisCache.setCacheObject(heartBeat, paramsManages1); + redisCache.expire(heartBeat, 30, TimeUnit.MINUTES); + } + } + } + @Override public List selectListByParams(HashMap queryMap) { if (queryMap == null || queryMap.isEmpty()) { diff --git a/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java b/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java index 811b1de..e6772c4 100644 --- a/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java +++ b/mh-system/src/main/java/com/mh/system/service/device/impl/GatewayManageServiceImpl.java @@ -9,6 +9,7 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import java.util.List; +import java.util.stream.Collectors; /** * @author LJF @@ -23,6 +24,11 @@ public class GatewayManageServiceImpl implements IGatewayManageService { @Resource private GatewayManageMapper gatewayManageMapper; + @Override + public void updateGatewayStatus(String heartBeat) { + gatewayManageMapper.updateOnlineStatusByHeartBeat(heartBeat); + } + @Override public List selectGwManageList(GatewayManage gatewayManage) { QueryWrapper queryWrapper = new QueryWrapper<>();