修正命名错误
xiyunfei authored at 2023-04-09 21:10:58
13.81 KiB
NewLife.JT808
# NewLife.JT808 使用文档 > 版本:v1.0 | 日期:2026-07-15 本文档面向协议库使用者,涵盖协议库的核心 API 使用、粤标扩展集成和 RocketMQ 消息队列配置。 --- ## 目录 1. [快速开始](/NewLife/NewLife.JT808/Blob/master/Doc/#1-快速开始) 2. [核心 API 使用](/NewLife/NewLife.JT808/Blob/master/Doc/#2-核心-api-使用) 3. [粤标扩展](/NewLife/NewLife.JT808/Blob/master/Doc/#3-粤标扩展) 4. [RocketMQ 消息队列](/NewLife/NewLife.JT808/Blob/master/Doc/#4-rocketmq-消息队列) 5. [客户端使用](/NewLife/NewLife.JT808/Blob/master/Doc/#5-客户端使用) 6. [常见问题](/NewLife/NewLife.JT808/Blob/master/Doc/#6-常见问题) --- ## 1. 快速开始 ### 1.1 安装 NuGet 包 ```bash dotnet add package NewLife.JT808 ``` ### 1.2 初始化消息工厂 ```csharp using NewLife.JT808.Protocols; // 初始化消息工厂,扫描程序集注册所有消息类型 var factory = MessageFactory.Instance; factory.Init(); ``` ### 1.3 解析终端上报消息 ```csharp using NewLife.JT808.Protocols; using NewLife.JT808.Models; // 解析原始二进制帧 var raw = new Byte[] { 0x7E, 0x02, 0x00, /* ... */, 0x7E }; var msg = new JTMessage(); msg.Read(new MemoryStream(raw), null); // 反序列化为业务模型 var body = factory.Parse(msg); if (body is T0200 position) { Console.WriteLine($"纬度: {position.Latitude / 1_000_000.0}"); Console.WriteLine($"经度: {position.Longitude / 1_000_000.0}"); Console.WriteLine($"速度: {position.Speed} km/h"); // 读取位置附加信息 foreach (var kv in position.Additionals) Console.WriteLine($"{kv.Key}: {kv.Value}"); } ``` ### 1.4 构建下发消息 ```csharp using NewLife.JT808.Protocols; using NewLife.JT808.Models; // 构建平台通用应答 var t8001 = new T8001 { Sequence = msg.Sequence, Kind = msg.Kind, Result = ResultKinds.成功 }; // 序列化消息体 var ms = new MemoryStream(); var writer = Helper.CreateWriter(ms, msg); writer.TryWrite(null, ref t8001); // 封装为 JTMessage 帧 var reply = new JTMessage { Kind = MessageKinds.平台通用应答, Mobile = msg.Mobile, Sequence = (UInt16)(msg.Sequence + 1), Payload = new Packet(ms.ToArray()), Reply = false }; // 输出完整帧 var outMs = new MemoryStream(); reply.Write(outMs, null); var frameBytes = outMs.ToArray(); ``` --- ## 2. 核心 API 使用 ### 2.1 消息帧(JTMessage) `JTMessage` 是协议的核心消息帧对象,负责: - **帧解析**:从原始字节流中提取 0x7E 帧边界、0x7D 转义 - **校验**:异或校验验证 - **分包**:识别分包标志,缓存合并分包 - **版本识别**:自动识别 2011/2013/2019 版本 ```csharp // 手动创建消息帧 var msg = new JTMessage { Kind = MessageKinds.位置信息汇报, // 消息ID Mobile = "01380000001", // 终端手机号(BCD编码) Sequence = 1, // 流水号 Version = 0, // 0=2013版, 1=2019版 Payload = new Packet(bodyBytes), // 消息体载荷 }; ``` ### 2.2 消息工厂(MessageFactory) 消息工厂维护消息类型到实体类的映射,支持自动扫描注册和手动注册。 ```csharp // 自动扫描注册(Init 自动扫描整个程序集的 [MessageKind] 特性) var factory = MessageFactory.Instance; factory.Init(); // 手动注册单个类型 factory.Register<MyCustomMessage>(); factory.Register(MessageKinds.自定义类型, typeof(MyCustomMessage)); // 注册外部程序集(用于扩展包) factory.Register(typeof(MyExtension).Assembly); // 覆盖已有映射(用于厂商自定义协议) factory.SetMap<TMyOverride>(); factory.SetMap(MessageKinds.位置信息汇报, typeof(T0200_V2)); ``` ### 2.3 消息体模型 #### 简单消息体(反射自动序列化) 对于属性类型为基础类型、无特殊编解码逻辑的消息体,使用反射自动序列化: ```csharp [MessageKind(MessageKinds.平台通用应答)] public class T8001 { public UInt16 Sequence { get; set; } public MessageKinds Kind { get; set; } public ResultKinds Result { get; set; } } ``` #### 复杂消息体(IAccessor 手动序列化) 对于需要精确控制编解码逻辑的复杂消息体,实现 `IAccessor` 接口: ```csharp [MessageKind(MessageKinds.位置信息汇报)] public class T0200 : IAccessor { public PositionAlarms Alarm { get; set; } public PositionStatus Status { get; set; } public Int32 Latitude { get; set; } public Int32 Longitude { get; set; } public Boolean Read(Stream stream, Object? context) { var reader = Helper.CreateReader(stream); Alarm = (PositionAlarms)reader.ReadUInt32(); Status = (PositionStatus)reader.ReadUInt32(); Latitude = reader.ReadInt32(); Longitude = reader.ReadInt32(); // ... 后续字段 return true; } public Boolean Write(Stream stream, Object? context) { var writer = Helper.CreateWriter(stream); writer.Write((UInt32)Alarm); writer.Write((UInt32)Status); writer.Write(Latitude); writer.Write(Longitude); // ... 后续字段 return true; } } ``` ### 2.4 序列化特性 | 特性 | 用途 | |------|------| | `[BCDString(Length)]` | BCD 编码字符串字段 | | `[BCDTime(Length, Format)]` | BCD 编码时间字段,如 `[BCDTime(6, "yyMMddHHmmss")]` | | `[FieldSize(N)]` | 固定长度字段 | | `[FullBytes]` / `[FullString2]` | 数据流剩余部分整体读取 | | `[JT2011]` / `[JT2019]` | 2011/2019 版本条件字段,非对应版本自动跳过 | | `[MessageKind(Kind)]` | 消息类型标记,用于工厂注册 | ### 2.5 消息队列接口 核心库定义了消息队列抽象接口: ```csharp // 消息生产者:将设备上行消息生产到队列 public interface IMsgProducer<TMessage> { Task<Boolean> ProduceAsync(String mobile, TMessage message, CancellationToken ct = default); String Topic { get; } } // 消息消费者:消费设备上行消息 public interface IMsgConsumer<TMessage> { Task SubscribeAsync(Func<String, TMessage, Task> onMessage, CancellationToken ct = default); Task UnsubscribeAsync(CancellationToken ct = default); String Topic { get; } } // 会话通知:终端上下线事件 public interface ISessionProducer { /* ProduceOnline / ProduceOffline */ } public interface ISessionConsumer { /* Subscribe 上下线回调 */ } // 下行指令处理器 public interface IDownMessageHandler { Task<Byte[]> HandleAsync(String mobile, Byte[] data); } ``` 内置实现: | 实现 | 位置 | 说明 | |------|------|------| | `DefaultMsgQueue<T>` | `NewLife.JT808.Protocols` | 内存队列,无需外部依赖 | | `RocketMQProducer<T>` / `RocketMQConsumer<T>` | `NewLife.JT808.Protocols.RocketMQ` | RocketMQ 适配器 | | `QueueService` | `JT808.Server.Services` | Redis Stream 实现 | --- ## 3. 粤标扩展 ### 3.1 概述 粤标(广东省主动安全标准)是 JT/T 808 在广东地区的区域性扩展,与苏标共用 ADAS/DSM/TPMS/BSD 报警数据结构,但存在以下差异: | 差异点 | 苏标 | 粤标 | |--------|------|------| | 终端 ID 长度 | 7 字节 | 30 字节 | | 报警标识号长度 | 16 字节 | 40 字节 | | 核心报警消息 | 位置附加信息 0x64~0x67 | 独立消息 0x1FC4 | | USB 外设透传 | 不支持 | 0x0900/0x8900 子类型 0xF7/0xF8 | | 参数 ID | 标准范围 | 0xF364~0xF370 | ### 3.2 粤标消息体列表 | 消息 | 消息ID | 方向 | 说明 | |------|--------|------|------| | `T1FC4` | 0x1FC4 | 上行 | 主动安全报警上报(核心) | | `T0900_F7` | 0x0900+0xF7 | 上行 | USB 外设状态信息上报 | | `T0900_F8` | 0x0900+0xF8 | 上行 | USB 外设信息上报 | | `T8900_F7` | 0x8900+0xF7 | 下行 | USB 外设状态查询 | | `T8900_F8` | 0x8900+0xF8 | 下行 | USB 外设信息查询 | | `T0200_F1` | 0x0200+0xF1 | 上行 | 位置附加信息-安装异常 | | `T0200_F2` | 0x0200+0xF2 | 上行 | 位置附加信息-算法异常 | | `T8103_F364` | 0x8103+0xF364 | 下行 | 前向碰撞报警参数 | | `T8103_F370` | 0x8103+0xF370 | 下行 | 盲区监测参数 | ### 3.3 使用粤标扩展 粤标消息体与核心消息体在同一个程序集中,初始化消息工厂后自动注册: ```csharp MessageFactory.Instance.Init(); // 粤标消息体随程序集扫描自动注册,无需额外配置 ``` 解析粤标主动安全报警: ```csharp using NewLife.JT808.Protocols; using NewLife.JT808.YueBiao; // 解析 0x1FC4 消息 if (body is T1FC4 alarm) { Console.WriteLine($"终端ID: {alarm.TerminalId}"); Console.WriteLine($"报警类型: 0x{alarm.AlarmType:X2}"); Console.WriteLine($"报警子类型: 0x{alarm.AlarmKind:X2}"); Console.WriteLine($"级别: {alarm.Level}"); Console.WriteLine($"位置: {alarm.Latitude / 1_000_000.0}, {alarm.Longitude / 1_000_000.0}"); } ``` 解析 USB 外设状态(0x0900 透传消息): ```csharp // 假设已解析出 T0900 透传消息 if (body is T0900 passthrough && passthrough.Type == 0xF7) { var usbStatus = T0900_F7.Parse(passthrough.Data); Console.WriteLine($"USB设备总数: {usbStatus.TotalCount}"); foreach (var item in usbStatus.Items ?? []) { Console.WriteLine($" ID: {item.Id}, 状态: {item.Status}, 在线: {item.Online}"); } } ``` ### 3.4 粤标报警标识号 `AlarmDevice` 类已内置苏标/粤标自适应逻辑: ```csharp // AlarmDevice 自动根据数据流剩余长度判断 // 流剩余 ≥ 40 字节 → 粤标(30 字节终端 ID + 16 字节扩展) // 流剩余 < 40 字节 → 苏标(7 字节终端 ID) ``` --- ## 4. RocketMQ 消息队列 ### 4.1 安装 ```bash # 核心库已引用 NewLife.RocketMQ,无需额外安装 ``` ### 4.2 RocketMQ 生产者适配器 ```csharp using NewLife.JT808.Protocols.RocketMQ; // 创建生产者 var producer = new RocketMQProducer<PositionData>( nameServer: "127.0.0.1:9876", topic: "PositionData", group: "PID_PositionData" ); // 生产消息 await producer.ProduceAsync("01380000001", new PositionData { Latitude = 22300000, Longitude = 114000000, Speed = 60, }); // 释放 producer.Dispose(); ``` ### 4.3 RocketMQ 消费者适配器 ```csharp using NewLife.JT808.Protocols.RocketMQ; // 创建消费者 var consumer = new RocketMQConsumer<PositionData>( nameServer: "127.0.0.1:9876", topic: "PositionData", group: "CG_PositionData" ); // 订阅消息 await consumer.SubscribeAsync(async (mobile, data) => { Console.WriteLine($"收到终端 {mobile} 的位置数据:"); Console.WriteLine($" 纬度: {data.Latitude / 1_000_000.0}"); Console.WriteLine($" 经度: {data.Longitude / 1_000_000.0}"); Console.WriteLine($" 速度: {data.Speed} km/h"); }); // 取消订阅 await consumer.UnsubscribeAsync(); consumer.Dispose(); ``` ### 4.4 会话通知适配器 ```csharp // 生产者端(Server) var sessionProducer = new RocketMQSessionAdapter( nameServer: "127.0.0.1:9876", topic: "SessionEvent" ); await sessionProducer.ProduceOnlineAsync("01380000001"); await sessionProducer.ProduceOfflineAsync("01380000001"); ``` ### 4.5 完整集成示例(替代 Redis Stream) ```csharp // 在 Server 启动时注册 RocketMQ 适配器 services.AddSingleton<IMsgProducer<PositionData>>(_ => new RocketMQProducer<PositionData>("127.0.0.1:9876", "PositionData")); services.AddSingleton<IMsgProducer<ADASAlarm>>(_ => new RocketMQProducer<ADASAlarm>("127.0.0.1:9876", "ADASAlarm")); ``` ### 4.6 RocketMQ 配置要点 | 参数 | 说明 | 默认值 | |------|------|--------| | `NameServerAddress` | RocketMQ NameServer 地址 | 必填 | | `Topic` | 主题名称 | 必填 | | `Group` | 生产者组/消费组 | 自动生成 | | `AccessKey` / `SecretKey` | 阿里云/华为云等云实例认证 | 可选 | | `InstanceId` | 阿里云实例 ID | 可选 | 云厂商适配说明: ```csharp // 阿里云 Producer.AccessKey = "xxx"; Producer.SecretKey = "xxx"; Producer.Aliyun = new AliyunProvider { InstanceId = "MQ_INST_xxx" }; // 华为云 Producer.AccessKey = "xxx"; Producer.SecretKey = "xxx"; // 自动识别 InstanceId // 腾讯云 Producer.AccessKey = "xxx"; Producer.SecretKey = "xxx"; // 自动识别 Namespace ``` --- ## 5. 客户端使用 ### 5.1 TCP 客户端 ```csharp using NewLife.JT808.Protocols; // 创建客户端 var client = new JT808Client { Server = "127.0.0.1", Port = 808, Mobile = "01380000001", AutoReconnect = true, // 自动重连 HeartbeatInterval = 30, // 心跳间隔(秒) }; // 设置消息处理器 client.AddHandler<T0200>(msg => { Console.WriteLine($"收到位置: {msg.Latitude}, {msg.Longitude}"); }); // 连接服务器 await client.ConnectAsync(); ``` ### 5.2 发送消息 ```csharp // 发送消息并等待应答 var result = await client.SendJT808(new T0200 { Latitude = 22300000, Longitude = 114000000, }); ``` --- ## 6. 常见问题 ### 6.1 如何添加自定义消息体? 1. 创建消息体类,添加 `[MessageKind]` 特性 2. 实现 `IAccessor`(复杂消息体)或依赖反射序列化(简单消息体) 3. 调用 `MessageFactory.Instance.Register<T>()` 注册 4. 或在外部程序集中定义,使用 `factory.Register(Assembly)` 批量注册 ### 6.2 如何切换消息队列实现? 消息队列通过依赖注入解耦,在 `Program.cs` 中替换实现即可: ```csharp // 开发测试:内存队列 services.AddSingleton<IMsgProducer<PositionData>, DefaultMsgQueue<PositionData>>(); // 生产环境:RocketMQ services.AddSingleton<IMsgProducer<PositionData>>(_ => new RocketMQProducer<PositionData>("127.0.0.1:9876", "PositionData")); // 生产环境:Redis Stream(在 Server 项目中使用 QueueService) ``` ### 6.3 如何支持 2011 版本? ```csharp JT808Config.Version = JT808Version.JT2011; ``` 设置 `JT808Config.Version` 后,消息序列化时自动跳过 `[JT2011]` 特性标记的字段。 ### 6.4 性能建议 - 使用 `ArrayPool<Byte>` 或 `Pool.StringBuilder` 减少内存分配 - 热点路径避免反射,优先实现 `IAccessor` - 使用 `FileCodec` 处理大量报警附件文件传输 - 生产环境建议使用 RocketMQ 或 Redis Stream 替代默认内存队列