hj/t212协议工具类


国标hj/t212协议Java解析

HJ212分为2005年(HJ/T212-2005)和2017年(HJ212-2017)的版本,略有不同。
网上没找到非常官方的渠道下载,在这贴一份2017年版本的下载地址


TCP/IP通讯包组成

名称 类型 长度 描述
包头 字符 2 固定为##
数据段长度 十进制整数 4 表示数据段长度,如长度336则为0336
数据段 字符 0-1024 变长的数据,为包的传输内容
CRC校验 十六进制整数 4 用于校验数据包完整性的CRC校验值,后续附上算法
包尾 字符 2 固定为(回车、换行)

通讯包数据段组成

  • HJ/T212-2005
名称 类型 长度 描述
请求编码QN 字符 20 精确到毫秒的时间戳:QN=YYYYMMDDhhmmsszzz,用来唯一标识一次命令交互
系统编码ST 字符 5 系统编号
命令编号CN 字符 7 命令编号
访问密码PW 字符 6 访问密码
设备唯一标识MN 字符 14 监测点编号
拆分包及应答标志Flag 字符 3 见文档原文
总包号PNUM 字符 4 表示本次通讯总共包含的包数
包号PNO 字符 4 PNO指示当前数据包的包号
指令参数CP 字符 0-960 CP=&&数据区&&
  • HJ212-2017
名称 类型 长度 描述
请求编码QN 字符 20 精确到毫秒的时间戳:QN=YYYYMMDDhhmmsszzz,用来唯一标识一次命令交互
系统编码ST 字符 5 系统编号
命令编号CN 字符 7 命令编号
访问密码PW 字符 9 访问密码
设备唯一标识MN 字符 27 监测点编号
拆分包及应答标志Flag 字符 8 见文档原文
总包号PNUM 字符 9 表示本次通讯总共包含的包数
包号PNO 字符 8 PNO指示当前数据包的包号
指令参数CP 字符 0-950 CP=&&数据区&&

报文解析

T212Parser

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;


/**
 * T212通信包解析器
 */
@Slf4j
public class T212Parser {

    private static char[] HEADER = new char[]{'#', '#'};
    private static char[] FOOTER = new char[]{'\r', '\n'};

//    private final static String Flag = "5";
    /**
     * 完整包数据
     */
    public char[] data = null;
    /**
     * 完整包数据
     */
    private char[] dataString = null;
    /**
     * 数据长度
     */
    private Integer dataLength;

    /**
     * 数据段
     * 字段与其值用‘=’连接;在数据区中,同一项目的不同分类值间用‘,’来分隔,不同项目之间用‘;’来分隔
     */
    private String segmentData;

    /**
     * segmentDataByMap
     */
    private Map<String, String> segmentDataMap;
    /**
     * crc
     */
    private String crc;

    /**
     * 指令参数
     */
    private static final String CP = "CP";

    /**
     * 上传公共参数
     */
    public String QN;
    public String PW;
    public String MN;
    public String ST;
    public String CN;
    public String Flag;
    public String IP;
    /**
     * CP数据
     */
    public Map<String, String> CPMap;

    public T212Parser(String data) throws T212FormatException {
        String[] split = data.split("CP=&&");
        String data1 = split[0] + "CP=&&" + split[1].replace(";", ",");
        this.dataString = data1.toCharArray();
        this.data = data.toCharArray();
        init();
    }

    private void init() throws T212FormatException {
        if (data == null || this.data.length < 2) {
            throw new T212FormatException("data不能为空或长度应大于2");
        }
        int dataPointer = 0; //数据指针
        int dataLengths = data.length; //数据长度
        //包头 ##
        StringBuilder btStringBuilder = new StringBuilder();
        dataPointer = setStringBuilder(btStringBuilder, 2, data, dataPointer);
        if (!btStringBuilder.toString().equals("##")) {
            throw new T212FormatException("包头错误");
        }
        //数据包长度
        StringBuilder dataLengthChar = new StringBuilder();
        dataPointer = setStringBuilder(dataLengthChar, 4, data, dataPointer);
        this.dataLength = Integer.valueOf(dataLengthChar.toString());

        //数据包
        StringBuilder datas = new StringBuilder();
        int lengthdatas=dataLengths - dataPointer - 4 - 2;
        int lengthdatasDataPointer=dataPointer;
        //总长度-当前长度-crc-包尾
        dataPointer = setStringBuilder(datas, lengthdatas, data, dataPointer);
        if (this.dataLength.intValue() != datas.length()) {
            throw new T212FormatException("数据段长度验证失败");
        }
        //数据包转换后
        StringBuilder datasString = new StringBuilder();
        //总长度-当前长度-crc-包尾
        setStringBuilder(datasString, lengthdatas, dataString, lengthdatasDataPointer);
        this.segmentData = datasString.toString();


        //初始化segmentDataByMap
        segmentDataByMap();
        //初始化QN=20160801085857223;PW=123456;MN
        upp();
        //初始化CP参数
        cpMap();

        //crc
        StringBuilder crc = new StringBuilder();
        dataPointer = setStringBuilder(crc, 4, data, dataPointer);
        String crcs = CRCUtil.crc16(datas.toString().toCharArray());
        if (!crc.toString().toLowerCase().equals(crcs)) {
            throw new T212FormatException("crc校验未通过");
        }
        this.crc = crc.toString();
        //包尾 \r\n
        StringBuilder bw = new StringBuilder();
        dataPointer = setStringBuilder(bw, 2, data, dataPointer);
        if (!bw.toString().equals("\r\n")) {
            throw new T212FormatException("包尾错误");
        }
    }


    /**
     * 读取 包头
     *
     * @return chars
     */
    public char[] readHeader() {
        return HEADER;
    }

    /**
     * 读取 数据段长度
     *
     * @return chars
     */
    public Integer readDataLen() {

        return dataLength;
    }

    /**
     * 读取 数据段
     *
     * @return chars
     */
    public String readData(int segmentLen) {

        return segmentData;
    }

    /**
     * 读取 DATA_CRC 校验
     *
     * @return header chars
     */
    public String readCrc() {

        return crc;
    }

    /**
     * 读取 包尾
     *
     * @return chars
     */
    public char[] readDataAndCrc(int dataLen) {

        return FOOTER;
    }

    /**
     * 数据拼装
     *
     * @param stringBuilder
     * @param length
     * @param data
     * @param pointer
     */
    private int setStringBuilder(StringBuilder stringBuilder, int length, char[] data, int pointer) throws ArrayIndexOutOfBoundsException {
        while (length > 0) {
            stringBuilder.append(data[pointer++]);
            --length;
        }
        return pointer;
    }


    /**
     * 数据解析
     */
    private void segmentDataByMap() {
        String tcpData = this.segmentData;
        String[] splitTcpData = tcpData.split(";");
        if (splitTcpData.length == 0) {
            return;
        }
        segmentDataMap = new Hashtable<>();
        for (String tdata : splitTcpData) {
            String[] values = tdata.split("=");
            if (values[0].equals(CP)) {
                segmentDataMap.put(CP, tdata.substring(3));
            } else {
                segmentDataMap.put(values[0], values[1]);
            }
        }
    }

    /**
     * 公共参数解析
     */
    private void upp() {
        QN = segmentDataMap.get("QN");
        if(QN==null|| QN.equals(""))
            throw new RuntimeException("QN is not null");
        PW = segmentDataMap.get("PW");
        MN = segmentDataMap.get("MN");
        if(MN==null|| MN.equals(""))
            throw new RuntimeException("MN is not null");
        CN = segmentDataMap.get("CN");
        if(CN==null|| CN.equals(""))
            throw new RuntimeException("CN is not null");
        ST = segmentDataMap.get("ST");
        Flag = segmentDataMap.get("Flag");
        if(Flag==null|| Flag.equals(""))
            throw new RuntimeException("Flag is not null");
    }

    private void cpMap() {
        CPMap = new HashMap<>();
        String cp = segmentDataMap.get(CP);
        String substring = cp.substring(2, cp.length() - 2);
        if (substring.length() == 0)
            return;
        String replace = substring.replace(",", ";");
        String[] split = replace.split(";");
        if (split.length == 0)
            return;
        for (String c : split) {
            String[] split1 = c.split("=");
            CPMap.put(split1[0], split1[1]);
        }
    }

    public Map<String, String> getSegmentDataMap() {
        return this.segmentDataMap;
    }
}

CRCUtil

/**
 * 环保212协议crc校验
 */
public class CRCUtil {
    /**
     * CRC校验
     * 结果需要Integer.toHexString()
     *
     * @param msg 消息
     * @return DATA_CRC 校验码
     */
    private static int crc16Checkout(char[] msg) {
        int i, j, crc_reg, check;

        crc_reg = 0xFFFF;
        for (i = 0; i < msg.length; i++) {
            crc_reg = (crc_reg >> 8) ^ msg[i];
            for (j = 0; j < 8; j++) {
                check = crc_reg & 0x0001;
                crc_reg >>= 1;
                if (check == 0x0001) {
                    crc_reg ^= 0xA001;
                }
            }
        }
        return crc_reg;
    }

    public static String crc16(char[] msg) {
        String hexString = Integer.toHexString(CRCUtil.crc16Checkout(msg));
        return getDataLength(hexString);
    }

    /**
     * 长度4位,前面补0
     *
     * @param tData
     * @return
     */
    private static String getDataLength(String tData) {
        int length = tData.length();
        while (length < 4) {
            ++length;
            tData = "0" + tData;
        }
        return tData;
    }

    public static void main(String[] args) {
        String data = "QN=一个20160801085857223;ST=32;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=5;CP=&&DataTime=20160801085857;w01001-Rtd=7.1,w01001-Flag=N;w01018-SampleTime=20160801070000,01018-Rtd=2.2,w01018-Flag=N,w01018-EFlag=A01;&&";
        //data = "QN=20200713142518207;ST=91;CN=9011;PW=123456;MN=0C733B9CDB9E5332260E91060EB9E427E7DD954B;Flag=4;CP=&&&&";
        String string = Integer.toHexString(crc16Checkout(data.toCharArray()));
        System.out.println(string);

    }
}

T212FormatException

@SuppressWarnings("serial")
public class T212FormatException extends Exception {

    private Object result;

    public T212FormatException(String message) {
        super(message);
    }

    public T212FormatException(String message, Object result) {
        super(message);
        this.result = result;
    }

    public T212FormatException(String message, Throwable t) {
        super(message,t);
    }


    public static void separator_position(char c, Enum<?> mode) throws T212FormatException {
        throw new T212FormatException("Separator position is wrong: " + c + " cant in Mode: " + mode.name());
    }

    public static void static_data_match(Enum<?> flag, char[] tar, char[] src) throws T212FormatException {
        throw new T212FormatException("Static data core: " + flag.toString() + ": " + new String(tar) + " -> " + new String(src));
    }

    public static void length_not_match(Enum<?> flag, int tar, int src) throws T212FormatException {
        throw new T212FormatException("Length does not core: " + flag.toString() + ": " + tar + " -> " + src);
    }

    public static void length_not_range(Enum<?> flag, int src, int min, int max) throws T212FormatException {
        throw new T212FormatException("Length does not in range: " + flag.toString() + ": " + src + " -> (" + min + "," + max + ")");
    }

    public static void field_is_missing(Enum<?> flag, String field) throws T212FormatException {
        throw new T212FormatException("Field is missing: " + flag.toString() + ": " + field);
    }

    public static void crc_verification_failed(Enum<?> flag, char[] msg, char[] crc) throws T212FormatException {
        throw new T212FormatException("Crc Verification failed: " + new String(msg) + ": " + new String(crc));
    }

    public static void crc_verification_failed(Enum<?> flag, char[] msg, int crc) throws T212FormatException {
        throw new T212FormatException("Crc Verification failed: " + new String(msg) + ": " + Integer.toHexString(crc));
    }

    public static void segment_exception(SegmentFormatException e) throws T212FormatException {
        throw new T212FormatException("Segment format exception in: " + PacketElement.DATA.toString(),e);
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    public T212FormatException withResult(Object result) {
        this.result = result;
        return this;
    }
}

生成报文

T212Generator

/**
 * T212通信包生成器
 */
public class T212Generator {
    public static char[] HEADER = new char[]{'#', '#'};
    public static char[] FOOTER = new char[]{'\r', '\n'};


    protected StringBuilder data;

    @Deprecated
    public T212Generator(StringBuilder data) {
        this.data = data;
    }


    public static String generateT212(String datastring) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(HEADER);

        Integer dataLength = datastring.length();
        String dataLength1 = getDataLength(dataLength.toString());
        stringBuilder.append(dataLength1);
        stringBuilder.append(datastring);
        String crc = CRCUtil.crc16(datastring.toCharArray());
        stringBuilder.append(crc);
        stringBuilder.append(FOOTER);
        return stringBuilder.toString();
    }

    /**
     * 写入 包头
     *
     * @return count always 2
     * @see #HEADER
     */
    public int dataHeader() {
        data.append(HEADER);
        return 2;
    }


    /**
     * 写入 数据段长度
     *
     * @param len chars
     * @return length always 4
     */
    public int dataDataLen(String len) {
        data.append(len);
        return 4;
    }

    /**
     * 写入 4字节Integer
     *
     * @param i integer
     * @return length always 4
     */
    public int writeHexInt32(int i) {
        char[] intChars = Integer.toHexString(i).toCharArray();
        data.append(intChars);
        return intChars.length;
    }

    /**
     * 读取 数据段
     *
     * @param data data chars
     * @return data length
     */
    public int writeData(String data) {
        this.data.append(data);
        return data.length();
    }

    /**
     * 写入 DATA_CRC 校验
     *
     * @param crc crc chars
     * @return length always 4
     */
    public int writeCrc(String crc) {
        data.append(crc);
        return crc.length();
    }


    /**
     * 写入 包尾
     *
     * @return length always 2
     */
    public int writeFooter() {
        data.append(FOOTER);
        return 2;
    }

    /**
     * 长度4位,前面补0
     *
     * @param tData
     * @return
     */
    public static String getDataLength(String tData) {
        int length = tData.length();
        while (length < 4) {
            ++length;
            tData = "0" + tData;
        }
        return tData;
    }
}

文章作者: wmg
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wmg !
  目录