精华内容
下载资源
问答
  • 1、HJ212TestClient v2.0.2免费提供给环保企业使用,可通过串口方式和网络方式对符合HJ212-2017标准的设备进行测试,如以网络通讯方式测试,请联系我们获取服务器IP和端口。2、可通过本软件对HJ212-2017中所有命令...
  • 支持多线程通讯; 支持报文log,多线程收发报文写log,采用互斥锁机制,防止阻塞; 基于HJ212-2017协议,接收客户端上报数据...可以直接运行HJ212TCPServerC\Debug\HJ212Server.exe 基于VS2010开发,可以继续开发;
  • HJ212Dispatcher-源码

    2021-03-11 14:36:46
    HJ212Dispatcher
  • hj212_server.zip

    2020-12-07 13:00:53
    标准HJ212协议数据解析,兼容HJ212-2017、HJ212-2005版本。参考网上大神代码进行完善开发,可以直接导入开发工具运行。
  • local hj212_proto=Proto("hj212-2017","environment 212 protocal") local fields = hj212_proto.fields fields.qn = ProtoField.string("hj212_proto.qn", "QN", base.ASCII) fields.st = ProtoField.stri

    目录

    前言

    还没解决的问题:

    • 还不知道如何解析不固定长度的字段
    • 还不知道一个数据被分在多个TCP包里的情况如何处理

    代码

    local hj212_proto=Proto("hj212-2017","environment 212 protocal")
    local fields = hj212_proto.fields
    
    fields.qn = ProtoField.string("hj212_proto.qn", "QN", base.ASCII)
    fields.st = ProtoField.string("hj212_proto.st", "ST",base.ASCII)
    fields.mn = ProtoField.string("hj212_proto.mn", "MN",base.ASCII)
    fields.datatime = ProtoField.string("hj212_proto.dt", "Datatime",base.ASCII)
    ------------------------------------------------------------------------
    
    function hj212_proto.dissector(tvb,pinfo,tree)
    pinfo.cols.protocol = hj212_proto.name
    data_len  = tvb:len()
    local d=tree:add(hj212_proto.name.."-Data")
    d:append_text("("..data_len.." bytes of data)")
    d:add(fields.qn,tvb(9,17))
    d:add(fields.st,tvb(30,2))
    d:add(fields.mn,tvb(72,14))
    d:add(fields.datatime,tvb(108,14))
    -- 
    local data_dis = Dissector.get("data")
    data_dis:call(tvb(0):tvb(), pinfo,d)
    end
    
    
    ---------------------------------------------------------------
    
    local tcp_port_table=DissectorTable.get("tcp.port")
    
    -- 默认10086端口
    tcp_port_table:add(10086,hj212_proto)
    

    在这里插入图片描述

    参考

    https://www.cnblogs.com/dongpohezui/p/14248605.html

    展开全文
  • HJ212Test.zip

    2020-04-03 22:14:13
    HJ212-2017测试软件是一款由长沙亿美奥智能科技公司开发的测试工具,主要功能包括功能界面、上报应答、解析报文、原始报文等等,更多功能包括系统设置、参数命令、数据命令、控制命令等等,如果你觉得这款HJ212-2017...
  • HJ212TCPServer 基于HJ212-2017协议; 已经编译,下载后HJ212TCPServer\Debug\HJ212TCPServer.exe程序可以直接运行; 基于VS2012开发,带源代码,可以自主再开发; TCP Socket服务端,支持多个客户端连接,MFC多...
  • HJ212TCPServerMFC\Debug\HJ212TCPServerMFC.exe 基于VS2012开发,包含完整源代码,可以自主再开发; 1、TCP Socket服务端,支持多个客户端连接,MFC多线程异步收发,防阻塞; 收到客户端的报文自动回码确认,判断...
  • HJ212数据模拟器.rar

    2021-01-08 18:05:03
    HJ212-2005协议调试软件,可生成部分污水,废气的数据,
  • HJ212Test.rar

    2019-06-14 16:07:45
    可以对最新的国标HJ212-2017进行测试,作为一个平台一样使用;接收设备发过来的数据并自动回复握手信息。
  • Java 解析国标hj212协议

    2020-05-21 22:14:17
    Java解析环保污染物hj212协议,java项目导入eclipse运行即可,直接调用即可。有什么问题可以给我留言。
  • hj212-master.zip

    2019-08-13 15:13:16
    hj212协议文档,需要的童鞋放心下载,解压皆可以用,无毒
  • 多线程通讯;报文log,多线程收发报文写log,采用互斥锁机制,防止阻塞; 基于HJ212-2017协议,接收客户端上报数据后,立即回复确认;...可以直接运行HJ212Server.exe 基于VS2010开发,可以继续开发;
  • HJ212-2017环保协议测试软件 1、可通过串口方式和网络方式对符合HJ212-2017标准的设备进行测试,如以网络通讯方式测试,请联系我们获取服务器IP和端口。 2、可通过本软件对HJ212-2017中所有命令的测试。 3、解压后...
  • HJ212TCPServer程序加源码,可以直接使用或者二次开发。是vc写的,是学习的好例子可以改写成其它语言
  • HJ212-2017 记录

    2021-03-30 11:11:23
    HJ212-2017 记录

    HJ212-2017 记录

    在这里插入图片描述

    在这里插入图片描述

    ST字段解析

    在这里插入图片描述

    CN字段解析

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    Python编码—基于HJ 212协议的简单接收程序
    https://cloud.tencent.com/developer/article/1752838

    利用Python简单实现环保212协议客户端【HJ212_2017协议的封包发送】
    https://blog.csdn.net/lzl640/article/details/113394845

    上位机 应答

    需求

    主要需求
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    测试数据

    ##0141QN=20210331101308609;ST=21;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&DataTime=20210331101308;a01002-Rtd=12.69;a01002-Flag=N&&9E81
    
    ##0141QN=20210331101313609;ST=21;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&DataTime=20210331101313;a01002-Rtd=12.69;a01002-Flag=N&&4400
    
    ##0141QN=20210331101328611;ST=21;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&DataTime=20210331101328;a01002-Rtd=12.69;a01002-Flag=N&&95C1
    
    
    

    调试 软件

    http://www.cmsoft.cn/
    在这里插入图片描述

    ##0141QN=20210331101308609;ST=21;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=4;CP=&&DataTime=20210331101308;a01002-Rtd=12.69;a01002-Flag=N&&9E81
    

    flag =1

    ##0141QN=20210331164130600;ST=21;CN=2011;PW=123456;MN=010000A8900016F000169DC0;Flag=1;CP=&&DataTime=20210331164130;a01002-Rtd=12.69;a01002-Flag=N&&F3C1
    
    展开全文
  • 数据传输标准》HJ_T212-2005以及HJ 212-2017的PDF文档,做环保方面的一定对于HJ212协议不陌生的,另外由于工作中涉及到环保212协议的接收和解、推送,上传了一个基于C++的212解析类GB212,可以从上传的资料中下载
  • HJ212-2017污染物传输标准.pdf
  • hj212_crc.zip

    2019-05-16 16:46:50
    该工具是用qt做的一个界面小程序,用于对Hj212调试时组包校验; 自己调试用的,上传供大家参考。 包括源代码和可执行程序 数据校验是从"ST="开始到&&结束,包括&&。 没有积分转此百度网盘:链接: ...
  • HJ212-2005环境监测服务器端,可以给2017版本胡做参考,免费
  • HJ212-2017标准协议交流

    2020-02-17 01:16:50
    应用本软件可对《污染物在线监控(监测)系统数据传输标准HJ212-2017》协议中所有参数命令、数据命令、控制命令进行测试操作,具体如下: 参数命令:超时参数、设备密码、设备时间、实时数据间隔、分钟数据间隔; ...
  • 环保 HJ212协议解析

    千次阅读 2020-09-21 17:36:13
    HJ212协议是一种字符串协议,数据传输通讯包主要由包头、数据段长度、数据段、CRC校验、包尾组成,其中“数据段”内容包括请求编码、系统编码、命令编码、密码、设备唯一标识、总包数、包号、指令参数。请求编码为...

    HJ212协议简介

    由于是做环保相关的,有时需要对212协议进行拆包和解包。HJ212协议是一种字符串协议,数据传输通讯包主要由包头、数据段长度、数据段、CRC校验、包尾组成,其中“数据段”内容包括请求编码、系统编码、命令编码、密码、设备唯一标识、总包数、包号、指令参数。请求编码为请求的时间戳,系统编码ST统一规定为22,命令编码CN为该数据包的时间类型,访问密码、设备唯一标识在对接时由平台提供,指令参数为数据内容。通讯协议的数据结构如图4所示。
    通讯协议的数据结构
    图4 通讯协议的数据结构

    6.1.1通讯包结构组成

    名称 类型 长度 描述
    包头 字符 2 固定为##
    数据段长度 十进制整数 4 数据段的ASCII字符数。例如数据段的字符数为128,则写为“0128”
    数据段 字符 0<=n<=9999 变长的数据
    CRC校验 十六进制 4 数据段的校验结果,例如C901,如果CRC错,即执行超时
    包尾 字符 2 回车换行(\r\n)

    《污染物在线监控(监测)系统数据传输标准》简称《HJ212-2017》标准PDF文档可以从中华人民共和国生态环境部的官网下载,具体地址为:HJ212-2017》标准PDF文档
    如下图所示:
    HJ212-2017标准下载
    目前HJ212标准协议已经发布了两个版本,一个是HJ/T 212-2005,另一个是 HJ 212-2017,最新的HJ 212-2017下载地址为:污染物在线监控(监测)系统数据传输标准(HJ 212-2017代替HJ/T 212-2005)

    基于C++的HJ212解析类

    下面是一个使用C++写的212解析类 GB212:
    GB212.h

    #pragma once
    #include <type.h>
    #include <func.h>
    
    // 国标 212
    // 所有的通讯包都是由ASCII码
    class GB212
    {
    public:
    	typedef std::vector<GB212> GB212Array;
    	typedef std::vector<StringMap> DataItemArray;
    	// 切分数据
    	static void split_kv(DataItemArray& _return, const String& cp) {
    		auto arr1 = Math::Tools::split(cp, ";", true);
    		for (auto& i : arr1) {
    			StringMap item;
    			auto arr2 = Math::Tools::split(i, ",", true);
    			for (auto& j : arr2) {
    				auto arrkv = Math::Tools::split(j, "=", false);
    				if (arrkv.size() == 2) {
    					item.insert(std::make_pair(arrkv[0], arrkv[1]));
    				}
    			}
    			_return.emplace_back(item);
    		}
    	}
    	// 组合数据
    	static String join_kv(const DataItemArray& arr) {
    		StringArray item;
    		for (auto& i : arr) {
    			StringArray arrkv;
    			for (auto& j : i) {
    				arrkv.emplace_back(j.first + "=" + j.second);
    			}
    			item.emplace_back(Math::Tools::join(arrkv, ","));
    		}
    		return Math::Tools::join(item, ";");
    	}
    	// 数据区
    	struct DataCP {
    		DataCP() {}
    		DataCP(const String& s) {
    			DataItemArray arr;
    			split_kv(arr, s);
    			for (auto& i : arr) {
    				auto kvlen = i.size();
    				String key;
    				for (auto& j : i) {
    					// 对指定监测因子的项,统一使用因子代表
    					if (j.first == "PolId") {
    						key = j.second;
    						this->Value[key] = StringMap();
    						continue;
    					}
    					String name, field;
    					// 查询是否包含标准协议中的设备状态
    					auto f1 = j.first.find("SB");
    					auto f2 = j.first.find("-");
    					if (f2 != String::npos && f1 != 0) {
    						// a20004-Rtd   name-field
    						name = j.first.substr(0, f2);
    						field = j.first.substr(f2 + 1);
    						// i11001-Info   field
    						if (field == "Info") {
    							field = name;
    						}
    						else {
    							key = name;
    						}
    					}
    					else {
    						if (j.first == "DataTime") {
    							this->DataTime = j.second;
    						}
    						name = j.first;
    						key = name;
    						field = "value";
    					}
    					this->Value[key].insert(std::make_pair(field, j.second));
    				}
    			}
    			// 如果不包含DataTime字段,则将当前时间作为数据时间
    			if (this->Value.find("DataTime") == this->Value.end()) {
    				this->DataTime = Math::Date::getnow("%04d%02d%02d%02d%02d%02d");
    			}
    		}
    		void clear() {
    			this->Value.clear();
    		}
    		std::map<String, StringMap> Value;
    		String DataTime;
    	};
    	// 数据段
    	struct Data {
    		String QN;		// 请求编码 20字符	QN=YYYYMMDDHHmmssZZZ
    		String ST;		// 系统编码 5字符		ST=21
    		String CN;		// 命令编码 7字符		CN=2011
    		String PW;		// 访问密码 9字符		PW=123456
    		String MN;		// 设备标识 27字符	MN=[0-9A-F]
    		String Flag = "4";	// 标志位   8整数		Flag=7	bit:000001(协议版本)0(是否有包号)0(是否需应答)
    		String PNUM;	// 总包数   9字符		PNUM=0000	[不分包则没有本字段]
    		String PNO;		// 包号     8字符	PNO=0000	[不分包则没有本字段]
    		String CP;		// 指令参数	<=950字符	CP=&&数据区&&
    		DataCP CPs;
    		// 设置flag, bit从1开始
    		void set_flag(uint32 bit, bool enable) {
    			int32 flag = 4;
    			Math::Tools::to_int(flag, Flag);
    			enable ? SETBIT(flag, bit) : CLRBIT(flag, bit);
    			this->Flag = std::to_string(flag);
    		}
    		// 获取flag, bit从1开始
    		bool get_flag(uint32 bit) const {
    			int32 flag = 4;
    			Math::Tools::to_int(flag, Flag);
    			return GETBIT(flag, bit);
    		}
    		// 有效性
    		bool valid() const {
    			return bvalid;
    		}
    		// 长度
    		size_t size() const {
    #define ADDDataItem(name, subnum) result += name.size() + (name.empty() ? 0 : subnum);
    			size_t result = 0;
    			ADDDataItem(QN, 3);
    			ADDDataItem(ST, 3);
    			ADDDataItem(CN, 3);
    			ADDDataItem(PW, 3);
    			ADDDataItem(MN, 3);
    			ADDDataItem(Flag, 5);
    			ADDDataItem(PNUM, 5);
    			ADDDataItem(PNO, 4);
    			ADDDataItem(CP, 3);
    #undef ADDDataItem
    			return QN.size() + ST.size() + CN.size() + PW.size() + MN.size() + Flag.size() +
    				PNUM.size() + PNO.size() + CP.size();
    		}
    		// 构造函数
    		Data() {}
    		Data(const String& s){
    			CopyStr(s);
    		}
    		// 赋值构造
    		Data& operator=(const String& str) {
    			CopyStr(str);
    			return *this;
    		}
    		void CopyStr(const String& s) {
    			auto d1 = s.find("CP=&&");
    			auto d2 = s.find("&&", d1 + 5);
    			String tmp;
    			if (d1 != String::npos && d2 != String::npos) {
    				CP = s.substr(d1 + 5, d2 - d1 - 5);
    				CPs = CP;
    				tmp = s.substr(0, d1) + s.substr(d2 + 2);
    			}
    			else {
    				tmp = s;
    			}
    			StringMap it;
    			auto arr1 = Math::Tools::split(tmp, ";", true);
    			for (auto& i : arr1) {
    				auto arr2 = Math::Tools::split(i, "=", false);
    				if (arr2.size() == 2) {
    					it.insert(std::make_pair(arr2[0], arr2[1]));
    				}
    			}
    #define SETDataItem(name) name = it[#name];
    			SETDataItem(QN);
    			SETDataItem(ST);
    			SETDataItem(CN);
    			SETDataItem(PW);
    			SETDataItem(MN);
    			SETDataItem(Flag);
    			SETDataItem(PNUM);
    			SETDataItem(PNO);
    #undef SETDataItem
    			bvalid = true;
    		}
    		// 获取数据项
    		void CP2Object(DataItemArray& _return) const {
    			GB212::split_kv(_return, CP);
    		}
    		// 字符串输出
    		String toString() const {
    			String str;
    #define StrDataItem(name) if (name.size()) str += (#name"=" + name + ";");
    			StrDataItem(QN);
    			StrDataItem(ST);
    			StrDataItem(CN);
    			StrDataItem(PW);
    			StrDataItem(MN);
    			StrDataItem(Flag);
    			StrDataItem(PNUM);
    			StrDataItem(PNO);
    #undef StrDataItem
    			str += ("CP=&&" + CP + "&&");
    			return str;
    		}
    
    	private:
    		bool bvalid = false;
    	};
    	String	header = "##";		// 包头	2字符
    	String	datalen = "0000";	// 数据段长度		4整数,如长100,写为"0100"
    	Data	data;				// 数据段	<=1024
    	String	crc = "0000";		// CRC校验	4hex
    	String	tailer = "\r\n";	// 包尾	2字符
    
    	String	full;	// 全数据
    
    	// 输出
    	String toString() const {
    		auto datastr = data.toString();
    		uint16 jisuan_crc16 = Math::Byte::crc16_checksum((uint8*)datastr.data(), datastr.size());
    		char out[2048];
    		sprintf(out, "##%04d%s%04X\r\n", datastr.size(), datastr.c_str(), jisuan_crc16);
    		return out;
    	}
    	// 有效性
    	bool valid() const {
    		return bvalid;
    	}
    	// 有效性
    	bool is_data() const {
    		return this->data.CN == "2011" || this->data.CN == "2051" || this->data.CN == "2061" 
    			|| this->data.CN == "2031" || this->data.CN == "2041" || this->data.CN == "3020";
    	}
    	// 长度
    	size_t size() const {
    		return header.size() + datalen.size() + data.size() + crc.size() + tailer.size();
    	}
    	// 清空数据区
    	void clear_cp() {
    		this->data.CP.clear();
    		this->data.CPs.clear();
    	}
    	// 设置是否需应答
    	void set_need_reply(bool need) {
    		data.set_flag(1, need);
    	}
    	// 设置是否有包号
    	void set_need_subpack(bool need) {
    		data.set_flag(2, need);
    		if (!need) {
    			data.PNUM.clear();
    			data.PNO.clear();
    		}
    	}
    	// 是否需应答
    	bool is_need_reply() const {
    		return data.get_flag(1);
    	}
    	// 是否有包号
    	bool is_need_subpack() {
    		return data.get_flag(2);
    	}
    
    	// 数据区。字段与其值用‘=’连接;
    	// 在数据区中,同一项目的不同分类值间用‘,’来分隔,不同项目之间 用‘;’来分隔。
    	void combine() {
    		this->datalen = Math::Tools::to_string("%04d", this->data.size());
    		auto datastr = this->data.toString();
    		uint16 jisuan_crc = Math::Byte::crc16_checksum((uint8*)datastr.data(), datastr.size());
    		this->crc = Math::Tools::to_string("%04X", jisuan_crc);
    		this->bvalid = true;
    	}
    
    private:
    	GB212(const String& str) {
    		this->full = str;
    		size_t total_size = str.size();
    		datalen = str.substr(header.size(), datalen.size());
    		int32 data_len = 0;
    		Math::Tools::to_int(data_len, datalen);
    		if ((10 + data_len) > total_size/* || data_len == 0*/) {
    			return;
    		}
    		auto datastr = str.substr(header.size() + datalen.size(), data_len);
    		data = datastr;
    		crc = str.substr(header.size() + datalen.size() + data_len, crc.size());
    		uint16 jisuan_crc16 = Math::Byte::crc16_checksum((uint8*)datastr.data(), datastr.size());
    		int32 src_crc16;
    		if (!Math::Tools::to_int(src_crc16, crc, 16)) {
    			return;
    		}
    		if (src_crc16 != jisuan_crc16) {
    			return;
    		}
    		bvalid = true;
    	}
    
    	bool bvalid = false;
    
    public:
    	GB212(const GB212& r) {
    		this->header = r.header;
    		this->datalen = r.datalen;
    		this->data = r.data;
    		this->crc = r.crc;
    		this->tailer = r.tailer;
    		this->full = r.full;
    		this->bvalid = r.bvalid;
    	}
    	GB212(const String& st, const String& cn, const String& mn, const String& pw, const String& cp, bool need_reply) {
    		this->data.QN = Math::Date::getnow("%04d%02d%02d%02d%02d%02d000");
    		this->data.ST = st;
    		this->data.CN = cn;
    		this->data.MN = mn;
    		this->data.PW = pw;
    		this->data.CP = cp;
    		this->set_need_reply(need_reply);
    		this->combine();
    	}
    	// 获取响应报文
    	GB212 data_res(const char* cn) const {
    		GB212 out(*this);
    		out.data.ST = "91";
    		out.data.CN = cn;
    		out.clear_cp();
    		out.combine();;
    		return out;
    	}
    	// 解析
    	static void Parser(GB212Array& _return, const String& buffer) {
    		auto buffsize = buffer.size();
    		String it;
    		for (int i = 0; i < buffsize; i++)
    		{
    			if (i < buffsize - 1 && buffer[i] == '#' && buffer[i + 1] == '#') {
    				if (it.size()) {
    					_return.emplace_back(GB212(it));
    					it.clear();
    				}
    			}
    			it += (char)buffer[i];
    			if (i > 0 && buffer[i - 1] == '\r' && buffer[i] == '\n') {
    				if (it.size()) {
    					_return.emplace_back(GB212(it));
    					it.clear();
    				}
    			}
    		}
    		if (it.size()) {
    			_return.emplace_back(GB212(it));
    			it.clear();
    		}
    	}
    };
    

    相关的type.h类型定义文件如下:

    #ifndef _XM_DATA_TYPE_H_
    #define _XM_DATA_TYPE_H_
    
    // 自定义
    typedef unsigned char		uint8;
    typedef unsigned short		uint16;
    typedef unsigned int		uint32;
    #ifdef WIN32
    typedef unsigned __int64	uint64;
    typedef __int64	 int64;
    #else
    typedef unsigned long long	uint64;
    typedef long long	int64;
    #endif
    typedef char	int8;
    typedef short	int16;
    typedef int		int32;
    
    #include <string.h>
    
    // 数组
    #include <string>
    #include <vector>
    #include <map>
    #include <list>
    #include <functional>
    typedef std::string	String;
    typedef std::vector<uint8>		Uint8Array;
    typedef std::vector<uint16>		Uint16Array;
    typedef std::vector<uint32>		Uint32Array;
    typedef std::vector<int8>		Int8Array;
    typedef std::vector<int16>		Int16Array;
    typedef std::vector<int32>		Int32Array;
    typedef std::vector<int64>		Int64Array;
    typedef std::vector<uint64>		Uint64Array;
    typedef std::vector<float>		Float32Array;
    typedef std::vector<double>		Float64Array;
    typedef std::vector<std::string>	StringArray;
    typedef std::map<std::string, std::string> StringMap;
    typedef std::map<int32, std::string> Int32StringMap;
    typedef std::list<std::string> StringList;
    typedef std::vector<Uint8Array> Uint8sArray;
    
    
    typedef std::function<bool(const char* topic, const char* payload, int payload_size, int qos)> publish_handler;
    typedef std::function<void(const char* topic, const char* payload, int payload_size, int sid)> subscribe_handler;
    
    typedef std::function<int(const Uint8Array& req, Uint8Array& res)> request_handler;
    
    typedef std::function<void(int type, const String& data)> data_handler; 
    
    typedef std::function<void(const char* topic, const void* payload, size_t payload_size)> handler_subscribe;
    
    typedef std::function<void()> handler_empty;
    
    typedef std::function<void(void* d)> handler_void;
    
    #include "type_define.h"
    typedef void(*log_cb)(const char* file, const char* func, long line, log_level level, bool need_send, const char* msg);
    
    #include <thread>
    typedef std::thread Thread;
    
    #include <chrono>
    // 休眠毫秒
    #define msleep(millsec) std::this_thread::sleep_for(std::chrono::milliseconds(millsec));
    // 休眠秒
    #define ssleep(sec) std::this_thread::sleep_for(std::chrono::seconds(sec));
    
    
    #define macroStr(s) #s
    
    // 获取16位数据的高8位
    #define GET16H(d)		((d)>>8&0xff)
    // 获取16位数据的低8位
    #define GET16L(d)		((d)&0xff)
    // 通过高低8位构造16位数据
    #define GET16T(h,l)		(((h)<<8&0xff00)|((l)&0xff))
    // 交换16位数据的高低8位获取数据
    #define GET16S(d)		GET16T(GET16L(d),GET16H(d))
    
    #define GET32H(d)		((d)>>16&0xffff)
    #define GET32L(d)		((d)&0xffff)
    
    // 将对应位置1, n从1开始
    #define SETBIT(x, n) (x |= 1u << (n-1))
    // 将对应位置零, n从1开始
    #define CLRBIT(x, n) (x &= ~(1u << (n-1)))
    // 取对应位数, n从1开始
    #define GETBIT(x, n) (x & 1u << (n-1))
    
    #ifdef WIN32
    #ifndef snprintf
    #define snprintf sprintf_s
    #endif
    #endif
    
    
    // 类宏
    #define CLASS_DISCOPY(Class) \
    Class(const Class&) = delete;\
    Class& operator=(const Class&) = delete;
    
    
    // 类成员的set,get函数定义
    #define	PROERTY_DEFINE(type, var) void set_##var(const type var); const type get_##var() const;
    
    // 类成员的set,get函数实现
    #define	PROERTY_FUNC(type, var) void set_##var(const type var) { var##_ = var; } const type get_##var() const { return var##_; }
    
    // 类成员的set,get函数实现
    #define	PROERTY_CLASS(Class, type, var) void Class::set_##var(const type var) { d_ptr->set_##var(var); } const type Class::get_##var() const { return d_ptr->get_##var(); }
    
    
    // 删除内存
    #define ReleaseObj(ptr) if(ptr) delete ptr;
    #define ReleaseObj2(ptr) if(ptr) { delete ptr; ptr = nullptr; }
    
    
    #endif
    
    

    功能呢函数定义文件func.h如下所示:

    #pragma once
    #include "type.h"
    #ifdef _WIN32
    #include <io.h>
    #include <direct.h>
    #else
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #endif
    
    namespace Math
    {
    	class Byte
    	{
    	public:
    		// bytes转换成BCD码
    		static Uint8Array bytes2bcd(const Uint8Array& data)
    		{
    			Uint8Array out;
    			// 比如 20 18 转换成 0x20 0x18
    			for (size_t i = 0; i < data.size(); i++)
    			{
    				const uint8 d = data[i];
    				out.push_back(d / 10 * 16 + d % 10);
    			}
    			return out;
    		}
    
    		// BCD码转换成bytes
    		static Uint8Array bcd2bytes(const Uint8Array& data)
    		{
    			Uint8Array out;
    			// 比如 0x20 0x18 转换成 20 18
    			for (size_t i = 0; i < data.size(); i++)
    			{
    				const uint8 d = data[i];
    				out.push_back(d / 16 * 10 + d % 16);
    			}
    			return out;
    		}
    
    		// BCD码转换成bytes
    		static Uint8Array bcd2bytes(const uint8* buff, uint32 buffsize)
    		{
    			Uint8Array out(buff, buff + buffsize);
    			return bcd2bytes(out);
    		}
    
    		// 计算16位校验和
    		static uint16 getsum(const uint8* buff, uint32 buffsize)
    		{
    			uint32 sum = 0;
    			for (uint32 i = 0; i < buffsize; i++)
    			{
    				sum += (uint8)buff[i];
    			}
    			return sum;
    		}
    
    		// 计算32位校验和
    		static uint32 getsum32(const uint8* buff, uint32 buffsize)
    		{
    			uint32 sum = 0;
    			for (uint32 i = 0; i < buffsize; i++)
    			{
    				sum += (uint8)buff[i];
    			}
    			return sum;
    		}
    
    		// 计算CRC16循环冗余校验
    		static uint16 crc16_checksum(uint8 *puchMsg, uint32 usDataLen)
    		{
    			uint32 crc_reg, check;
    			crc_reg = 0xFFFF;
    			for (uint32 i = 0; i < usDataLen; i++) {
    				crc_reg = (crc_reg >> 8) ^ puchMsg[i];
    				for (uint32 j = 0; j < 8; j++) {
    					check = crc_reg & 0x0001;
    					crc_reg >>= 1;
    					if (check == 0x0001) {    
    						crc_reg ^= 0xA001;   
    					}
    				}
    			} 
    			return crc_reg;
    		}
    	};
    	
    	// 时间
    	class Date
    	{
    	public:
    		// 获取unix时间戳
    		static time_t now_unix() {
    			return time(0);
    		}
    		// 获取本地时间tm
    		static tm gettm(const time_t unix_time = 0)
    		{
    			time_t t = unix_time == 0 ? time(0) : unix_time;
    			tm tt;
    #ifdef WIN32
    			localtime_s(&tt, &t);
    #else
    			localtime_r(&t, &tt);
    #endif
    			return tt;
    		}
    		// 获取当前时间的BCD码20190308150102
    		static Uint8Array getbcd(const time_t unix_time = 0)
    		{
    			time_t t = unix_time <= 0 ? time(0) : unix_time;
    			Uint8Array _return;
    			tm tt = gettm(t);
    			int year = tt.tm_year + 1900;
    			_return.push_back(year / 100);
    			_return.push_back(year % 100);
    			_return.push_back(tt.tm_mon + 1);
    			_return.push_back(tt.tm_mday);
    			_return.push_back(tt.tm_hour);
    			_return.push_back(tt.tm_min);
    			_return.push_back(tt.tm_sec);
    			return Byte::bytes2bcd(_return);
    		}
    		// 将unix时间戳转换成本地时间, fmt默认格式 %04d-%02d-%02d %02d:%02d:%02d
    		static std::string unix2str(const time_t unix_time, const char* fmt = "%04d-%02d-%02d %02d:%02d:%02d")
    		{
    			tm tt = gettm(unix_time);
    			char date[64];
    			snprintf(date, sizeof(date), fmt, tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec);
    			return date;
    		}
    		// 获取当前时间字符串,默认格式 2018-10-01 01:10:20
    		static std::string getnow(const char* fmt = "%04d-%02d-%02d %02d:%02d:%02d")
    		{
    			return unix2str(::time(0), fmt);
    		}
    
    		// 转换字符串时间的格式, 输入顺序需和输出顺序一致, %04d-%02d-%02d %02d:%02d:%02d
    		static std::string convertfmt(const char* indatetime, const char* infmt, const char* outfmt)
    		{
    			int year, month, day, hour, minute, second;
    			sscanf(indatetime, infmt, &year, &month, &day, &hour, &minute, &second);
    			char out[64];
    			sprintf(out, outfmt, year, month, day, hour, minute, second);
    			return out;
    		}
    
    		// 获取输入字符串时间时间戳,默认格式 2018-10-01 01:10:20, %04d-%02d-%02d
    		static time_t getunix(const char* indatetime, const char* infmt = "%04d-%02d-%02d %02d:%02d:%02d")
    		{
    			int year, month, day, hour, minute, second;
    			sscanf(indatetime, infmt, &year, &month, &day, &hour, &minute, &second);
    			return gettime(year, month, day, hour, minute, second);
    		}
    
    		// 构造时间
    		static time_t gettime(int year, int month, int day, int hour = 0, int minute = 0, int second = 0)
    		{
    			tm tt = { 0 };
    			tt.tm_year = year - 1900;
    			tt.tm_mon = month - 1;
    			tt.tm_mday = day;
    			tt.tm_hour = hour;
    			tt.tm_min = minute;
    			tt.tm_sec = second;
    			return mktime(&tt);
    		}
    
    	};
    
    	//  定时器
    	class Timer
    	{
    		// 毫秒时钟
    		typedef std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> millisecClock_type;
    	public:
    		// 秒
    		static time_t now_s() {
    			return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    		}
    		// 毫秒
    		static time_t now_ms() {
    			return std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
    			// return system_clock::to_time_t(time_point_cast<milliseconds>(system_clock::now()));
    		}
    		// 微秒
    		static time_t now_mms() {
    			return std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
    		}
    		// 条件休眠,单位秒
    		static inline void sleep_if(volatile bool& condition, int32 sec) {
    			auto t0 = now_s();
    			while (condition && (now_s() - t0) < sec)
    			{
    				msleep(10);
    			}
    		}
    		// 条件休眠,单位毫秒
    		static inline void usleep_if(volatile bool& condition, int32 millsec) {
    			auto t0 = now_ms();
    			while (condition && (now_ms() - t0) < millsec)
    			{
    				msleep(10);
    			}
    		}
    	public:
    		typedef std::function<void()> timer_handler;
    		void start(timer_handler func, int timeout_ms)
    		{
    			cancel();
    			continue_ = true;
    			auto t1 = Timer::now_ms();
    			thread_ = std::thread([this, func, t1, timeout_ms]() {
    				while (continue_ && (Timer::now_ms() - t1 < timeout_ms))
    				{
    					msleep(10);
    				}
    				func();
    			});
    		}
    		void cancel()
    		{
    			continue_ = false;
    			if (thread_.joinable()) {
    				thread_.join();
    			}
    		}
    
    	private:
    		std::thread		thread_;
    		volatile bool	continue_ = false;
    	};
    
    	// 文件相关
    	class File
    	{
    	public:
    		// 多级创建文件夹,path不包含文件名
    		static bool mk_dirs(const char* path)
    		{
    			if (!path) {
    				return false;
    			}
    			int32 len = strlen(path);
    			if (len > 256) {
    				return false;
    			}
    			const char* p1 = path;
    			const char* p2;
    			while (p1 - path < len)
    			{
    				p2 = strchr(p1, '/');
    				if (!p2) {
    					p2 = strchr(p1, '\\');
    				}
    				if (!p2) {
    					p2 = path + len;
    				}
    				char dir[64] = { 0 };
    				memcpy(dir, path, p2 - path + 1);
    #ifdef _WIN32
    				if (_access(dir, 0) == 0 || _mkdir(dir) == 0)
    #else
    				if (access(dir, R_OK | W_OK) == 0 || mkdir(dir, 0666) == 0)
    #endif
    				{
    					p1 = p2 + 1;
    					continue;
    				}
    				return false;
    			}
    			return true;
    		}
    		// 文件是否存在
    		static bool exist(const char* path) {
    #ifdef _WIN32
    			return _access(path, 0) == 0;
    #else
    			return access(path, R_OK | W_OK) == 0;
    #endif
    		}
    		// 删除文件
    		static bool remove(const char* filename) {
    			return ::remove(filename) == 0;
    		}
    		// 重命名文件
    		static bool rename(const char* oldfile, const char* newfile) {
    			return ::rename(oldfile, newfile) == 0;
    		}
    	};
    
    	class Tools
    	{
    	public:
    		// 将数字类型转成字符类型. fmt同printf中的fmt
    		static inline String to_string(const char* fmt, const uint32 val)
    		{
    			char tmp[256];
    			sprintf(tmp, fmt, val);
    			return tmp;
    		}
    		// 替换字符
    		static inline String replace(const String& str, const String& search, const String& dest)
    		{
    			String result;
    			size_t start = 0, end = 0;	
    			while (start < str.size())
    			{
    				end = str.find_first_of(search, start);
    				result += str.substr(start, end - start);
    				start = end + search.size();
    				if (end == String::npos) {
    					break;
    				}
    				result += dest;
    			}
    			return result;
    		}
    
    		// 字符串分割函数
    		static inline StringArray split(const std::string& s, const std::string& sep, const bool compress = false)
    		{
    			StringArray out;
    			int pos = 0;
    			int index = -1;
    			while ((index = s.find(sep, pos)) != s.npos)
    			{
    				if (index - pos == 0)
    				{
    
    				}
    				// s.substr(pos, index - pos == 0 ? 1 : index - pos);
    				std::string it = index - pos == 0 ? "" : s.substr(pos, index - pos);
    				if (compress && it == "") // 压缩 index - pos == sep.size() && 
    				{
    
    				}
    				else // 不用压缩
    				{
    					out.push_back(it);
    				}
    
    				pos = index + sep.size();
    			}
    			if (pos < (int32)s.size())
    			{
    				out.push_back(s.substr(pos));
    			}
    			else if (pos == (int32)s.size() && !compress)
    			{
    				out.push_back("");
    			}
    			return out;
    		}
    
    		// 数据组合字符串
    		static inline String join(const StringArray& arr, const std::string& sep) {
    			String result;
    			for (auto& i : arr) {
    				if (result.empty()) {
    					result = i;
    					continue;
    				}
    				result += sep + i;
    			}
    			return result;
    		}
    		
    		// 转换hex到字符串显示
    		static inline String hex2str(const char* buff, const size_t buffsize, const char* sep = "", bool is_case = false) {
    			String out;
    			char ch[4];
    			const char* fmt = is_case ? "%02x" : "%02X";
    			for (size_t i = 0; i < buffsize; i++) {
    				sprintf(ch, fmt, buff[i] & 0xFF);
    				if (out.empty()) {
    					out = ch;
    				}
    				else {
    					out += sep;
    					out += ch;
    				}
    			}
    			return out;
    		}
    
    		// 转换字符串显示到hex数组
    		static inline String str2hex(const String& buff, const String& sep = "") {
    			String out;
    			size_t buffsize = buff.size();
    			StringArray items;
    			if (sep.empty() && buffsize % 2 == 0) {
    				for (size_t i = 0; i < buffsize / 2; i++) {
    					items.emplace_back(buff.substr(i * 2, 2));
    				}
    			}
    			else if (sep.size()) {
    				items = split(buff, sep, true);
    			}
    			for (auto& i : items) {
    				int ch;
    				if (!to_int(ch, i, 16)) {
    					return out;
    				}
    				out.push_back(ch & 0xff);
    			}
    			return out;
    		}
    
    		// 获取字符串中数字
    		static inline bool to_int(int& _return, const std::string& buff, int base = 10, size_t offset = 0, size_t count = String::npos)
    		{
    			_return = 0;
    			if (buff.empty())
    				return false;
    			try {
    				_return = std::stoi(buff.substr(offset, count), 0, base);
    			}
    			catch (...) {
    				return false;
    			}
    			return true;
    		}
    
    		// 截取字符串buff中从offset开始的count个字符转换成double
    		static inline bool to_double(double& _return, const std::string& buff, size_t offset = 0, size_t count = String::npos)
    		{
    			_return = 0;
    			if (buff.empty())
    				return false;
    			try {
    				_return = std::stod(buff.substr(offset, count));
    			}
    			catch (...) {
    				return false;
    			}
    			return true;
    		}
    
    		// 截取字符串buff中从offset开始的count个字符转换成float
    		static inline bool to_float(float& _return, const std::string& buff, size_t offset = 0, size_t count = String::npos)
    		{
    			_return = 0;
    			if (buff.empty())
    				return false;
    			try {
    				_return = std::stof(buff.substr(offset, count));
    			}
    			catch (...) {
    				return false;
    			}
    			return true;
    		}
    
    		// 去掉尾部的特定字符
    		static inline String trim(const String& buffer, const String& sep = " ") {
    			String result;
    			size_t pos = 0;
    			do
    			{
    				size_t n = buffer.find(sep, pos);
    				result += buffer.substr(pos, n - pos);
    				pos = (n != String::npos) ? (n + sep.size()) : n;
    			} while (pos < buffer.size());
    			return result;
    		}
    	};
    }
    
    

    使用GB212类进行212协议包的解析,

    例如有如下212协议数据报文:

    ##0285QN=20190925181031464;ST=22;CN=2061;PW=BF470F88957588DE902D1A52;MN=Z13401000010301;Flag=5;CP=&&DataTime=20190924220000;a34006-Avg=2.69700,a34006-Flag=N;a34007-Avg=7.96600,a34007-Flag=N;a34048-Avg=3.30600,a34048-Flag=N;a34047-Avg=7.35700,a34047-Flag=N;a34049-Avg=10.66300,a34049-Flag=N&&C181\r\n
    

    相关解析代码如下:

    	// parse gb212 format data
    	GB212::GB212Array arr;
    	GB212::Parser(arr, body);
    	// 实时,最新,报文
    	for (auto& i : arr) {
    		if (i.valid()) {
    			// 实时数据, 设置回复数据 , 推送
    			if (i.is_data()) {
    			    // 解析处理212数据报文
    				task_real_data(i, res);
    			}
    		}
    	}
    

    关于HJ212Receiver项目

    由于经常需要采用C++接收和解析环保212协议,所以采用C++ Boost asio库编写了一个TCP接收服务端,并解析HJ212-2017协议数据,上传到我的GitHub上面,仓库地址为:https://github.com/ccf19881030/HJ212Receiver,已经在Windows10系统下使用VS2017进行测试过,并且在CentOS8系统下使用Cmake进行编译测试。有需要的话可以自行下载:

    git clone https://github.com/ccf19881030/HJ212Receiver.git
    
    展开全文
  • 1.hj212 2015、2017皆可以测试 2.本项目测试都可以使用,主要数据存储为mongdb数据库, 3.根据mn和站点号码绑定在一起 4.数据异常,数据超标做了一些处理 本文参考博客使用更加方便!...
  • 污染物在线监控(监测)系统数据传输标准(HJ 212-2017)污染物在线监控(监测)系统数据传输标准(HJ 212-2017)
  • 之前写过一篇关于环保 HJ212协议解析的博文,有不少做环保行业的人文我关于HJ212-2017协议怎么解析,由于我主要是做C++开发的,之前采用C++ Boost asio库编写了一个TCP接收服务端,并解析HJ212-2017协议数据,上传到...

    基于Qt5.14.2的HJ212 TCP服务端接收解析入库程序

    之前写过一篇关于环保 HJ212协议解析的博文,有不少做环保行业的人咨询我关于HJ212-2017协议怎么解析,由于我主要是做C++开发的,之前采用C++ Boost asio库编写了一个TCP接收服务端,并解析HJ212-2017协议数据,上传到我的GitHub上面,仓库地址为:https://github.com/ccf19881030/HJ212Receiver,已经在Windows10系统下使用VS2017进行测试过,并且在CentOS8系统下使用Cmake进行编译测试。有需要的话可以自行下载:

    git clone https://github.com/ccf19881030/HJ212Receiver.git
    

    由于Qt自带了很多库,比如说网络库,SQL数据库接口等,使用起来比Boost库方便很多,于是使用Qt写了一个TCP服务端用于接收和解析HJ212-2017报文,然后入库到MySQL数据库中。最终的程序效果如下图所示:
    TCP服务端
    相关源代码我已经上传到Github上面,仓库地址为:https://github.com/ccf19881030/GB212ReceiverApp
    需要的话可以自行下载:

    git clone https://github.com/ccf19881030/GB212ReceiverApp
    

    GB212ReceiverApp仓库

    该程序是基于Qt5.14.2的HJ212-2017 TCP接收服务端,解析入库程序。数据库采用MySQL ODBC,使用Qt库编写TCP服务器,接收到HJ212-2017报文后解析入库到MySQL数据库中。

    注意事项

    一、开发环境:

    • 1、使用VS2017+QT5.14.2作为开发环境,已经测试过基于X64位的Debug和Relase版本
    • 2、使用到的技术有Qt、ODBC连接库mysql-connector-odbc-8.0.24-winx64.msi,关于在Windows10中使用Qt连接MySQL数据库,可以参考CSDN上的一篇博客:连接MySQL数据库(通过ODBC方式连接)
      https://blog.csdn.net/joey_ro/article/details/105411135
    • 3、需要将 MySQL数据库脚本 目录下的test_hj212_db.sql导入到你本地的MySQL数据库中,可以直接使用Navicat Premium 12等客户端工具,或者其他的数据库客户端工具,为了简便,目前数据库连接信息没有写入到配置文件中,直接硬编码到hj212dialog.cpp文件中,需要根据自己MySQL数据库中的连接情况相应修改第31行代码:dbprocess_.setDBInfo(“127.0.0.1”, 3306, “root”, “1030”, “test_hj212_db”);
    • 4、可以使用windeployqt.exe和Inno Setup Compiler对编译好的release x64位的Qt C++程序进行打包。

    二、前提条件:

    • 1、安装Qt5.14.2等版本
    • 2、安装MySQL数据库
    • 3、将MySQL数据库脚本 目录下的test_hj212_db.sql导入到你本地的MySQL数据库中
    • 4、安装mysql-connector-odbc-8.0.24-winx64.msi,并配置mysql ODBC数据源,具体方法参考https://blog.csdn.net/joey_ro/article/details/105411135这篇博客
    • 5、修改hj212dialog.cpp文件中第31行代码:dbprocess_.setDBInfo(“127.0.0.1”, 3306, “root”, “1030”, “test_hj212_db”);
      改成自己的MySQL数据库配置
    • 6、编译打包,可以先使用Qt5.14.2的windeployqt工具将相应的Qt5的dll库拷贝到exe可执行程序目录,然后再使用innosetup-6.1.2.exe工具进行打包。

    MySQL数据库

    数据库采用的是MySQL,SQL数据库脚本如下:

    /*
     Navicat Premium Data Transfer
    
     Source Server         : 本地MySQL数据库
     Source Server Type    : MySQL
     Source Server Version : 80016
     Source Host           : localhost:3306
     Source Schema         : test_hj212_db
    
     Target Server Type    : MySQL
     Target Server Version : 80016
     File Encoding         : 65001
    
     Date: 22/04/2021 21:15:56
    */
    
    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for monitordata
    -- ----------------------------
    DROP TABLE IF EXISTS `monitordata`;
    CREATE TABLE `monitordata`  (
      `mn` varchar(255) CHARACTER SET utf8 COLLATE utf8_croatian_ci NOT NULL COMMENT '站点编码',
      `datatime` datetime(0) NOT NULL COMMENT '数据时间',
      `paramCode` varchar(255) CHARACTER SET utf8 COLLATE utf8_croatian_ci NOT NULL COMMENT '监测因子编码',
      `val` double(255, 0) NULL DEFAULT NULL COMMENT '监测数值',
      `mark` varchar(255) CHARACTER SET utf8 COLLATE utf8_croatian_ci NULL DEFAULT NULL COMMENT '监测数据标记',
      PRIMARY KEY (`mn`, `datatime`, `paramCode`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_croatian_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of monitordata
    -- ----------------------------
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a01001', -1, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a01002', 22, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a01006', 1032, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a01007', 4, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a01008', 353, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a05002', 34, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a05024', 6, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a21002', 18, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a21003', 5, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a21004', 9, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a21005', 1, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a21026', 2, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a24088', 35, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a34002', 124, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a34004', 25, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a34006', 874, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a34007', 2679, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a34049', 3553, 'N');
    INSERT INTO `monitordata` VALUES ('LB4200001', '2021-01-07 14:38:00', 'a99999', 69, 'N');
    
    -- ----------------------------
    -- Table structure for station
    -- ----------------------------
    DROP TABLE IF EXISTS `station`;
    CREATE TABLE `station`  (
      `mn` varchar(255) CHARACTER SET utf8 COLLATE utf8_croatian_ci NULL DEFAULT NULL COMMENT '站点编码',
      `stationname` varchar(255) CHARACTER SET utf8 COLLATE utf8_croatian_ci NULL DEFAULT NULL COMMENT '站点名称'
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_croatian_ci ROW_FORMAT = Dynamic;
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    MySQL数据库

    程序运行

    在环保项目中一般一个空气自动站、水站或者土壤站点一般涉及到的表比较多,其中有监测数据表、站点表、因子编码表等等。
    首先看一下MySQL数据库中的monitordata表结构:
    monitordata表结构
    monitordata表中的数据刚开始把它清空:
    monitordata表的数据

    再运行程序,
    HJ212-2017服务端接收和解析入库程序
    首先设置好TCP服务端的监听端口号,然后再点击上面程序界面的【监听】按钮;然后使用网络调试助手开一个TCP客户端,连接上面的TCP服务端,
    TCP客户端
    输入HJ212-2017报文,然后点击发送。可以看到TCP服务端接收并解析数据显示的效果,如下图所示:
    程序接收示例
    可以看到MySQL数据库test_hj212_db的表monitordata表`中插入了几条监测数据记录,如下图所示:
    monitordata表

    展开全文
  • HJ212_CRC16校检 _Python

    2020-08-28 18:25:33
    HJ212_CRC16校检 代码 def crcFast(text): """ hj 212-2017 crc16效验 :param text: 待效验的字符串 :return: result """ data = bytearray(text, encoding='utf-8') crc = 0xffff dxs = 0xa001 for i in ...
  • HJ 212-2017 污染物在线监控(监测)系统数据传输标准,可用于气体监测和水质监测,发送给国控网
  • java解析hj212.rar

    2021-06-01 10:23:45
    封装好的环保212协议代码,包括HJ2121的各个方面,直接可用。
  • HJ 212-2005 污染源在线自动监控(监测)系统数据传输标准
  • HJ 212-2017 污染源在线自动监控(监测)系统数据传输标准,替代 2005版本

空空如也

空空如也

1 2 3 4 5 6
收藏数 118
精华内容 47
关键字:

hj212