feat: 新增粤标扩展与 RocketMQ 消息队列适配器 - 新增粤标主动安全报警消息 T1FC4(0x1FC4)、USB 外设透传(T0900_F7/F8、T8900_F7/F8)、安装/算法异常位置附加信息(T0200_F1/F2)及粤标参数设置消息 - 新增 RocketMQ 生产者/消费者/会话通知/下行指令适配器,集成 NewLife.RocketMQ - 新增使用文档,涵盖核心 API、粤标集成与 RocketMQ 配置 - 更新功能清单、需求文档、README,补充粤标及 RocketMQ 模块描述 - csproj 添加 NewLife.RocketMQ 依赖 - MessageKinds 添加粤标主动安全报警枚举大石头 authored at 2026-07-15 17:23:06
diff --git "a/Doc/\344\275\277\347\224\250\346\226\207\346\241\243.md" "b/Doc/\344\275\277\347\224\250\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..df02351
--- /dev/null
+++ "b/Doc/\344\275\277\347\224\250\346\226\207\346\241\243.md"
@@ -0,0 +1,519 @@
+# NewLife.JT808 使用文档
+
+> 版本:v1.0 | 日期:2026-07-15
+
+本文档面向协议库使用者,涵盖协议库的核心 API 使用、粤标扩展集成和 RocketMQ 消息队列配置。
+
+---
+
+## 目录
+
+1. [快速开始](#1-快速开始)
+2. [核心 API 使用](#2-核心-api-使用)
+3. [粤标扩展](#3-粤标扩展)
+4. [RocketMQ 消息队列](#4-rocketmq-消息队列)
+5. [客户端使用](#5-客户端使用)
+6. [常见问题](#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 替代默认内存队列
diff --git "a/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md" "b/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
index 4268adf..bba2097 100644
--- "a/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
+++ "b/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
@@ -1,12 +1,12 @@
# NewLife.JT808 功能清单
-> 版本:v2.4 | 日期:2026-07-15 | **状态:全部完成 🎉**
+> 版本:v2.5 | 日期:2026-07-15 | **状态:全部完成 🎉**
> 状态标记:✅ 已实现 | 🟡 部分实现 | 🔧 规划中 | ⏸ 暂缓/占位 | ❌ 未开始
本清单维护"核心目标 → 功能模块/子模块 → 完成状态"。模块名优先对齐[需求文档](需求文档.md)第 3 章的功能点名称;需要更细设计时跳转到[架构设计](架构设计.md)。
-> 🔍 **审计说明**:测试覆盖 M1/M2/M3/M4/M5/M6 共 59 个测试用例。XML 注释存在 CS1591 缺失(消息体模型类)。
+> 🔍 **审计说明**:测试覆盖 M1/M2/M3/M4/M5/M6 共 59 个测试用例。M18 粤标/M19 RocketMQ 为新增模块,待补充专用测试。XML 注释存在 CS1591 缺失(消息体模型类)。
## M1 核心协议层(Newlife.JT808 — 已完成 ✅)
@@ -120,6 +120,7 @@
| M9-3 | 下行消息处理接口 | ✅ | `IDownMessageHandler` 外部指令处理 |
| M9-4 | 默认内存实现 | ✅ | `DefaultMsgQueue<T>` 内存队列实现 |
| M9-5 | Redis Stream 实现 | ✅ | QueueService + GpsHostedService 实现 Redis Stream 生产/消费 |
+| M9-6 | RocketMQ 实现 | ✅ | RocketMQProducer/Consumer/Session/DownHandler 适配器,基于 NewLife.RocketMQ |
## M10 客户端增强(Newlife.JT808 — 已完成 ✅)
@@ -237,6 +238,30 @@
| M17-6 | BSD 报警消费 Worker | ✅ | BSDAlarmWorker Redis Stream 消费 + BSDAlarm 持久化 ✅ |
| M17-7 | 胎压数据消费 Worker | ✅ | TirePressureWorker Redis Stream 消费 + TirePressure 持久化 ✅ |
+## M18 粤标扩展(Newlife.JT808 — 已完成 ✅)
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M18-1 | 主动安全报警上报(T1FC4) | ✅ | 0x1FC4 粤标核心报警消息,含 ADAS/DSM/TPMS/BSD 报警 |
+| M18-2 | 位置附加信息-安装异常(T0200_F1) | ✅ | 0x0200 附加类型 0xF1,外设安装异常状态 |
+| M18-3 | 位置附加信息-算法异常(T0200_F2) | ✅ | 0x0200 附加类型 0xF2,算法异常状态 |
+| M18-4 | USB 外设状态上报(T0900_F7) | ✅ | 0x0900 子类型 0xF7,USB 外设工作状态 |
+| M18-5 | USB 外设信息上报(T0900_F8) | ✅ | 0x0900 子类型 0xF8,USB 外设厂商/型号/版本 |
+| M18-6 | USB 外设状态查询(T8900_F7) | ✅ | 0x8900 子类型 0xF7,平台查询 USB 状态 |
+| M18-7 | USB 外设信息查询(T8900_F8) | ✅ | 0x8900 子类型 0xF8,平台查询 USB 信息 |
+| M18-8 | 粤标参数设置(T8103_F364~F370) | ✅ | ADAS/车道偏离/疲劳驾驶/BSD 等粤标专用参数 |
+| M18-9 | 终端ID自适应 | ✅ | AlarmDevice 自动识别苏标 7B / 粤标 30B |
+| M18-10 | 报警标识号长度适配 | ✅ | 苏标 16B / 粤标 40B 条件读写 |
+
+## M19 RocketMQ 消息队列(Newlife.JT808 — 已完成 ✅)
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M19-1 | RocketMQ 生产者适配器 | ✅ | `RocketMQProducer<T>` 实现 `IMsgProducer<T>` |
+| M19-2 | RocketMQ 消费者适配器 | ✅ | `RocketMQConsumer<T>` 实现 `IMsgConsumer<T>` |
+| M19-3 | 会话通知适配器 | ✅ | `RocketMQSessionAdapter` 实现 `ISessionProducer` |
+| M19-4 | 下行指令适配器 | ✅ | `RocketMQDownHandler` 实现 `IDownMessageHandler` |
+
## 统计
| 模块 | 功能数 | 已完成 | 完成率 | 阶段 |
@@ -259,4 +284,6 @@
| M15 JT808 网关服务 | 13 | 13 | 100% | ✅ 阶段3 |
| M16 Web 管理后台 | 19 | 19 | 100% | ✅ 阶段4 |
| M17 Worker 消费服务 | 7 | 7 | 100% | ✅ 阶段5 |
-| **总计** | **144** | **144** | **100%** | 🎉 全部完成 |
+| M18 粤标扩展 | 10 | 10 | 100% | ✅ |
+| M19 RocketMQ 消息队列 | 4 | 4 | 100% | ✅ |
+| **总计** | **158** | **158** | **100%** | 🎉 全部完成 |
diff --git "a/Doc/\351\234\200\346\261\202\346\226\207\346\241\243.md" "b/Doc/\351\234\200\346\261\202\346\226\207\346\241\243.md"
index ff9c551..13129fc 100644
--- "a/Doc/\351\234\200\346\261\202\346\226\207\346\241\243.md"
+++ "b/Doc/\351\234\200\346\261\202\346\226\207\346\241\243.md"
@@ -1,6 +1,6 @@
# NewLife.JT808 需求文档
-> 版本:v1.2 | 日期:2026-07-15
+> 版本:v1.3 | 日期:2026-07-15
本文档描述 NewLife.JT808 的愿景、核心目标和功能方向。完成状态在[功能清单](功能清单.md)中追踪,详细设计在[架构设计](架构设计.md)中展开。
@@ -41,6 +41,8 @@ NewLife.JT808 是一套完整的 JT/T 808 道路运输车辆卫星定位系统
| M15 | JT808 网关服务 | 服务层 | TCP 协议网关、指令处理器链、业务服务、Redis 消息生产 |
| M16 | Web 管理后台 | 表现层 | NewLife.Cube 框架,6 个功能 Area,CRUD + 指令下发 + 统计 |
| M17 | Worker 消费服务 | 服务层 | Redis Stream 消费者,异步处理位置/传感器/报警数据 |
+| M18 | 粤标扩展 | 扩展协议 | 粤标主动安全(0x1FC4 报警上报、USB 外设透传、位置附加信息、专用参数设置),广东省主动安全标准 |
+| M19 | RocketMQ 消息队列 | 基础设施 | 基于 NewLife.RocketMQ 的生产者/消费者适配器,替代 Redis Stream 的跨进程消息解耦方案 |
## 3. 功能需求
@@ -138,6 +140,7 @@ NewLife.JT808 是一套完整的 JT/T 808 道路运输车辆卫星定位系统
- **M9-3 下行消息处理**:`IDownMessageHandler` 接口,处理外部系统下发的指令。
- **M9-4 默认内存实现**:基于内存队列的默认实现,无需外部依赖。
- **M9-5 Redis Stream 实现**:基于 Redis Stream 的生产者/消费者实现,支持消费组和 ACK 机制,用于跨进程消息解耦。
+- **M9-6 RocketMQ 实现**:基于 NewLife.RocketMQ 的生产者/消费者适配器,支持通过 RocketMQ 主题进行跨进程消息解耦,可作为 Redis Stream 的替代方案。
### 3.10 M10 客户端增强
@@ -237,6 +240,27 @@ NewLife.JT808 是一套完整的 JT/T 808 道路运输车辆卫星定位系统
- **M17-6 BSD 报警消费**:消费 `BSDAlarm` 主题,BSD 报警持久化和聚合。
- **M17-7 胎压数据消费**:消费 `TirePressure` 主题,胎压数据处理。
+### 3.18 M18 粤标扩展
+
+- **M18-1 粤标主动安全报警上报**:T1FC4 消息(0x1FC4)粤标核心报警消息,终端→平台,含 ADAS/DSM/TPMS/BSD 报警数据。
+- **M18-2 位置附加信息-安装异常**:T0200_F1 消息(0x0200 附加类型 0xF1),标识终端外设安装异常状态。
+- **M18-3 位置附加信息-算法异常**:T0200_F2 消息(0x0200 附加类型 0xF2),标识主动安全算法异常状态。
+- **M18-4 USB 外设状态上报**:T0900_F7 消息(0x0900 子类型 0xF7),终端上报 USB 外设工作状态。
+- **M18-5 USB 外设信息上报**:T0900_F8 消息(0x0900 子类型 0xF8),终端上报 USB 外设详细信息(厂商/型号/版本)。
+- **M18-6 USB 外设状态查询**:T8900_F7 消息(0x8900 子类型 0xF7),平台查询终端 USB 外设状态。
+- **M18-7 USB 外设信息查询**:T8900_F8 消息(0x8900 子类型 0xF8),平台查询终端 USB 外设详细信息。
+- **M18-8 粤标参数设置**:T8103_F364~F370 参数定义,覆盖 ADAS/车道偏离/疲劳驾驶/BSD 等粤标专用参数。
+- **M18-9 粤标终端ID自适应**:`AlarmDevice` 自动识别苏标(7字节)和粤标(30字节)终端ID长度,无感适配。
+- **M18-10 粤标报警标识号适配**:报警标识号长度苏标 16 字节 vs 粤标 40 字节,`AlarmDevice` 条件读写处理。
+
+### 3.19 M19 RocketMQ 消息队列
+
+- **M19-1 RocketMQ 生产者适配器**:`RocketMQProducer<T>` 实现 `IMsgProducer<T>`,将 mobile 编码到消息 keys 中,支持同步/异步发送。
+- **M19-2 RocketMQ 消费者适配器**:`RocketMQConsumer<T>` 实现 `IMsgConsumer<T>`,从消息 keys 提取 mobile,JSON 反序列化消息体。
+- **M19-3 会话通知适配器**:`RocketMQSessionAdapter` 实现 `ISessionProducer`,终端上下线事件通过 RocketMQ 主题广播。
+- **M19-4 下行指令适配器**:`RocketMQDownHandler` 实现 `IDownMessageHandler`,消费 RocketMQ 指令队列中的下行指令。
+- **M19-5 云厂商适配**:支持阿里云(实例 ID)、华为云(SSL/TLS)、腾讯云(Namespace)、Apache ACL 四种云厂商认证模式。
+
## 6. 不做什么
- 不实现 JT/T 1078 音视频流编解码:仅保留消息体模型和枚举定义,不做 H.264/ADPCM/G.711 等编解码,流媒体服务列为后续迭代。
diff --git a/Newlife.JT808/Newlife.JT808.csproj b/Newlife.JT808/Newlife.JT808.csproj
index f0a6050..13438b0 100644
--- a/Newlife.JT808/Newlife.JT808.csproj
+++ b/Newlife.JT808/Newlife.JT808.csproj
@@ -42,6 +42,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
+ <PackageReference Include="NewLife.RocketMQ" Version="3.1.2026.601" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0' Or '$(TargetFramework)'=='netstandard2.1'">
diff --git a/Newlife.JT808/Protocols/MessageKinds.cs b/Newlife.JT808/Protocols/MessageKinds.cs
index 23ed662..f9808a6 100644
--- a/Newlife.JT808/Protocols/MessageKinds.cs
+++ b/Newlife.JT808/Protocols/MessageKinds.cs
@@ -451,5 +451,10 @@ namespace NewLife.JT808.Protocols
/// <summary>平台下发远程录像回放控制</summary>
录像回放控制 = 0x9202,
#endregion
+
+ #region 粤标
+ /// <summary>主动安全报警上报。粤标核心报警消息</summary>
+ 粤标主动安全报警 = 0x1FC4,
+ #endregion
}
}
\ No newline at end of file
diff --git a/Newlife.JT808/Protocols/RocketMQ/RocketMQConsumer.cs b/Newlife.JT808/Protocols/RocketMQ/RocketMQConsumer.cs
new file mode 100644
index 0000000..9df40ab
--- /dev/null
+++ b/Newlife.JT808/Protocols/RocketMQ/RocketMQConsumer.cs
@@ -0,0 +1,115 @@
+using NewLife.Log;
+using NewLife.RocketMQ;
+using NewLife.Serialization;
+#if NET45
+using TaskEx = System.Threading.Tasks.Task;
+#endif
+
+namespace NewLife.JT808.Protocols.RocketMQ;
+
+/// <summary>基于 RocketMQ 的消息消费者适配器</summary>
+/// <remarks>
+/// 将 RocketMQ 的 Consumer 包装为 JT808 的 IMsgConsumer 接口。
+/// 消费时反序列化为 TMessage,从消息 keys 中提取 mobile。
+/// </remarks>
+/// <typeparam name="TMessage">消息类型</typeparam>
+public class RocketMQConsumer<TMessage> : IMsgConsumer<TMessage>, IDisposable
+{
+ #region 属性
+ /// <summary>RocketMQ 消费者实例</summary>
+ public Consumer Consumer { get; }
+
+ /// <summary>主题名称</summary>
+ public String Topic { get; }
+
+ /// <summary>是否已启动</summary>
+ public Boolean Active => Consumer?.Active ?? false;
+ #endregion
+
+ #region 构造
+ /// <summary>实例化 RocketMQ 消费者适配器</summary>
+ /// <param name="nameServer">NameServer 地址</param>
+ /// <param name="topic">主题名称</param>
+ /// <param name="group">消费组</param>
+ public RocketMQConsumer(String nameServer, String topic, String group)
+ {
+ Topic = topic;
+ Consumer = new Consumer
+ {
+ NameServerAddress = nameServer,
+ Topic = topic,
+ Group = group,
+ };
+ }
+
+ /// <summary>实例化 RocketMQ 消费者适配器</summary>
+ /// <param name="consumer">已配置的 Consumer 实例(未启动)</param>
+ /// <param name="topic">主题名称</param>
+ public RocketMQConsumer(Consumer consumer, String topic)
+ {
+ Consumer = consumer;
+ Topic = topic;
+ }
+ #endregion
+
+ #region 方法
+ /// <summary>订阅消息</summary>
+ /// <param name="onMessage">消息处理回调:参数为 mobile 和消息体</param>
+ /// <param name="cancellationToken">取消令牌</param>
+ public Task SubscribeAsync(Func<String, TMessage, Task> onMessage, CancellationToken cancellationToken = default)
+ {
+ Consumer.OnConsumeAsync = async (queue, messages, ct) =>
+ {
+ foreach (var msg in messages)
+ {
+ try
+ {
+ // 从 keys 中提取 mobile
+ var mobile = msg.Keys ?? String.Empty;
+
+ // 反序列化消息体
+ var body = default(TMessage);
+ if (msg.BodyString != null)
+ {
+ if (typeof(TMessage) == typeof(String))
+ body = (TMessage)(Object)msg.BodyString;
+ else
+ body = JsonHelper.ToJsonEntity<TMessage>(msg.BodyString);
+ }
+
+ if (body != null)
+ await onMessage(mobile, body);
+ }
+ catch (Exception ex)
+ {
+ Log?.Error("RocketMQ 消费异常: {0}", ex.Message);
+ return false;
+ }
+ }
+ return true;
+ };
+
+ Consumer.Start();
+ return Task.FromResult(0);
+ }
+
+ /// <summary>取消订阅</summary>
+ public Task UnsubscribeAsync(CancellationToken cancellationToken = default)
+ {
+ if (Consumer?.Active == true)
+ Consumer.Stop();
+
+ return Task.FromResult(0);
+ }
+
+ /// <summary>释放资源</summary>
+ public void Dispose()
+ {
+ if (Consumer?.Active == true)
+ Consumer.Stop();
+ }
+
+ /// <summary>日志</summary>
+ public ILog Log { get; set; } = Logger.Null;
+ #endregion
+}
diff --git a/Newlife.JT808/Protocols/RocketMQ/RocketMQDownHandler.cs b/Newlife.JT808/Protocols/RocketMQ/RocketMQDownHandler.cs
new file mode 100644
index 0000000..7f7beb6
--- /dev/null
+++ b/Newlife.JT808/Protocols/RocketMQ/RocketMQDownHandler.cs
@@ -0,0 +1,94 @@
+using NewLife.Log;
+using NewLife.RocketMQ;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.Protocols.RocketMQ;
+
+/// <summary>基于 RocketMQ 的下行指令处理器</summary>
+/// <remarks>
+/// 消费 RocketMQ 指令队列中的下行指令,将字符串类型的 BodyData 转换为 Byte[]。
+/// 配合 CommandClient 使用,CommandClient 向 RocketMQ 发送指令,此处理器接收并转换。
+/// </remarks>
+public class RocketMQDownHandler : IDownMessageHandler, IDisposable
+{
+ #region 属性
+ /// <summary>RocketMQ 消费者实例</summary>
+ public Consumer Consumer { get; }
+
+ /// <summary>指令主题</summary>
+ public String Topic { get; }
+
+ /// <summary>是否已启动</summary>
+ public Boolean Active => Consumer?.Active ?? false;
+
+ private Func<String, Byte[], Task<Byte[]>>? _handler;
+ #endregion
+
+ #region 构造
+ /// <summary>实例化 RocketMQ 下行指令处理器</summary>
+ /// <param name="nameServer">NameServer 地址</param>
+ /// <param name="topic">指令主题</param>
+ /// <param name="group">消费组</param>
+ public RocketMQDownHandler(String nameServer, String topic, String group)
+ {
+ Topic = topic;
+ Consumer = new Consumer
+ {
+ NameServerAddress = nameServer,
+ Topic = topic,
+ Group = group,
+ };
+ }
+ #endregion
+
+ #region 方法
+ /// <summary>处理下行消息</summary>
+ /// <param name="mobile">目标终端手机号</param>
+ /// <param name="data">消息体序列化数据</param>
+ /// <returns>处理结果</returns>
+ public Task<Byte[]> HandleAsync(String mobile, Byte[] data)
+ {
+ return Task.FromResult(data);
+ }
+
+ /// <summary>开始消费指令队列</summary>
+ /// <param name="onMessage">消费回调:mobile, data → 结果</param>
+ public void Start(Func<String, Byte[], Task<Byte[]>> onMessage)
+ {
+ _handler = onMessage;
+
+ Consumer.OnConsumeAsync = async (queue, messages, ct) =>
+ {
+ foreach (var msg in messages)
+ {
+ try
+ {
+ var mobile = msg.Keys ?? String.Empty;
+ var data = msg.Body ?? new Byte[0];
+
+ if (_handler != null)
+ await _handler(mobile, data);
+ }
+ catch (Exception ex)
+ {
+ Log?.Error("RocketMQ 指令消费异常: {0}", ex.Message);
+ return false;
+ }
+ }
+ return true;
+ };
+
+ Consumer.Start();
+ }
+
+ /// <summary>释放资源</summary>
+ public void Dispose()
+ {
+ if (Consumer?.Active == true)
+ Consumer.Stop();
+ }
+
+ /// <summary>日志</summary>
+ public ILog Log { get; set; } = Logger.Null;
+ #endregion
+}
diff --git a/Newlife.JT808/Protocols/RocketMQ/RocketMQProducer.cs b/Newlife.JT808/Protocols/RocketMQ/RocketMQProducer.cs
new file mode 100644
index 0000000..534242e
--- /dev/null
+++ b/Newlife.JT808/Protocols/RocketMQ/RocketMQProducer.cs
@@ -0,0 +1,82 @@
+using NewLife.Log;
+using NewLife.RocketMQ;
+
+namespace NewLife.JT808.Protocols.RocketMQ;
+
+/// <summary>基于 RocketMQ 的消息生产者适配器</summary>
+/// <remarks>
+/// 将 RocketMQ 的 Producer 包装为 JT808 的 IMsgProducer 接口。
+/// 消息体被序列化为 JSON 字符串后发送,mobile 编码到消息的 keys 中。
+/// </remarks>
+/// <typeparam name="TMessage">消息类型</typeparam>
+public class RocketMQProducer<TMessage> : IMsgProducer<TMessage>, IDisposable
+{
+ #region 属性
+ /// <summary>RocketMQ 生产者实例</summary>
+ public Producer Producer { get; }
+
+ /// <summary>主题名称</summary>
+ public String Topic { get; }
+
+ /// <summary>是否已启动</summary>
+ public Boolean Active => Producer?.Active ?? false;
+ #endregion
+
+ #region 构造
+ /// <summary>实例化 RocketMQ 生产者适配器</summary>
+ /// <param name="nameServer">NameServer 地址</param>
+ /// <param name="topic">主题名称</param>
+ /// <param name="group">生产者组</param>
+ public RocketMQProducer(String nameServer, String topic, String? group = null)
+ {
+ Topic = topic;
+ Producer = new Producer
+ {
+ NameServerAddress = nameServer,
+ Topic = topic,
+ Group = group ?? $"PID_{topic}",
+ };
+ Producer.Start();
+ }
+
+ /// <summary>实例化 RocketMQ 生产者适配器</summary>
+ /// <param name="producer">已启动的 Producer 实例</param>
+ /// <param name="topic">主题名称</param>
+ public RocketMQProducer(Producer producer, String topic)
+ {
+ Producer = producer;
+ Topic = topic;
+ }
+ #endregion
+
+ #region 方法
+ /// <summary>生产消息到 RocketMQ</summary>
+ /// <param name="mobile">终端手机号,编码到消息 keys 中</param>
+ /// <param name="message">消息对象</param>
+ /// <param name="cancellationToken">取消令牌</param>
+ /// <returns>是否成功</returns>
+ public async Task<Boolean> ProduceAsync(String mobile, TMessage message, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ var result = await Producer.PublishAsync(message, null, mobile);
+ return result != null;
+ }
+ catch (Exception ex)
+ {
+ Log?.Error("RocketMQ 生产失败: {0}", ex.Message);
+ return false;
+ }
+ }
+
+ /// <summary>释放资源</summary>
+ public void Dispose()
+ {
+ if (Producer?.Active == true)
+ Producer.Stop();
+ }
+
+ /// <summary>日志</summary>
+ public ILog Log { get; set; } = Logger.Null;
+ #endregion
+}
diff --git a/Newlife.JT808/Protocols/RocketMQ/RocketMQSessionAdapter.cs b/Newlife.JT808/Protocols/RocketMQ/RocketMQSessionAdapter.cs
new file mode 100644
index 0000000..1b24fc5
--- /dev/null
+++ b/Newlife.JT808/Protocols/RocketMQ/RocketMQSessionAdapter.cs
@@ -0,0 +1,60 @@
+using NewLife.Log;
+using NewLife.RocketMQ;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.Protocols.RocketMQ;
+
+/// <summary>基于 RocketMQ 的会话通知适配器</summary>
+/// <remarks>
+/// 将终端的上下线事件通过 RocketMQ 主题广播。
+/// 上线通知发送 "online" 消息,下线通知发送 "offline" 消息。
+/// </remarks>
+public class RocketMQSessionAdapter : ISessionProducer, IDisposable
+{
+ #region 属性
+ /// <summary>RocketMQ 生产者实例</summary>
+ public Producer Producer { get; }
+
+ /// <summary>会话通知主题</summary>
+ public String Topic { get; set; } = "SessionEvent";
+ #endregion
+
+ #region 构造
+ /// <summary>实例化 RocketMQ 会话通知适配器</summary>
+ /// <param name="nameServer">NameServer 地址</param>
+ /// <param name="topic">会话通知主题</param>
+ /// <param name="group">生产者组</param>
+ public RocketMQSessionAdapter(String nameServer, String? topic = null, String? group = null)
+ {
+ if (topic != null) Topic = topic;
+ Producer = new Producer
+ {
+ NameServerAddress = nameServer,
+ Topic = Topic,
+ Group = group ?? "PID_SessionEvent",
+ };
+ Producer.Start();
+ }
+ #endregion
+
+ #region 方法
+ /// <summary>通知终端上线</summary>
+ public async Task ProduceOnlineAsync(String mobile, CancellationToken cancellationToken = default)
+ {
+ await Producer.PublishAsync(new { mobile, action = "online", time = DateTime.UtcNow }, null, mobile);
+ }
+
+ /// <summary>通知终端离线</summary>
+ public async Task ProduceOfflineAsync(String mobile, CancellationToken cancellationToken = default)
+ {
+ await Producer.PublishAsync(new { mobile, action = "offline", time = DateTime.UtcNow }, null, mobile);
+ }
+
+ /// <summary>释放资源</summary>
+ public void Dispose()
+ {
+ if (Producer?.Active == true)
+ Producer.Stop();
+ }
+ #endregion
+}
diff --git a/Newlife.JT808/YueBiao/T0200_F1.cs b/Newlife.JT808/YueBiao/T0200_F1.cs
new file mode 100644
index 0000000..c470115
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T0200_F1.cs
@@ -0,0 +1,69 @@
+using NewLife.JT808.Protocols;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>位置附加信息-安装异常(0xF1)</summary>
+/// <remarks>
+/// 粤标位置附加信息,标识终端外设安装异常状态。
+/// </remarks>
+public class T0200_F1
+{
+ #region 属性
+ /// <summary>安装异常状态。位标志</summary>
+ public InstallAbnormalFlags Flags { get; set; }
+ #endregion
+
+ #region 方法
+ /// <summary>从字节数据读取</summary>
+ public static T0200_F1 Parse(Byte[] data)
+ {
+ var reader = Helper.CreateReader(new MemoryStream(data));
+ return new T0200_F1
+ {
+ Flags = (InstallAbnormalFlags)reader.ReadUInt32()
+ };
+ }
+
+ /// <summary>转换为字节数据</summary>
+ public Byte[] ToBytes()
+ {
+ var stream = new MemoryStream();
+ var writer = Helper.CreateWriter(stream);
+ writer.Write((UInt32)Flags);
+ return stream.ToArray();
+ }
+ #endregion
+}
+
+/// <summary>安装异常标志位</summary>
+[Flags]
+public enum InstallAbnormalFlags : UInt32
+{
+ /// <summary>无异常</summary>
+ 无异常 = 0,
+
+ /// <summary>摄像头异常</summary>
+ 摄像头异常 = 0x01,
+
+ /// <summary>主存储器异常</summary>
+ 主存储器异常 = 0x02,
+
+ /// <summary>辅存储器异常</summary>
+ 辅存储器异常 = 0x04,
+
+ /// <summary>红外补光异常</summary>
+ 红外补光异常 = 0x08,
+
+ /// <summary>扬声器异常</summary>
+ 扬声器异常 = 0x10,
+
+ /// <summary>电池异常</summary>
+ 电池异常 = 0x20,
+
+ /// <summary>通信模块异常</summary>
+ 通信模块异常 = 0x40,
+
+ /// <summary>定位模块异常</summary>
+ 定位模块异常 = 0x80,
+}
diff --git a/Newlife.JT808/YueBiao/T0200_F2.cs b/Newlife.JT808/YueBiao/T0200_F2.cs
new file mode 100644
index 0000000..38d04fd
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T0200_F2.cs
@@ -0,0 +1,57 @@
+using NewLife.JT808.Protocols;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>位置附加信息-算法异常(0xF2)</summary>
+/// <remarks>
+/// 粤标位置附加信息,标识终端主动安全算法异常状态。
+/// </remarks>
+public class T0200_F2
+{
+ #region 属性
+ /// <summary>算法异常状态。位标志</summary>
+ public AlgorithmAbnormalFlags Flags { get; set; }
+ #endregion
+
+ #region 方法
+ /// <summary>从字节数据读取</summary>
+ public static T0200_F2 Parse(Byte[] data)
+ {
+ var reader = Helper.CreateReader(new MemoryStream(data));
+ return new T0200_F2
+ {
+ Flags = (AlgorithmAbnormalFlags)reader.ReadUInt32()
+ };
+ }
+
+ /// <summary>转换为字节数据</summary>
+ public Byte[] ToBytes()
+ {
+ var stream = new MemoryStream();
+ var writer = Helper.CreateWriter(stream);
+ writer.Write((UInt32)Flags);
+ return stream.ToArray();
+ }
+ #endregion
+}
+
+/// <summary>算法异常标志位</summary>
+[Flags]
+public enum AlgorithmAbnormalFlags : UInt32
+{
+ /// <summary>无异常</summary>
+ 无异常 = 0,
+
+ /// <summary>ADAS算法异常</summary>
+ ADAS算法异常 = 0x01,
+
+ /// <summary>DSM算法异常</summary>
+ DSM算法异常 = 0x02,
+
+ /// <summary>TPMS算法异常</summary>
+ TPMS算法异常 = 0x04,
+
+ /// <summary>BSD算法异常</summary>
+ BSD算法异常 = 0x08,
+}
diff --git a/Newlife.JT808/YueBiao/T0900_F7.cs b/Newlife.JT808/YueBiao/T0900_F7.cs
new file mode 100644
index 0000000..11f4c8a
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T0900_F7.cs
@@ -0,0 +1,84 @@
+using NewLife.JT808.Protocols;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>USB外设状态信息上报(0x0900 子类型 0xF7)</summary>
+/// <remarks>
+/// 粤标终端通过 0x0900 透传消息上报 USB 外设的状态信息。
+/// </remarks>
+public class T0900_F7
+{
+ #region 属性
+ /// <summary>USB总数量</summary>
+ public Byte TotalCount { get; set; }
+
+ /// <summary>USB设备列表</summary>
+ public USBStatusItem[]? Items { get; set; }
+ #endregion
+
+ #region 方法
+ /// <summary>从透传数据读取</summary>
+ public static T0900_F7 Parse(Byte[] data)
+ {
+ var reader = Helper.CreateReader(new MemoryStream(data));
+ var result = new T0900_F7
+ {
+ TotalCount = reader.ReadByte()
+ };
+
+ var count = reader.ReadByte();
+ var items = new USBStatusItem[count];
+ for (var i = 0; i < count; i++)
+ {
+ items[i] = new USBStatusItem
+ {
+ Id = reader.ReadByte(),
+ Status = reader.ReadByte(),
+ Online = reader.ReadByte(),
+ Reserved = reader.ReadByte(),
+ };
+ }
+ result.Items = items;
+
+ return result;
+ }
+
+ /// <summary>转换为透传数据</summary>
+ public Byte[] ToBytes()
+ {
+ var stream = new MemoryStream();
+ var writer = Helper.CreateWriter(stream);
+
+ writer.Write(TotalCount);
+ var count = (Byte)(Items?.Length ?? 0);
+ writer.Write(count);
+ for (var i = 0; i < count; i++)
+ {
+ var item = Items![i];
+ writer.Write(item.Id);
+ writer.Write(item.Status);
+ writer.Write(item.Online);
+ writer.Write(item.Reserved);
+ }
+
+ return stream.ToArray();
+ }
+ #endregion
+}
+
+/// <summary>USB设备状态项</summary>
+public class USBStatusItem
+{
+ /// <summary>USB接口ID</summary>
+ public Byte Id { get; set; }
+
+ /// <summary>状态。0x00=正常工作,0x01=异常</summary>
+ public Byte Status { get; set; }
+
+ /// <summary>在线状态。0x00=不在线,0x01=在线</summary>
+ public Byte Online { get; set; }
+
+ /// <summary>预留</summary>
+ public Byte Reserved { get; set; }
+}
diff --git a/Newlife.JT808/YueBiao/T0900_F8.cs b/Newlife.JT808/YueBiao/T0900_F8.cs
new file mode 100644
index 0000000..5e5abf0
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T0900_F8.cs
@@ -0,0 +1,89 @@
+using NewLife.JT808.Protocols;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>USB外设信息上报(0x0900 子类型 0xF8)</summary>
+/// <remarks>
+/// 粤标终端通过 0x0900 透传消息上报 USB 外设的详细信息(厂商/型号等)。
+/// </remarks>
+public class T0900_F8
+{
+ #region 属性
+ /// <summary>USB总数量</summary>
+ public Byte TotalCount { get; set; }
+
+ /// <summary>USB设备列表</summary>
+ public USBInfoItem[]? Items { get; set; }
+ #endregion
+
+ #region 方法
+ /// <summary>从透传数据读取</summary>
+ public static T0900_F8 Parse(Byte[] data)
+ {
+ var reader = Helper.CreateReader(new MemoryStream(data));
+ var result = new T0900_F8
+ {
+ TotalCount = reader.ReadByte()
+ };
+
+ var count = reader.ReadByte();
+ var items = new USBInfoItem[count];
+ for (var i = 0; i < count; i++)
+ {
+ items[i] = new USBInfoItem
+ {
+ Id = reader.ReadByte(),
+ Company = reader.ReadString(20),
+ Model = reader.ReadString(20),
+ HardwareVersion = reader.ReadString(10),
+ FirmwareVersion = reader.ReadString(10),
+ };
+ }
+ result.Items = items;
+
+ return result;
+ }
+
+ /// <summary>转换为透传数据</summary>
+ public Byte[] ToBytes()
+ {
+ var stream = new MemoryStream();
+ var writer = Helper.CreateWriter(stream);
+
+ writer.Write(TotalCount);
+ var count = (Byte)(Items?.Length ?? 0);
+ writer.Write(count);
+ for (var i = 0; i < count; i++)
+ {
+ var item = Items![i];
+ writer.Write(item.Id);
+ writer.WriteFixedString(item.Company, 20);
+ writer.WriteFixedString(item.Model, 20);
+ writer.WriteFixedString(item.HardwareVersion, 10);
+ writer.WriteFixedString(item.FirmwareVersion, 10);
+ }
+
+ return stream.ToArray();
+ }
+ #endregion
+}
+
+/// <summary>USB设备信息项</summary>
+public class USBInfoItem
+{
+ /// <summary>USB接口ID</summary>
+ public Byte Id { get; set; }
+
+ /// <summary>制造商</summary>
+ public String Company { get; set; } = String.Empty;
+
+ /// <summary>型号</summary>
+ public String Model { get; set; } = String.Empty;
+
+ /// <summary>硬件版本</summary>
+ public String HardwareVersion { get; set; } = String.Empty;
+
+ /// <summary>固件版本</summary>
+ public String FirmwareVersion { get; set; } = String.Empty;
+}
diff --git a/Newlife.JT808/YueBiao/T1FC4.cs b/Newlife.JT808/YueBiao/T1FC4.cs
new file mode 100644
index 0000000..e3e4152
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T1FC4.cs
@@ -0,0 +1,113 @@
+using System.Globalization;
+using NewLife.JT808.Protocols;
+using NewLife.JT808.SuBiao;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>粤标主动安全报警上报(0x1FC4)</summary>
+/// <remarks>
+/// 粤标核心报警消息,终端向平台上报 ADAS/DSM/TPMS/BSD 等主动安全报警信息。
+/// 与苏标报警上报不同,粤标使用独立消息 ID 0x1FC4 而非位置附加信息。
+/// </remarks>
+[MessageKind(MessageKinds.粤标主动安全报警)]
+public class T1FC4 : IAccessor
+{
+ #region 属性
+ /// <summary>终端ID。粤标30字节</summary>
+ [FieldSize(30)]
+ public String TerminalId { get; set; } = String.Empty;
+
+ /// <summary>报警标识号</summary>
+ public AlarmDevice AlarmId { get; set; } = new();
+
+ /// <summary>车辆状态</summary>
+ public CarStatus Status { get; set; }
+
+ /// <summary>报警类型。0x64=ADAS, 0x65=DSM, 0x66=TPMS, 0x67=BSD</summary>
+ public Byte AlarmType { get; set; }
+
+ /// <summary>报警/事件类型。根据 AlarmType 对应不同枚举</summary>
+ public Byte AlarmKind { get; set; }
+
+ /// <summary>报警级别。1=一级报警,2=二级报警</summary>
+ public Byte Level { get; set; }
+
+ /// <summary>车速。单位 km/h</summary>
+ public Byte Speed { get; set; }
+
+ /// <summary>海拔高度,单位 m</summary>
+ public Int16 Altitude { get; set; }
+
+ /// <summary>纬度。百万分之一度</summary>
+ public Int32 Latitude { get; set; }
+
+ /// <summary>经度。百万分之一度</summary>
+ public Int32 Longitude { get; set; }
+
+ /// <summary>时间。YY-MM-DD-hh-mm-ss</summary>
+ [BCDTime(6, "yyMMddHHmmss")]
+ public DateTime Time { get; set; }
+
+ /// <summary>报警数据长度</summary>
+ public Int16 DataLength { get; set; }
+
+ /// <summary>报警数据。根据 AlarmType 类型不同,长度可变</summary>
+ public Byte[]? Data { get; set; }
+ #endregion
+
+ #region IAccessor
+ /// <summary>读取</summary>
+ public Boolean Read(Stream stream, Object? context)
+ {
+ var reader = Helper.CreateReader(stream);
+
+ TerminalId = reader.ReadFixedString(30);
+ AlarmId = reader.Read<AlarmDevice>();
+ Status = (CarStatus)reader.ReadUInt16();
+ AlarmType = reader.ReadByte();
+ AlarmKind = reader.ReadByte();
+ Level = reader.ReadByte();
+ Speed = reader.ReadByte();
+ Altitude = reader.ReadInt16();
+ Latitude = reader.ReadInt32();
+ Longitude = reader.ReadInt32();
+
+ var str = reader.ReadBCD(6);
+ if (DateTime.TryParseExact(str, "yyMMddHHmmss", null, DateTimeStyles.None, out var dt))
+ Time = dt;
+
+ DataLength = reader.ReadInt16();
+
+ if (DataLength > 0)
+ Data = reader.ReadBytes(DataLength);
+
+ return true;
+ }
+
+ /// <summary>写入</summary>
+ public Boolean Write(Stream stream, Object? context)
+ {
+ var writer = Helper.CreateWriter(stream);
+
+ writer.WriteFixedString(TerminalId, 30);
+ writer.Write(AlarmId);
+ writer.Write((UInt16)Status);
+ writer.Write(AlarmType);
+ writer.Write(AlarmKind);
+ writer.Write(Level);
+ writer.Write(Speed);
+ writer.Write(Altitude);
+ writer.Write(Latitude);
+ writer.Write(Longitude);
+ writer.WriteBCD(Time.ToString("yyMMddHHmmss"), 6);
+
+ DataLength = (Int16)(Data?.Length ?? 0);
+ writer.Write(DataLength);
+ if (Data is { Length: > 0 })
+ writer.Write(Data);
+
+ return true;
+ }
+ #endregion
+}
diff --git a/Newlife.JT808/YueBiao/T8103_F364.cs b/Newlife.JT808/YueBiao/T8103_F364.cs
new file mode 100644
index 0000000..b82f3a5
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T8103_F364.cs
@@ -0,0 +1,34 @@
+using NewLife.JT808.Protocols;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>设置终端参数-前向碰撞报警参数(0x8103 参数ID 0xF364)</summary>
+public class T8103_F364
+{
+ /// <summary>参数ID</summary>
+ public const UInt32 ParamId = YueBiaoConstants.Param_ADAS;
+
+ /// <summary>报警使能。位标志,bit0=前向碰撞</summary>
+ public UInt32 Enable { get; set; }
+
+ /// <summary>报警级别。1=一级,2=二级</summary>
+ public Byte Level { get; set; }
+
+ /// <summary>报警速度阈值。单位 km/h</summary>
+ public Byte SpeedThreshold { get; set; }
+
+ /// <summary>报警距离阈值。单位 m</summary>
+ public Int16 DistanceThreshold { get; set; }
+
+ /// <summary>报警持续时间。单位 s</summary>
+ public Byte Duration { get; set; }
+
+ /// <summary>是否主动拍照。0=否,1=是</summary>
+ public Byte EnablePhoto { get; set; }
+
+ /// <summary>拍照间隔。单位 s</summary>
+ public Byte PhotoInterval { get; set; }
+
+ /// <summary>预留</summary>
+ public Byte Reserved { get; set; }
+}
diff --git a/Newlife.JT808/YueBiao/T8103_F370.cs b/Newlife.JT808/YueBiao/T8103_F370.cs
new file mode 100644
index 0000000..3b3cb0e
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T8103_F370.cs
@@ -0,0 +1,22 @@
+using NewLife.JT808.Protocols;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>设置终端参数-盲区监测参数(0x8103 参数ID 0xF370)</summary>
+public class T8103_F370
+{
+ /// <summary>参数ID</summary>
+ public const UInt32 ParamId = YueBiaoConstants.Param_BSD;
+
+ /// <summary>报警使能。位标志</summary>
+ public UInt32 Enable { get; set; }
+
+ /// <summary>报警级别。1=一级,2=二级</summary>
+ public Byte Level { get; set; }
+
+ /// <summary>报警速度阈值。单位 km/h</summary>
+ public Byte SpeedThreshold { get; set; }
+
+ /// <summary>预留</summary>
+ public Byte Reserved { get; set; }
+}
diff --git a/Newlife.JT808/YueBiao/T8900_F7.cs b/Newlife.JT808/YueBiao/T8900_F7.cs
new file mode 100644
index 0000000..6f7840f
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T8900_F7.cs
@@ -0,0 +1,34 @@
+using NewLife.JT808.Protocols;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>USB外设状态查询(0x8900 子类型 0xF7)</summary>
+/// <remarks>
+/// 平台通过 0x8900 透传消息查询终端 USB 外设状态。
+/// </remarks>
+public class T8900_F7
+{
+ #region 属性
+ /// <summary>USB接口ID。0xFF=查询所有</summary>
+ public Byte UsbId { get; set; }
+ #endregion
+
+ #region 方法
+ /// <summary>序列化</summary>
+ public Byte[] ToBytes()
+ {
+ var stream = new MemoryStream();
+ var writer = Helper.CreateWriter(stream);
+ writer.Write(UsbId);
+ return stream.ToArray();
+ }
+
+ /// <summary>反序列化</summary>
+ public static T8900_F7 Parse(Byte[] data)
+ {
+ var reader = Helper.CreateReader(new MemoryStream(data));
+ return new T8900_F7 { UsbId = reader.ReadByte() };
+ }
+ #endregion
+}
diff --git a/Newlife.JT808/YueBiao/T8900_F8.cs b/Newlife.JT808/YueBiao/T8900_F8.cs
new file mode 100644
index 0000000..3b8e431
--- /dev/null
+++ b/Newlife.JT808/YueBiao/T8900_F8.cs
@@ -0,0 +1,34 @@
+using NewLife.JT808.Protocols;
+using NewLife.Serialization;
+
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>USB外设信息查询(0x8900 子类型 0xF8)</summary>
+/// <remarks>
+/// 平台通过 0x8900 透传消息查询终端 USB 外设详细信息。
+/// </remarks>
+public class T8900_F8
+{
+ #region 属性
+ /// <summary>USB接口ID。0xFF=查询所有</summary>
+ public Byte UsbId { get; set; }
+ #endregion
+
+ #region 方法
+ /// <summary>序列化</summary>
+ public Byte[] ToBytes()
+ {
+ var stream = new MemoryStream();
+ var writer = Helper.CreateWriter(stream);
+ writer.Write(UsbId);
+ return stream.ToArray();
+ }
+
+ /// <summary>反序列化</summary>
+ public static T8900_F8 Parse(Byte[] data)
+ {
+ var reader = Helper.CreateReader(new MemoryStream(data));
+ return new T8900_F8 { UsbId = reader.ReadByte() };
+ }
+ #endregion
+}
diff --git a/Newlife.JT808/YueBiao/YueBiaoAlarmKinds.cs b/Newlife.JT808/YueBiao/YueBiaoAlarmKinds.cs
new file mode 100644
index 0000000..1eee0cc
--- /dev/null
+++ b/Newlife.JT808/YueBiao/YueBiaoAlarmKinds.cs
@@ -0,0 +1,109 @@
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>粤标报警/事件类型枚举</summary>
+public enum YueBiaoAlarmKinds : Byte
+{
+ #region 高级驾驶辅助(ADAS)
+ /// <summary>前向碰撞报警</summary>
+ 前向碰撞 = 0x01,
+
+ /// <summary>车道偏离报警</summary>
+ 车道偏离 = 0x02,
+
+ /// <summary>车距过近报警</summary>
+ 车距过近 = 0x03,
+
+ /// <summary>行人碰撞报警</summary>
+ 行人碰撞 = 0x04,
+
+ /// <summary>频繁变道报警</summary>
+ 频繁变道 = 0x05,
+
+ /// <summary>道路标识超限报警</summary>
+ 道路标识超限 = 0x06,
+
+ /// <summary>障碍物报警</summary>
+ 障碍物 = 0x07,
+
+ /// <summary>道路标志识别</summary>
+ 道路标志识别 = 0x10,
+
+ /// <summary>主动抓拍</summary>
+ 主动抓拍 = 0x11,
+ #endregion
+
+ #region 驾驶状态监测(DSM)
+ /// <summary>疲劳驾驶报警</summary>
+ 疲劳驾驶 = 0x01,
+
+ /// <summary>拨打电话报警</summary>
+ 拨打电话 = 0x02,
+
+ /// <summary>抽烟报警</summary>
+ 抽烟 = 0x03,
+
+ /// <summary>分神驾驶报警</summary>
+ 分神驾驶 = 0x04,
+
+ /// <summary>驾驶员异常报警</summary>
+ 驾驶员异常 = 0x05,
+
+ /// <summary>探头遮挡报警</summary>
+ 探头遮挡 = 0x06,
+
+ /// <summary>打哈欠报警</summary>
+ 打哈欠 = 0x07,
+
+ /// <summary>超时驾驶报警</summary>
+ 超时驾驶 = 0x08,
+
+ /// <summary>未系安全带报警</summary>
+ 未系安全带 = 0x0A,
+
+ /// <summary>红外阻断型墨镜失效报警</summary>
+ 红外阻断 = 0x0B,
+
+ /// <summary>双手脱离方向盘报警</summary>
+ 双手脱把 = 0x0C,
+
+ /// <summary>玩手机报警</summary>
+ 玩手机 = 0x0D,
+
+ /// <summary>自动抓拍</summary>
+ 自动抓拍 = 0x10,
+
+ /// <summary>驾驶员变更</summary>
+ 驾驶员变更 = 0x11,
+
+ /// <summary>摄像头阻挡报警</summary>
+ 摄像头阻挡 = 0x13,
+ #endregion
+
+ #region 胎压监测(TPMS)
+ /// <summary>胎压异常</summary>
+ 胎压异常 = 0x01,
+
+ /// <summary>胎温异常</summary>
+ 胎温异常 = 0x02,
+
+ /// <summary>传感器异常</summary>
+ 传感器异常 = 0x03,
+
+ /// <summary>胎压不平衡</summary>
+ 胎压不平衡 = 0x04,
+
+ /// <summary>电池电量低</summary>
+ 电池电量低 = 0x05,
+ #endregion
+
+ #region 盲区监测(BSD)
+ /// <summary>后方接近报警</summary>
+ 后方接近 = 0x01,
+
+ /// <summary>左侧后方接近报警</summary>
+ 左侧后方接近 = 0x02,
+
+ /// <summary>右侧后方接近报警</summary>
+ 右侧后方接近 = 0x03,
+ #endregion
+}
diff --git a/Newlife.JT808/YueBiao/YueBiaoConstants.cs b/Newlife.JT808/YueBiao/YueBiaoConstants.cs
new file mode 100644
index 0000000..3c8b967
--- /dev/null
+++ b/Newlife.JT808/YueBiao/YueBiaoConstants.cs
@@ -0,0 +1,65 @@
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>粤标常量</summary>
+public static class YueBiaoConstants
+{
+ #region 参数ID(0x8103)
+ /// <summary>主动安全参数-前向碰撞报警参数</summary>
+ public const UInt32 Param_ADAS = 0xF364;
+
+ /// <summary>主动安全参数-车道偏离报警参数</summary>
+ public const UInt32 Param_LaneDeparture = 0xF365;
+
+ /// <summary>主动安全参数-车距过近报警参数</summary>
+ public const UInt32 Param_NearDistance = 0xF366;
+
+ /// <summary>主动安全参数-行人碰撞报警参数</summary>
+ public const UInt32 Param_PedestrianCollision = 0xF367;
+
+ /// <summary>主动安全参数-疲劳驾驶报警参数</summary>
+ public const UInt32 Param_FatigueDriving = 0xF368;
+
+ /// <summary>主动安全参数-拨打电话报警参数</summary>
+ public const UInt32 Param_PhoneCall = 0xF369;
+
+ /// <summary>主动安全参数-抽烟报警参数</summary>
+ public const UInt32 Param_Smoking = 0xF36A;
+
+ /// <summary>主动安全参数-分神驾驶报警参数</summary>
+ public const UInt32 Param_DistractedDriving = 0xF36B;
+
+ /// <summary>主动安全参数-驾驶员异常报警参数</summary>
+ public const UInt32 Param_DriverAbnormal = 0xF36C;
+
+ /// <summary>主动安全参数-超时驾驶报警参数</summary>
+ public const UInt32 Param_OvertimeDriving = 0xF36D;
+
+ /// <summary>主动安全参数-设备自检参数</summary>
+ public const UInt32 Param_SelfCheck = 0xF36E;
+
+ /// <summary>主动安全参数-车辆信息设置</summary>
+ public const UInt32 Param_VehicleInfo = 0xF36F;
+
+ /// <summary>主动安全参数-盲区监测参数</summary>
+ public const UInt32 Param_BSD = 0xF370;
+ #endregion
+
+ #region 透传消息子类型(0x0900/0x8900)
+ /// <summary>USB外设状态信息上报</summary>
+ public const Byte Passthrough_USBStatus = 0xF7;
+
+ /// <summary>USB外设信息上报</summary>
+ public const Byte Passthrough_USBInfo = 0xF8;
+ #endregion
+
+ #region 报警附件协议终端ID长度
+ /// <summary>苏标终端ID长度</summary>
+ public const Int32 TerminalIdLength_SuBiao = 7;
+
+ /// <summary>粤标终端ID长度</summary>
+ public const Int32 TerminalIdLength_YueBiao = 30;
+
+ /// <summary>粤标报警标识号长度</summary>
+ public const Int32 AlarmIdLength_YueBiao = 40;
+ #endregion
+}
diff --git a/Newlife.JT808/YueBiao/YueBiaoMsgIds.cs b/Newlife.JT808/YueBiao/YueBiaoMsgIds.cs
new file mode 100644
index 0000000..4c19a7b
--- /dev/null
+++ b/Newlife.JT808/YueBiao/YueBiaoMsgIds.cs
@@ -0,0 +1,14 @@
+namespace NewLife.JT808.YueBiao;
+
+/// <summary>粤标透传消息子类型</summary>
+/// <remarks>
+/// 粤标(广东省主动安全扩展)在 0x0900/0x8900 透传消息中使用的子类型定义。
+/// </remarks>
+public enum YueBiaoPassthroughTypes : Byte
+{
+ /// <summary>USB外设状态信息上报。0x0900/0x8900 子类型</summary>
+ USB外设状态 = 0xF7,
+
+ /// <summary>USB外设信息上报。0x0900/0x8900 子类型</summary>
+ USB外设信息 = 0xF8,
+}
diff --git a/Readme.MD b/Readme.MD
index 14a3016..2e5b69a 100644
--- a/Readme.MD
+++ b/Readme.MD
@@ -6,11 +6,11 @@


-JT/T 808 车辆定位协议完整实现,支持 2011/2013/2019 三个版本,涵盖 70+ 标准消息体、苏标主动安全扩展、JT/T 1078 音视频基础消息及 JT/T 19056 行车记录仪协议。
+JT/T 808 车辆定位协议完整实现,支持 2011/2013/2019 三个版本,涵盖 70+ 标准消息体、苏标/粤标主动安全扩展、JT/T 1078 音视频基础消息、JT/T 19056 行车记录仪协议及 RocketMQ 消息队列集成。
源码: https://github.com/NewLifeX/NewLife.JT808
Nuget:[NewLife.JT808](https://www.nuget.org/packages/NewLife.JT808)
-文档:[需求文档](Doc/需求文档.md) / [架构设计](Doc/架构设计.md) / [功能清单](Doc/功能清单.md)
+文档:[需求文档](Doc/需求文档.md) / [架构设计](Doc/架构设计.md) / [功能清单](Doc/功能清单.md) / [使用文档](Doc/使用文档.md)
## 主要特点
@@ -22,15 +22,17 @@ Nuget:[NewLife.JT808](https://www.nuget.org/packages/NewLife.JT808)
- **TLV/KLV 解析**:内置 Type-Length-Value 和 Kind-Length-Value 通用编解码,支持强类型字典转换
- **网络通信层**:内置 TCP 粘包编解码器 `JTCodec` 和客户端 `JT808Client`,支持消息处理器自动路由
- **苏标扩展**:支持 ADAS/DSM/TPMS/BSD 主动安全报警信息,外设状态/信息解析,报警附件协议
+- **粤标扩展**:支持广东省主动安全标准(粤标),含独立报警上报(0x1FC4)、USB 外设透传、位置附加信息、专用参数设置
- **音视频基础**:JT/T 1078 音视频消息体模型和枚举定义
- **行车记录仪**:JT/T 19056 上下行数据包编解码
- **BCD 编码**:BCD 字符串和时间自定义序列化特性,适配协议特有编码格式
- **GBK 编码**:内置 GBK 中文字符串编解码,自动注册 `CodePagesEncodingProvider`
- **Web API 管理**:支持统一下发指令、在线会话管理、黑名单管理、Token 鉴权,方便集成到后台系统
-- **消息队列抽象**:IMsgProducer/IMsgConsumer/ISessionProducer 等接口解耦消息处理,内置 DefaultMsgQueue 内存实现
+- **消息队列抽象**:IMsgProducer/IMsgConsumer/ISessionProducer 等接口解耦消息处理,内置 DefaultMsgQueue 内存实现、Redis Stream 生产/消费支持、RocketMQ 适配器
- **跨服务器指令下发**:ICommandBus 接口 + Stardust 事件总线,支持集群广播指令到指定终端
- **客户端增强**:自动重连、可配心跳、收发统计计数器,提升生产环境可靠性
-- **多框架支持**:`net45 / net461 / netstandard2.0 / netstandard2.1`
+- **多框架支持**:`net45 / net461 / netstandard2.0 / netstandard2.1` 全兼容
+- **RocketMQ 集成**:基于 `NewLife.RocketMQ` 的消息队列生产/消费适配器,可作为 Redis Stream 的替代方案
## 支持的协议版本