22 changed files with 2469 additions and 38 deletions
			
			
		@ -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<String> 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 ""; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -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异或 | 
				
			||||
 * <p> | 
				
			||||
 * (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码。 | 
				
			||||
 * <p> | 
				
			||||
 * 以上计算步骤中的多项式0xA001是0x8005按位颠倒后的结果。 | 
				
			||||
 * 0x8408是0x1021按位颠倒后的结果。 | 
				
			||||
 * 在线校验工具 | 
				
			||||
 * http://www.ip33.com/crc.html
 | 
				
			||||
 * https://blog.csdn.net/htmlxx/article/details/17369105
 | 
				
			||||
 * <p> | 
				
			||||
 * 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; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -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; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -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 资源已释放"); | 
				
			||||
        })); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -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<String> orderList; | 
				
			||||
    private int num = 0; | 
				
			||||
    private int size = 0; | 
				
			||||
    private String IP; | 
				
			||||
    private String port; | 
				
			||||
    private String receiveStr = ""; | 
				
			||||
    private List<CollectionParamsManage> 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> 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()......"); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
@ -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<SocketChannel>{ | 
				
			||||
 | 
				
			||||
    @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()); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue