欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

北斗数据包格式封装和解析

程序员文章站 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前加到通道中去如下图

北斗数据包格式封装和解析

 

主要的代码就这些,水平有限,请大家多多指教

原文地址 http://www.dncblogs.cn/blog/lookblog/71