北斗数据包格式封装和解析
程序员文章站
2022-03-20 10:37:56
1.北斗协议的具体格式如下图 2.数据包类型 根据北斗协议类型定义如下枚举类型 3.基础类封装 BDBaseFrame,使用 IByteBuffer 类来封装数据包,IByteBuffer 内置提供了很多字节操作方法(read,write) 4.具体数据包类型封装 PositionFrame 5.d ......
1.北斗协议的具体格式如下图
2.数据包类型 根据北斗协议类型定义如下枚举类型
/// <summary> /// 数据包类型 /// </summary> public enum bdframetype : ushort { /// <summary> /// 默认 /// </summary> default = 0x00, /// <summary> /// 终端通用应答 /// </summary> tercommonresponse = 0x0001, /// <summary> /// 平台通用应答 /// </summary> platcommonresponse = 0x8001, /// <summary> /// 终端心跳 /// </summary> terheartbeat = 0x0002, /// <summary> /// 位置信息汇报 /// </summary> position = 0x0200 //省略其他的数据包类型 }
3.基础类封装 bdbaseframe,使用 ibytebuffer 类来封装数据包,ibytebuffer 内置提供了很多字节操作方法(read,write)
bytebuffer.readunsignedshort() bytebuffer.writeunsignedshort() //等等
public abstract class bdbaseframe { /// <summary> /// 消息id /// </summary> public bdframetype frametype { get; set; } /// <summary> /// 是否分包 /// </summary> public bool issubpackage { get; set; } /// <summary> /// 加密方式 /// </summary> public bdframeencrypttype frameencrypttype { get; set; } /// <summary> /// 消息体长度 /// </summary> public uint16 framecontentlen { get; private set; } /// <summary> /// 终端手机号 唯一 /// </summary> public string terminalphone { get; set; } = string.empty; /// <summary> /// 消息流水号 /// </summary> public ushort frameserialnum { get; set; } /// <summary> /// 消息总包数 /// </summary> public ushort framepackagecount { get; set; } /// <summary> /// 包序号 从 1开始 /// </summary> public ushort framepackageindex { get; set; } private int m_framebodyoffset = 13; /// <summary> /// 消息体 数据偏于量 /// </summary> protected int framebodyoffset { get { return m_framebodyoffset; } } private static ushort m_sendframeserialnum = 0; /// <summary> /// 获取发送的流水号 /// </summary> public static ushort sendframeserialnum { get { if (m_sendframeserialnum == ushort.maxvalue) m_sendframeserialnum = 0; m_sendframeserialnum++; return m_sendframeserialnum; } } /// <summary> /// 数据包内容 字节 /// </summary> //public ibytebuffer contentbuffer { get; set; } #region 解析数据包 /// <summary> /// 解析头部 /// </summary> private void decoderhead(ibytebuffer bytebuffer) { //消息体属性 bytebuffer.setreaderindex(1); frametype = (bdframetype)bytebuffer.readunsignedshort(); ushort frameproerty = bytebuffer.readunsignedshort(); issubpackage = framehelper.readboolean16(frameproerty, 13); framecontentlen = (uint16)(frameproerty & 0x1fff);//消息体长度 if (issubpackage) m_framebodyoffset = 17; //终端手机号 stringbuilder stringbuilder = new stringbuilder(); for (int i = 0; i < 6; i++) { stringbuilder.append(bytebuffer.readbyte().tostring("x2")); } terminalphone = stringbuilder.tostring().trimstart(new char[] { '0' }); //消息流水号 frameserialnum = bytebuffer.readunsignedshort(); //消息包封装项 if (issubpackage) { framepackagecount = bytebuffer.readunsignedshort(); framepackageindex = bytebuffer.readunsignedshort(); } } /// <summary> /// 解析内容 /// </summary> public virtual void decoderframe(ibytebuffer bytebuffer) { //解析头部 decoderhead(bytebuffer); } #endregion #region 封装数据包 public virtual ibytebuffer encodercontent() { return null; } #endregion public override string tostring() { return $"{terminalphone} {frametypehelper.getframetype(frametype)} {datetime.now.tostring("yyyy-mm-dd hh:mm:ss")}"; } }
4.具体数据包类型封装 positionframe
/// <summary> /// 位置信息汇报 /// </summary> public class positionframe : bdbaseframe { public positionframe() { frametype = bdframetype.position; } /// <summary> /// 报警标志 /// </summary> public uint32 alarmflag { get; set; } /// <summary> /// 状态 /// </summary> public uint32 statusflag { get; set; } /// <summary> /// 纬度 dword 以度为单位的纬度值乘以 10 的 6 次方,精确到百万分之一度 /// </summary> public double lat { get; set; } /// <summary> /// 经度 dword 以度为单位的经度值乘以 10 的 6 次方,精确到百万分之一度 /// </summary> public double lng { get; set; } /// <summary> /// 高程 word 海拔高度,单位为米(m) /// </summary> public uint16 height { get; set; } /// <summary> /// 速度 word 1/10km/h /// </summary> public float speed { get; set; } /// <summary> /// 方向 word 0-359,正北为 0,顺时针 /// </summary> public uint16 direction { get; set; } /// <summary> /// 时间 bcd[6] yy-mm-dd-hh-mm-ss(gmt+8 时间,本标准中之后涉及的时间均采用此时区) /// </summary> public datetime gpsdatetime { get; set; } public override void decoderframe(ibytebuffer bytebuffer) { base.decoderframe(bytebuffer); alarmflag = bytebuffer.readunsignedint(); statusflag = bytebuffer.readunsignedint(); lat = bytebuffer.readunsignedint() / 1000000.0; lng = bytebuffer.readunsignedint() / 1000000.0; height = bytebuffer.readunsignedshort(); speed = bytebuffer.readunsignedshort() / 10.0f; direction = bytebuffer.readunsignedshort(); //时间 bcd[6] byte[] bcdtime = new byte[6]; bytebuffer.readbytes(bcdtime); string bcdtimestring = framehelper.bcd2string(bcdtime); datetime gpstime; if (datetime.tryparseexact(bcdtimestring, "yymmddhhmmss", new cultureinfo("zh-cn", true), datetimestyles.none, out gpstime)) gpsdatetime = gpstime; else gpsdatetime = new datetime(2001, 1, 1, 0, 0, 0); } public override ibytebuffer encodercontent() { ibytebuffer contentbuffer = unpooled.buffer(100, 1024); contentbuffer.writeint((int)alarmflag); contentbuffer.writeint((int)statusflag); contentbuffer.writeint((int)(lat * 1000000)); contentbuffer.writeint((int)(lng * 1000000)); contentbuffer.writeunsignedshort(height); contentbuffer.writeunsignedshort((uint16)(speed * 10)); contentbuffer.writeunsignedshort(direction); //时间 bcd[6] byte[] timebcdbuffer = framehelper.writebcdstring(gpsdatetime.tostring("yymmddhhmmss")); contentbuffer.writebytes(timebcdbuffer); return contentbuffer; } public override string tostring() { return string.format("通讯号:{0},时间:{1},经纬度{2}|{3},高度:{4},方向:{5}", terminalphone, gpsdatetime.tostring("yyyy-mm-dd hh:mm:ss"), lng, lat, height, direction); } }
5.dotnetty encoderhandler 封装,上面封装的只是消息体的数据,没有包括标识位,消息头,验证码,标识位,在发送数据通道中,需要把数据加上标识位,消息头,验证码,标识位。包括数据包转义
/// <summary> /// 北斗数据包 封装 /// </summary> public class beidoucontentencoderhandler : messagetobyteencoder<bdbaseframe> { protected override void encode(ichannelhandlercontext context, bdbaseframe message, ibytebuffer output) { encodeframe(message, output); } private void encodeframe(bdbaseframe message, ibytebuffer output) { //ibytebuffer framebuffer = output; output.markreaderindex(); //内容 ibytebuffer contentbuffer = message.encodercontent(); if (contentbuffer == null) contentbuffer = unpooled.empty; //byte[] content = new byte[contentbuffer.readablebytes]; //contentbuffer.readbytes(content, 0, content.length); //写头标志 output.writebyte(bdframeconst.frame_flag); //消息 id output.writeunsignedshort((ushort)message.frametype); //消息体属性 加密没做 // ushort contentlen = (ushort)content.length; ushort contentlen = (ushort)contentbuffer.readablebytes; if (message.issubpackage) { contentlen = (ushort)(contentlen | 0x2000); output.writeunsignedshort(contentlen); } else { output.writeunsignedshort(contentlen); } //终端手机号 string tphone = message.terminalphone.tostringframepropertylength(12, '0'); byte[] tphonebuffer = czeframehelper.writebcdstring(tphone); output.writebytes(tphonebuffer); //消息流水号 output.writeunsignedshort(message.frameserialnum); //消息包封装项 if (message.issubpackage) { output.writeunsignedshort(message.framepackagecount); output.writeunsignedshort(message.framepackageindex); } //消息体 output.writebytes(contentbuffer); contentbuffer.release(); //计算校验码 byte[] checkcodebuffer = new byte[output.readablebytes]; output.readbytes(checkcodebuffer, 0, checkcodebuffer.length); byte value = checkcodebuffer[1]; for (int i = 2; i < checkcodebuffer.length; i++) value ^= checkcodebuffer[i]; output.writebyte(value); //写尾标志 output.writebyte(bdframeconst.frame_flag); //转义 output.resetreaderindex(); checkcodebuffer = new byte[output.readablebytes]; output.readbytes(checkcodebuffer, 0, checkcodebuffer.length); byte[] frame = frameescaping.bdescapingbuffersend(checkcodebuffer); //数据写入 framebuffer output.clear(); output.writebytes(frame); } }
6.使用 beidoucontentencoderhandler,在通道中加入beidoucontentencoderhandler,通道里面的顺序很重要,beidoucontentencoderhandler必须要在你发送的handler前加到通道中去如下图
主要的代码就这些,水平有限,请大家多多指教
推荐阅读
-
小程序封装路由文件和路由方法(5种全解析)
-
小程序封装路由文件和路由方法(5种全解析)
-
javascript解析ajax返回的xml和json格式数据实例详解
-
解析json字符串的方式(json格式和字典区别)
-
javascript解析ajax返回的xml和json格式数据实例详解
-
解析json字符串的方式(json格式和字典区别)
-
Android App数据格式Json解析方法和常见问题
-
C#_Excel数据读取与写入_自定义解析封装类_支持设置标题行位置&使用excel表达式收集数据&单元格映射&标题映射&模板文件的参数数据替换(第二版-增加深度读取和更新功能)
-
Python 读取用户指令和格式化打印实现解析
-
Android ABC Jetpack学习之一文学会Navigation(附源码解析和使用封装)