网站建设教育板块,海淀seo搜索引擎优化公司,中海建筑建设有限公司网站,网站建设设计技术方案模板下载HJ212协议C#代码解析实现
HJ212协议是环保中一个非常重要的标准协议#xff08;字符串协议#xff09;#xff0c;之前写了两篇C HJ212协议解析的相关博文#xff1a;
环保 HJ212协议解析基于Qt5.14.2的HJ212 TCP服务端接收解析入库程序 最近在学习C##xff0c;所以打算…HJ212协议C#代码解析实现
HJ212协议是环保中一个非常重要的标准协议字符串协议之前写了两篇C HJ212协议解析的相关博文
环保 HJ212协议解析基于Qt5.14.2的HJ212 TCP服务端接收解析入库程序 最近在学习C#所以打算基于C#重新实现一遍算是熟悉一下C#的基本语法。
HJ212协议简介
由于是做环保相关的有时需要对212协议进行拆包和解包。HJ212协议是一种字符串协议数据传输通讯包主要由包头、数据段长度、数据段、CRC校验、包尾组成其中“数据段”内容包括请求编码、系统编码、命令编码、密码、设备唯一标识、总包数、包号、指令参数。请求编码为请求的时间戳系统编码ST统一规定为22命令编码CN为该数据包的时间类型访问密码、设备唯一标识在对接时由平台提供指令参数为数据内容。通讯协议的数据结构如图4所示。 图4 通讯协议的数据结构
6.1.1通讯包结构组成
名称类型长度描述包头字符2固定为##数据段长度十进制整数4数据段的ASCII字符数。例如数据段的字符数为128则写为“0128”数据段字符0n9999变长的数据CRC校验十六进制4数据段的校验结果例如C901如果CRC错即执行超时包尾字符2回车换行\r\n
《污染物在线监控监测系统数据传输标准》简称《HJ212-2017》标准PDF文档可以从中华人民共和国生态环境部的官网下载具体地址为HJ212-2017》标准PDF文档 如下图所示 目前HJ212标准协议已经发布了两个版本一个是HJ/T 212-2005另一个是 HJ 212-2017最新的HJ 212-2017下载地址为污染物在线监控监测系统数据传输标准(HJ 212-2017代替HJ/T 212-2005)
## 基于C#的HJ212解析类
首先创建一个基于C# .Net的库项目名称为HJ212ParseLibrary相关类实现代码如下
(1)、通用工具类 CommonUtils
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HJ212ParseLibrary
{/// summary/// 通用工具类/// /summarypublic class CommonUtils{/// summary/// 切分数据/// /summary/// param namecp/param/// returns/returnspublic static ListDictionarystring, string SplitKV(string cp){ListDictionarystring, string resultList new ListDictionarystring, string();var arr1 cp.Split(;);foreach (var i in arr1){Dictionarystring, string item new Dictionarystring, string();var arr2 i.Split(,);foreach(var j in arr2){var arrKv j.Split();if (arrKv.Length 2){item.Add(arrKv[0], arrKv[1]); }}resultList.Add(item);}return resultList;}/// summary/// 组合数据/// /summary/// param name/param/// returns/returnspublic static string JoinKV(ListDictionarystring, string myList){Liststring item new Liststring();foreach (var i in myList){Liststring arrKv new Liststring();foreach (var j in i){arrKv.Add(j.Key j.Value);}item.Add(Join(arrKv, ,));}return Join(item, ;);}/// summary/// 数据组合字符串/// /summary/// param namearrKv字符串列表/param/// param namesep分隔符/param/// returns/returns/// exception crefNotImplementedException/exceptionprivate static string Join(Liststring arrKv, char sep ){string result ;bool isFirst true;foreach (var item in arrKv){if (isFirst){result item;isFirst false;continue;}result sep item;}return result;}/// summary/// 将字符串数组按照sep分隔符分割之后生成新的字符串/// /summary/// param namearrKv字符串数组/param/// param namesep分隔符/param/// returns/returnsprivate static string Join(string[] arrKv, char sep ){string result ;bool isFirst true;foreach (var item in arrKv){if (isFirst){result item;isFirst false;continue;}result sep item;}return result;}/// summary/// 计算计算字节数组的CRC-16校验码/// /summary/// param namebyteArr字节数组/param/// returnsCRC-16校验码/returnspublic static uint GetCrc16(byte[] byteArr){uint crc_reg, check;int i, j;crc_reg 0xFFFF;for (i 0; i byteArr.Length; i){crc_reg (crc_reg 8) ^ byteArr[i];for (j 0; j 8; j){check crc_reg 0x0001;crc_reg 1;if (check 0x0001){crc_reg ^ 0xA001;}}}return crc_reg;}/// summary/// 将对应位置置1n从1开始/// /summary/// param namex/param/// param namen/parampublic static void SetBit(int x, int n){x | 1 (n - 1);}/// summary/// 将对应位置置0n从1开始/// /summary/// param namex/param/// param namen/parampublic static void CtrlBit(int x, int n){x ~(1 (n - 1));}public static int GetBit(int x, int n){return x (1 (n - 1));}}
}(2)、HJ212协议 CP数据部分 HJ212DataCP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HJ212ParseLibrary
{/// summary/// HJ212协议 CP数据部分/// /summarypublic class HJ212DataCP{public HJ212DataCP(){}public HJ212DataCP(string s){ListDictionarystring, string arr CommonUtils.SplitKV(s);foreach (var i in arr){int kvlen i.Count;string key ;foreach (var j in i){// 对指定监测因子的项统一使用因子代表if (j.Key PolId){key j.Value;this.KVDict[key] new Dictionarystring, string();continue;}string name, field;// 查询是否包含标准协议中的设备状态var f1 j.Key.IndexOf(SB);var f2 j.Key.IndexOf(-);if (f2 ! -1 f1 ! 0){// a20004-Rtd name-fieldname j.Key.Substring(0, f2);field j.Key.Substring(f2 1);// i11001-Info fieldif (field Info){field name;}else{key name;}}else{if (j.Key DataTime){this.DataTime j.Value;}name j.Key;key name;field value;}if (this.KVDict.ContainsKey(key)){// 如果存在key则追加this.KVDict[key].Add(field, j.Value);} else{// 如果不存在该key则创建一个字典对象并添加到KVDict中Dictionarystring, string dict new Dictionarystring, string();dict.Add(field, j.Value);this.KVDict.Add(key, dict);}}}}public void Clear(){this.KVDict.Clear();}public Dictionarystring, Dictionarystring, string KVDict new Dictionarystring, Dictionarystring, string(); // 监测因子数据字典string DataTime;}
}
(3)、HJ212协议 数据区 HJ212Data
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HJ212ParseLibrary
{/// summary/// HJ212协议 数据区/// 请求编码QN 系统编码ST 命令编码CN 密码PW 设备唯一标识MN 标志位Flag 总包数PNUM 包号PNO 指令参数CP/// 其中指令CP字符数范围为[0,9899]CP数据区/// /summarypublic class HJ212Data{public string QN; // 请求编码 20字符 QNYYYYMMDDHHmmssZZZpublic string ST; // 系统编码 5字符 ST21public string CN; // 命令编码 7字符 CN2011public string PW; // 访问密码 9字符 PW123456public string MN; // 设备标识 27字符 MN[0-9A-F]public string Flag; // 标志位 8整数 Flag7 bit:000001(协议版本)0(是否有包号)0(是否需应答)public string PNUM; // 总包数 9字符 PNUM0000 [不分包则没有本字段]public string PNO; // 包号 8字符 PNO0000 [不分包则没有本字段]public string CP; // 指令参数 9899字符 CP数据区public HJ212DataCP CPs;private bool bValid false;// 设置Flagbit从1开始public void SetFlag(int bit, bool enable){int flag 4;flag Int32.Parse(Flag);if (enable){CommonUtils.SetBit(flag, bit);} else{CommonUtils.CtrlBit(flag, bit);}this.Flag flag.ToString();}// 获取Flagbit从1开始public int GetFlag(int bit){int flag 4;if (Int32.TryParse(Flag, out flag)){return CommonUtils.GetBit(flag, bit);}return CommonUtils.GetBit(flag, bit);}// 有效性public bool IsValid(){return bValid;}// 长度public int Size(){return QN.Length ST.Length CN.Length PW.Length MN.Length Flag.Length PNUM.Length PNO.Length CP.Length;}// 构造函数public HJ212Data(){}public HJ212Data(string s){CopyStr(s);}public void CopyStr(string s){// 查找数据段int d1 s.IndexOf(CP); // 数据段开始位置int d2 s.IndexOf(, d1 5); // 数据段结束位置String tmpStr ;if (d1 ! -1 d2 ! -1){CP s.Substring(d1 5, d2 - d1 - 5);CPs new HJ212DataCP(CP);tmpStr s.Substring(0, d1) s.Substring(d2 2);}else{tmpStr s;}Dictionarystring, string strDict new Dictionarystring, string();var arr1 tmpStr.Split(;);foreach (var i in arr1){var arr2 i.Split();if (arr2.Length 2){strDict.Add(arr2[0], arr2[1]);}}if (strDict.ContainsKey(QN)){QN strDict[QN];}if (strDict.ContainsKey(ST)){ST strDict[ST];}if (strDict.ContainsKey(CN)){CN strDict[CN];}if (strDict.ContainsKey(PW)){PW strDict[PW];}if (strDict.ContainsKey(MN)){MN strDict[MN];}if (strDict.ContainsKey(Flag)){Flag strDict[Flag];}if (strDict.ContainsKey(PNUM)){PNUM strDict[PNUM];}if (strDict.ContainsKey(PNO)){PNO strDict[PNO];}bValid true;}// 获取数据项public ListDictionarystring, string Cp2Object(){return CommonUtils.SplitKV(CP);}// 字符串输出public override string ToString(){string str ;if (!string.IsNullOrEmpty(QN)){str (QN QN ;);}if (!string.IsNullOrEmpty(ST)){str (ST ST ;);}if (!string.IsNullOrEmpty(CN)){str (CN CN ;);}if (!string.IsNullOrEmpty(PW)){str (PW PW ;);}if (!string.IsNullOrEmpty(MN)){str (MN MN ;);}if (!string.IsNullOrEmpty(Flag)){str (Flag Flag ;);}if (!string.IsNullOrEmpty(PNUM)){str (PNUM PNUM ;);}if (!string.IsNullOrEmpty(PNO)){str (PNO PNO ;);}str (CP CP );return str;}}
}(4)、HJ212协议实体类 HJ212
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;namespace HJ212ParseLibrary
{/// summary/// HJ212协议 整包包头数据段长度数据段CRC校验包尾组成/// ##0637ST22;CN2011;PW123456;MNLB4200001;CPDataTime20210107143500;a05002-Rtd0,a05002-FlagN;a21005-Rtd0.825,a21005-FlagN;a34006-Rtd874,a34006-FlagN;a24088-Rtd0,a24088-FlagN;a21003-Rtd5.656,a21003-FlagN;a21004-Rtd9.253,a21004-FlagN;a21002-Rtd17.894,a21002-FlagN;a05024-Rtd5.803,a05024-FlagN;a34007-Rtd2679,a34007-FlagN;a34002-Rtd123.97,a34002-FlagN;a34004-Rtd24.82,a34004-FlagN;a01006-Rtd1032.3,a01006-FlagN;a01002-Rtd20.9,a01002-FlagN;a21026-Rtd0.795,a21026-FlagN;a01007-Rtd3.3,a01007-FlagN;a34049-Rtd3553,a34049-FlagN;a01001-Rtd-0.6,a01001-FlagN;a99999-Rtd0,a99999-FlagN;a01008-Rtd349,a01008-FlagN3300/// /summarypublic class HJ212{public string header ##; // 包头 2字符public string dataLen 0000; // 数据段长度 4整数如长度128,写为0128HJ212Data data; // 数据段 0n9999public string crc 0000; // CRC校验码 4hexpublic string tailer \r\n; // 包尾 2字符public string full; // 全数据整包/// 输出public override string ToString(){string dataStr data.ToString();uint crc16 CommonUtils.GetCrc16(Encoding.UTF8.GetBytes(dataStr));return string.Format({0}{1:D4}{2}{3:X4}{4}, header, dataStr.Length, dataStr, crc16, tailer); // D4标示4位10进制数字X4表示4位16进制数}// 有效性public bool IsValid(){return bValid;}// 有效性public bool IsDataValid(){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;}// 长度public int Size(){return header.Length dataLen.Length data.Size() crc.Length tailer.Length;}// 清空数据区public void ClearCp(){this.data.CP ;this.data.CPs.Clear();}// 设置是否需要应答public void SetNeedReply(bool need){data.SetFlag(1, need);}// 设置是否有包号public void SetNeedSubPack(bool need){data.SetFlag(2, need);if (!need){data.PNUM ;data.PNO ;}}// 是否需应答public int IsNeedReply(){return data.GetFlag(1);}// 是否有包号public int IsNeedSubPack(){return data.GetFlag(2);}// 数据区。字段与其值用‘’连接// 在数据区中同一项目的不同分类值间用‘,’来分隔不同项目之间 用‘;’来分隔。public void Combine(){this.dataLen string.Format({0:D4, this.data.Size());string dataStr this.data.ToString();uint jisuanCrc CommonUtils.GetCrc16(Encoding.UTF8.GetBytes(dataStr));this.crc string.Format({0:D4, jisuanCrc);this.bValid true;}public HJ212(string str){this.full str;int totalSize str.Length;dataLen str.Substring(header.Length, dataLen.Length);int dLen Int32.Parse(dataLen);if ((10 dLen) totalSize) {return;}string dataStr str.Substring(header.Length dataLen.Length, dLen);data new HJ212Data(dataStr);crc str.Substring(header.Length dataLen.Length dLen, crc.Length);//uint jisuanCrc16 CommonUtils.GetCrc16(dataStr.ToCharArray());uint jisuanCrc16 CommonUtils.GetCrc16(Encoding.UTF8.GetBytes(dataStr));int srcCrc16 Int32.Parse(crc, System.Globalization.NumberStyles.HexNumber);if (srcCrc16 ! jisuanCrc16) {return;}bValid true;}public HJ212(HJ212 r){this.header r.header;this.dataLen r.dataLen;//this.data new HJ212Data(r.data.ToString());this.data r.data;this.crc r.crc;this.tailer r.tailer;this.full r.full;this.bValid r.bValid;}public HJ212(string st, string cn, string mn, string pw, string cp, bool needReply){this.data.QN DateTime.Now.ToString(yyyyMMDDHHMMss000);this.data.ST st;this.data.CN cn;this.data.PW pw;this.data.CP cp;this.SetNeedReply(needReply);this.Combine();}// 获取响应报文public HJ212 GetDataResponse(string cn){HJ212 outHJ212 new HJ212(this);outHJ212.data.ST 91;outHJ212.data.CN cn;outHJ212.ClearCp();outHJ212.Combine();return outHJ212;}/// summary/// 解析字符串获取HJ212协议数据列表/// /summary/// param namebuffer输入字符串/param/// returns返回解析后的HJ212协议列表/returnspublic static ListHJ212 Parse(string buffer){ListHJ212 hj212List new ListHJ212();int bufferSize buffer.Length;string strTemp ;for (int i 0; i bufferSize; i){if ( i bufferSize - 1 buffer[i] # buffer[i 1] #){if (strTemp.Length 0){hj212List.Add(new HJ212(strTemp));strTemp ;}}strTemp buffer[i];// 遍历到HJ212协议末尾末尾以\r\n结束if (i 0 buffer[i - 1] \r buffer[i] \n){if (strTemp.Length 0){hj212List.Add(new HJ212(strTemp));strTemp ;}}}if (strTemp.Length 0){hj212List.Add(new HJ212(strTemp));strTemp ;}return hj212List;}private bool bValid false;}
}测试程序
新建一个基于C# .Net的控制台程序ConsoleHJ212App然后输入如下测试代码
using HJ212ParseLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConsoleHJ212App
{public class Program{static void Main(string[] args){string buffer ##0637ST22;CN2011;PW123456;MNLB4200001;CPDataTime20210107143500;a05002-Rtd0,a05002-FlagN;a21005-Rtd0.825,a21005-FlagN;a34006-Rtd874,a34006-FlagN;a24088-Rtd0,a24088-FlagN;a21003-Rtd5.656,a21003-FlagN;a21004-Rtd9.253,a21004-FlagN;a21002-Rtd17.894,a21002-FlagN;a05024-Rtd5.803,a05024-FlagN;a34007-Rtd2679,a34007-FlagN;a34002-Rtd123.97,a34002-FlagN;a34004-Rtd24.82,a34004-FlagN;a01006-Rtd1032.3,a01006-FlagN;a01002-Rtd20.9,a01002-FlagN;a21026-Rtd0.795,a21026-FlagN;a01007-Rtd3.3,a01007-FlagN;a34049-Rtd3553,a34049-FlagN;a01001-Rtd-0.6,a01001-FlagN;a99999-Rtd0,a99999-FlagN;a01008-Rtd349,a01008-FlagN3300\r\n##0404ST22;CN2011;PW123456;MNLB4200001;CPDataTime20210107152600;a05002-Rtd1.755,a05002-FlagN;a21005-Rtd0.699,a21005-FlagN;a34006-Rtd845,a34006-FlagN;a24088-Rtd4.945,a24088-FlagN;a21003-Rtd3.877,a21003-FlagN;a21002-Rtd14.502,a21002-FlagN;a05024-Rtd10.389,a05024-FlagN;a34007-Rtd2859,a34007-FlagN;a21026-Rtd1.525,a21026-FlagN;a34049-Rtd3704,a34049-FlagN;a99999-Rtd6.7,a99999-FlagN9C01\r\n;ListHJ212 hj212List HJ212.Parse(buffer);foreach (HJ212 item in hj212List){Console.WriteLine(item.ToString());}}}
}并引用HJ212ParseLibrary库项目在VS2022中运行结果如下图所示
运行结果如下 可以看到我们输入的数据和解析到的数据是一致的有2个协议数据报文。