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