国标hj/t212协议Java解析
HJ212分为2005年(HJ/T212-2005)和2017年(HJ212-2017)的版本,略有不同。
网上没找到非常官方的渠道下载,在这贴一份2017年版本的下载地址
TCP/IP通讯包组成
名称 |
类型 |
长度 |
描述 |
包头 |
字符 |
2 |
固定为## |
数据段长度 |
十进制整数 |
4 |
表示数据段长度,如长度336则为0336 |
数据段 |
字符 |
0-1024 |
变长的数据,为包的传输内容 |
CRC校验 |
十六进制整数 |
4 |
用于校验数据包完整性的CRC校验值,后续附上算法 |
包尾 |
字符 |
2 |
固定为(回车、换行) |
通讯包数据段组成
名称 |
类型 |
长度 |
描述 |
请求编码QN |
字符 |
20 |
精确到毫秒的时间戳:QN=YYYYMMDDhhmmsszzz,用来唯一标识一次命令交互 |
系统编码ST |
字符 |
5 |
系统编号 |
命令编号CN |
字符 |
7 |
命令编号 |
访问密码PW |
字符 |
6 |
访问密码 |
设备唯一标识MN |
字符 |
14 |
监测点编号 |
拆分包及应答标志Flag |
字符 |
3 |
见文档原文 |
总包号PNUM |
字符 |
4 |
表示本次通讯总共包含的包数 |
包号PNO |
字符 |
4 |
PNO指示当前数据包的包号 |
指令参数CP |
字符 |
0-960 |
CP=&&数据区&& |
名称 |
类型 |
长度 |
描述 |
请求编码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;
@Slf4j
public class T212Parser {
private static char[] HEADER = new char[]{'#', '#'};
private static char[] FOOTER = new char[]{'\r', '\n'};
public char[] data = null;
private char[] dataString = null;
private Integer dataLength;
private String segmentData;
private Map<String, String> segmentDataMap;
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;
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;
dataPointer = setStringBuilder(datas, lengthdatas, data, dataPointer);
if (this.dataLength.intValue() != datas.length()) {
throw new T212FormatException("数据段长度验证失败");
}
StringBuilder datasString = new StringBuilder();
setStringBuilder(datasString, lengthdatas, dataString, lengthdatasDataPointer);
this.segmentData = datasString.toString();
segmentDataByMap();
upp();
cpMap();
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();
StringBuilder bw = new StringBuilder();
dataPointer = setStringBuilder(bw, 2, data, dataPointer);
if (!bw.toString().equals("\r\n")) {
throw new T212FormatException("包尾错误");
}
}
public char[] readHeader() {
return HEADER;
}
public Integer readDataLen() {
return dataLength;
}
public String readData(int segmentLen) {
return segmentData;
}
public String readCrc() {
return crc;
}
public char[] readDataAndCrc(int dataLen) {
return FOOTER;
}
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
public class CRCUtil {
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);
}
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;&&";
String string = Integer.toHexString(crc16Checkout(data.toCharArray()));
System.out.println(string);
}
}
@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
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();
}
public int dataHeader() {
data.append(HEADER);
return 2;
}
public int dataDataLen(String len) {
data.append(len);
return 4;
}
public int writeHexInt32(int i) {
char[] intChars = Integer.toHexString(i).toCharArray();
data.append(intChars);
return intChars.length;
}
public int writeData(String data) {
this.data.append(data);
return data.length();
}
public int writeCrc(String crc) {
data.append(crc);
return crc.length();
}
public int writeFooter() {
data.append(FOOTER);
return 2;
}
public static String getDataLength(String tData) {
int length = tData.length();
while (length < 4) {
++length;
tData = "0" + tData;
}
return tData;
}
}