HJ212-2017是中國環(huán)境保護(hù)部發(fā)布的環(huán)境監(jiān)測數(shù)據(jù)傳輸協(xié)議,主要用于環(huán)境監(jiān)測設(shè)備與監(jiān)控中心之間的數(shù)據(jù)傳輸。該協(xié)議定義了數(shù)據(jù)傳輸?shù)母袷?、?nèi)容、通信方式等,廣泛應(yīng)用于空氣質(zhì)量、水質(zhì)等環(huán)境監(jiān)測領(lǐng)域。本文將詳細(xì)介紹HJ212-2017協(xié)議的結(jié)構(gòu),并通過Java語言和Netty框架實(shí)現(xiàn)一個簡單的協(xié)議解析。
HJ212-2017協(xié)議采用ASCII碼進(jìn)行數(shù)據(jù)傳輸,數(shù)據(jù)包由多個字段組成,每個字段以分號(;)分隔。協(xié)議的基本結(jié)構(gòu)如下:
ST=xx;CN=xx;PW=xx;MN=xx;Flag=xx;CP=xx;CRC=xx\r\n
ST:系統(tǒng)類型,表示數(shù)據(jù)來源的系統(tǒng)類型。
CN:命令編號,表示數(shù)據(jù)包的類型。
PW:密碼,用于身份驗(yàn)證。
MN:設(shè)備編號,唯一標(biāo)識設(shè)備。
Flag:標(biāo)志位,表示數(shù)據(jù)的傳輸狀態(tài)。
CP:數(shù)據(jù)段,包含具體的監(jiān)測數(shù)據(jù)。
CRC:校驗(yàn)碼,用于數(shù)據(jù)完整性校驗(yàn)。
數(shù)據(jù)段(CP)是HJ212-2017協(xié)議中最重要的部分,包含了具體的監(jiān)測數(shù)據(jù)。數(shù)據(jù)段由多個數(shù)據(jù)項(xiàng)組成,每個數(shù)據(jù)項(xiàng)以逗號(,)分隔,格式如下:
DataTime=xx;xxx-Rtd=xx,xxx-Flag=xx;...
DataTime:數(shù)據(jù)時(shí)間,表示數(shù)據(jù)采集的時(shí)間。
創(chuàng)建一個簡單的Netty服務(wù)器,用于接收HJ212-2017協(xié)議數(shù)據(jù)。
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class HJ212Server {
private final int port;
public HJ212Server(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new StringDecoder(), new StringEncoder(), new HJ212Handler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new HJ212Server(port).run();
}
}
實(shí)現(xiàn)一個HJ212Handler,用于解析HJ212-2017協(xié)議數(shù)據(jù)
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class HJ212Handler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
// 解析HJ212協(xié)議數(shù)據(jù)
HJ212Data data = parseHJ212Data(msg);
if (data != null) {
System.out.println("Received HJ212 Data: " + data);
} else {
System.out.println("Invalid HJ212 Data: " + msg);
}
}
private HJ212Data parseHJ212Data(String data) {
HJ212Data hj212Data = new HJ212Data();
String[] fields = data.split(";");
for (String field : fields) {
String[] keyValue = field.split("=");
if (keyValue.length == 2) {
String key = keyValue[0];
String value = keyValue[1];
switch (key) {
case "ST":
hj212Data.setSt(value);
break;
case "CN":
hj212Data.setCn(value);
break;
case "PW":
hj212Data.setPw(value);
break;
case "MN":
hj212Data.setMn(value);
break;
case "Flag":
hj212Data.setFlag(value);
break;
case "CP":
parseCP(hj212Data, value);
break;
case "CRC":
hj212Data.setCrc(value);
break;
default:
break;
}
}
}
return hj212Data;
}
private void parseCP(HJ212Data hj212Data, String cp) {
String[] cpFields = cp.split(",");
for (String cpField : cpFields) {
String[] keyValue = cpField.split("=");
if (keyValue.length == 2) {
String key = keyValue[0];
String value = keyValue[1];
if (key.endsWith("-Rtd")) {
String factor = key.substring(0, key.length() - 4);
hj212Data.addRtdData(factor, value);
} else if (key.endsWith("-Flag")) {
String factor = key.substring(0, key.length() - 5);
hj212Data.addFlagData(factor, value);
} else if (key.equals("DataTime")) {
hj212Data.setDataTime(value);
}
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
import java.util.HashMap;
import java.util.Map;
public class HJ212Data {
private String st;
private String cn;
private String pw;
private String mn;
private String flag;
private String dataTime;
private String crc;
private Map rtdData = new HashMap<>();
private Map flagData = new HashMap<>();
// Getters and Setters
public void addRtdData(String factor, String value) {
rtdData.put(factor, value);
}
public void addFlagData(String factor, String value) {
flagData.put(factor, value);
}
@Override
public String toString() {
return "HJ212Data{" +
"st='" + st + '\'' +
", cn='" + cn + '\'' +
", pw='" + pw + '\'' +
", mn='" + mn + '\'' +
", flag='" + flag + '\'' +
", dataTime='" + dataTime + '\'' +
", crc='" + crc + '\'' +
", rtdData=" + rtdData +
", flagData=" + flagData +
'}';
}
}
ST=32;CN=2011;PW=123456;MN=88888880000001;Flag=4;CP=DataTime=20231010120000;w01018-Rtd=12.5,w01018-Flag=N;w01019-Rtd=0.8,w01019-Flag=N;CRC=ABCD\r\n