初始化 JT808 协议库:协议实现、模型、文档、CI - 完善项目结构与配置,支持多目标框架和 NuGet 元数据 - 实现协议核心类(JT808Config、JTCodec)、消息体模型统一至 Models 命名空间 - 新增 JT808Client TCP 客户端,支持消息收发与处理器路由 - 增加消息工厂扩展、GBK 编码注册、分包编解码 - 集成 GitHub Actions 测试与发布流程 - 补充详细文档(Readme、需求、架构、功能清单、变更日志) - 明确依赖与兼容性,完善 License、图标等元数据大石头 authored at 2026-07-12 22:10:57
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..396b091
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,44 @@
+# EditorConfig is awesome:http://EditorConfig.org
+# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
+
+# top-most EditorConfig file
+root = true
+
+# Don't use tabs for indentation.
+[*]
+indent_style = space
+# (Please don't specify an indent_size here; that has too many unintended consequences.)
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = false
+charset = utf-8-bom
+end_of_line = crlf
+
+# Xml project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# Xml config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# JSON files
+[*.json]
+indent_size = 2
+
+# Dotnet code style settings:
+[*.{cs,vb}]
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = flush_left
+
+# 单行放置代码
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
+
+# Avoid "this." and "Me." if not necessary
diff --git a/.github/workflows/publish-beta.yml b/.github/workflows/publish-beta.yml
new file mode 100644
index 0000000..a74825c
--- /dev/null
+++ b/.github/workflows/publish-beta.yml
@@ -0,0 +1,35 @@
+name: publish-beta
+
+on:
+ push:
+ branches:
+ - master
+ - main
+ paths:
+ - 'Newlife.JT808/**'
+ workflow_dispatch:
+
+jobs:
+ build-publish:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup dotNET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 6.x
+ 7.x
+ 8.x
+ 9.x
+ 10.x
+ - name: Get Version
+ run: echo "VERSION=$(date '+%Y.%m%d-beta%H%M')" >> $GITHUB_ENV
+ - name: Build
+ run: |
+ dotnet pack --version-suffix ${{ env.VERSION }} -c Release -o out Newlife.JT808/NewLife.JT808.csproj
+ - name: Publish
+ run: |
+ dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.nugetKey }}
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..3a88f4d
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,29 @@
+name: publish
+
+on:
+ push:
+ tags: [ v* ]
+ workflow_dispatch:
+
+jobs:
+ build-publish:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup dotNET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 6.x
+ 7.x
+ 8.x
+ 9.x
+ 10.x
+ - name: Build
+ run: |
+ dotnet pack -c Release -o out Newlife.JT808/NewLife.JT808.csproj
+ - name: Publish
+ run: |
+ dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.nugetKey }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..a5321cf
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,29 @@
+name: test
+
+on:
+ push:
+ branches: [ '*' ]
+ pull_request:
+ branches: [ '*' ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup dotNET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 6.x
+ 7.x
+ 8.x
+ 9.x
+ 10.x
+ - name: Build
+ run: dotnet build -c Release
+
+ - name: Test
+ run: dotnet test -c Release
diff --git a/.gitignore b/.gitignore
index d6d4ae0..8b53321 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,24 @@
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
################################################################################
-/.vs/Newlife.JT808
-/Newlife.JT808/obj
-/.vs/ProjectEvaluation
-/Newlife.JT808/bin/Debug/net7.0
+/.vs
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+/packages
+*.user
+/Data
+/Log
+*.log
+*.htm
+*.nuspec
+*.nupkg
+/BinTest
+/BinUnitTest
diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
index 0000000..f92adff
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,15 @@
+# NewLife.JT808 版本更新记录
+
+## v1.0.2026.0712 (2026-07-12)
+
+### 初始版本
+- **核心协议**:完整实现 JT/T 808-2013/2019 消息帧解析,支持 0x7E 帧边界、转义处理、异或校验
+- **消息体模型**:覆盖 65+ 标准消息体(T0001~T8A00),支持双模式序列化(反射自动 + IAccessor 手动)
+- **双版本兼容**:通过 Version 字段 + [JT2019] 特性实现 2013/2019 版本自动适配
+- **TLV/KLV**:通用 TLV(Type-Length-Value)和 KLV(Kind-Length-Value)编解码
+- **苏标扩展**:支持 ADAS/DSM/TPMS/BSD 主动安全报警信息,外设状态/信息解析
+- **BCD 编码**:BCD 字符串和 BCD 时间自定义序列化特性
+- **GBK 编码**:内置 GBK 中文字符串编解码支持
+
+### 依赖
+- 基于 NewLife.Core v10.x 序列化、网络、日志基础设施
diff --git a/Doc/leaf.png b/Doc/leaf.png
new file mode 100644
index 0000000..cb15e89
Binary files /dev/null and b/Doc/leaf.png differ
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"
new file mode 100644
index 0000000..651a3f6
--- /dev/null
+++ "b/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
@@ -0,0 +1,81 @@
+# NewLife.JT808 功能清单
+
+> 版本:v1.0 | 日期:2026-07-12
+
+> 状态标记:✅ 已实现 | 🟡 部分实现 | 🔧 规划中 | ⏸ 暂缓/占位 | ❌ 未开始
+
+本清单维护"核心目标 → 功能模块/子模块 → 完成状态"。模块名优先对齐[需求文档](需求文档.md)第 3 章的功能点名称;需要更细设计时跳转到[架构设计](架构设计.md)。
+
+## M1 核心协议层
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M1-1 | 消息帧解析(0x7E/转义/校验) | ✅ | `JTMessage.Read()` |
+| M1-2 | 消息帧构建(封帧/转义/校验) | ✅ | `JTMessage.Write()` |
+| M1-3 | 版本识别(2011/2013/2019) | ✅ | 消息体属性第14位 |
+| M1-4 | 分包支持(总包数/序号/缓存合并) | 🟡 | 帧解析已有,合并待补 |
+| M1-5 | 消息类型注册(`[MessageKind]` 扫描) | ✅ | `MessageFactory.Init()` |
+| M1-6 | 外部程序集注册 | ❌ | `MessageFactory.Register(Assembly)` |
+| M1-7 | 自定义消息类型映射/覆盖 | ❌ | `SetMap<T>()` |
+| M1-8 | 协议配置类(版本/校验/加密) | ❌ | `JT808Config` |
+| M1-9 | 版本感知序列化增强 | 🟡 | `[JT2019]` 已有,`Version` 参数需增强 |
+
+## M2 标准消息体
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M2-1 | 终端上行消息体(42+ 个) | ✅ | T0001~T0901 等 |
+| M2-2 | 平台下行消息体(27+ 个) | ✅ | T8001~T8A00 等 |
+| M2-3 | 反射自动序列化 | ✅ | `Binary.TryRead/TryWrite` |
+| M2-4 | IAccessor 手动序列化 | ✅ | T0100/T0200/T0107/T0702 等 |
+| M2-5 | 版本条件字段 | ✅ | `[JT2019]` 特性 |
+| M2-6 | BCD 编码字段 | ✅ | `[BCDString]`/`[BCDTime]` |
+| M2-7 | FullBytes/FullString2 剩余数据读取 | ✅ | `[FullBytes]`/`[FullString2]` |
+| M2-8 | 位置附加信息结构化解析 | 🟡 | KLV 读取已有,强类型转换待补 |
+
+## M3 网络通信层
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M3-1 | 粘包编解码器(PacketCodec) | ❌ | `JTCodec` 待实现 |
+| M3-2 | TCP 客户端 | ❌ | `JT808Client` 待实现 |
+| M3-3 | 消息处理器路由 | ❌ | `[MessageKind]` → Handler 映射 |
+| M3-4 | 分包合并 | ❌ | MemoryCache + 自动合并 |
+| M3-5 | 协议配置 | ❌ | `JT808Config` 待实现 |
+
+## M4 苏标扩展
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M4-1 | ADAS 报警信息 | ✅ | `ADASInfo.cs` 16种报警 |
+| M4-2 | DSM 报警信息 | ✅ | `DSMInfo.cs` 16种报警 |
+| M4-3 | TPMS 报警信息 | ✅ | `TPMSInfo.cs` |
+| M4-4 | BSD 报警信息 | ✅ | `BSDInfo.cs` |
+| M4-5 | 外设工作状态/系统信息/类型 | ✅ | `PeripheralStatus.cs`/`PeripheralInfo.cs`/`PeripheralKinds.cs` |
+| M4-6 | 报警附件协议 | ✅ | T1210/T1211/T9208 |
+| M4-7 | 音视频资源列表 | ✅ | T1205/T1206 |
+| M4-8 | 文件传输消息 | ✅ | `FileMessage.cs` |
+| M4-9 | 车辆状态/码流枚举 | ✅ | `CarStatus.cs`/`BitStreams.cs` |
+
+## M5 辅助工具
+
+| 编码 | 功能 | 状态 | 说明 |
+|------|------|------|------|
+| M5-1 | GBK 编解码 | ✅ | `Helper` 静态构造注册 |
+| M5-2 | 版本感知 Reader/Writer | ✅ | `Helper.CreateReader/CreateWriter` |
+| M5-3 | JSON 序列化 | ✅ | `ToJsonJT808` 扩展 |
+| M5-4 | TLV 解析 | ✅ | `TLV.cs` + `Convert<TKind>()` |
+| M5-5 | KLV 解析 | ✅ | `KLV.cs` + `Convert<TKind>()` |
+| M5-6 | 枚举定义(报警/状态/参数等) | ✅ | UtilTool/ 下全部枚举 |
+| M5-7 | 快速反序列化 | 🟡 | `FastRead<T>` 待完善 |
+
+## 统计
+
+| 模块 | 功能数 | 已完成 | 完成率 |
+|------|--------|--------|--------|
+| M1 核心协议层 | 9 | 5 | 56% |
+| M2 标准消息体 | 8 | 7 | 88% |
+| M3 网络通信层 | 5 | 0 | 0% |
+| M4 苏标扩展 | 9 | 9 | 100% |
+| M5 辅助工具 | 7 | 6 | 86% |
+| **总计** | **38** | **27** | **71%** |
diff --git "a/Doc/\346\236\266\346\236\204\350\256\276\350\256\241.md" "b/Doc/\346\236\266\346\236\204\350\256\276\350\256\241.md"
new file mode 100644
index 0000000..6a1dec9
--- /dev/null
+++ "b/Doc/\346\236\266\346\236\204\350\256\276\350\256\241.md"
@@ -0,0 +1,160 @@
+# NewLife.JT808 架构设计
+
+> 版本:v1.0 | 日期:2026-07-12
+> 需求对应:[需求文档](需求文档.md) | 功能清单:[功能清单](功能清单.md)
+
+## 1. 整体架构
+
+JT/T 808 协议库采用单项目两层结构(协议层 + 模型层),无数据层和服务层。所有类型位于 `NewLife.JT808` 命名空间下。
+
+```
+┌──────────────────────────────────────────────────┐
+│ 应用层 │
+│ (使用者代码:JT808Client / 自定义处理器) │
+├──────────────────────────────────────────────────┤
+│ JT808Client JTCodec Handler路由 │
+│ (TCP客户端) (粘包编解码) (消息分发) │ 网络通信层
+├──────────────────────────────────────────────────┤
+│ MessageFactory JTMessage JT808Config │
+│ (消息工厂) (消息帧) (配置) │ 协议基础设施
+├──────────────────────────────────────────────────┤
+│ TLV / KLV BCDString / BCDTime │
+│ FullBytes / FullString2 / JT2019 │ 序列化特性
+├──────────────────────────────────────────────────┤
+│ T0001 ... T8A00 ADASInfo ... TPMSInfo │
+│ (标准消息体) (苏标扩展信息) │ 业务模型
+├──────────────────────────────────────────────────┤
+│ Helper MessageKinds PositionAlarms ... │
+│ (辅助) (消息枚举) (业务枚举) │ 辅助工具
+└──────────────────────────────────────────────────┘
+```
+
+**核心设计原则**:
+- 协议帧(JTMessage)与消息体(Txxxx)分离,各自独立序列化
+- 序列化方式按复杂度分级:简单消息反射自动,复杂消息手动 IAccessor
+- 版本差异通过特性驱动,避免代码分支
+
+## 2. 核心组件
+
+| 组件 | 命名空间 | 说明 |
+|------|----------|------|
+| `JTMessage` | `NewLife.JT808.Protocols` | JT/T 808 消息帧,负责 0x7E 帧解析、转义处理、异或校验、分包元数据 |
+| `MessageFactory` | `NewLife.JT808.Protocols` | 消息工厂,维护 `MessageKinds↔Type` 映射,提供 `Create/Parse` 方法 |
+| `MessageKinds` | `NewLife.JT808.Protocols` | 消息类型枚举(UInt16 底层值),覆盖全部标准消息ID |
+| `TLV` | `NewLife.JT808.Protocols` | Type(4B)-Length(1B)-Value 通用编解码,用于 0x8103/0x0104 |
+| `KLV` | `NewLife.JT808.Protocols` | Kind(1B)-Length(1B)-Value 通用编解码,用于 0x0200 附加信息 |
+| `Helper` | `NewLife.JT808.UtilTool` | 辅助类:GBK 编码注册、版本感知 Binary 创建、FastRead、ToJsonJT808 |
+| `JT808Config` | `NewLife.JT808.Protocols` | 协议配置:版本、校验开关、加密密钥、工厂初始化 |
+
+## 3. 关键流程
+
+### 3.1 消息接收流水线
+
+```mermaid
+sequenceDiagram
+ participant TCP as TCP 数据流
+ participant Codec as JTCodec
+ participant Frame as JTMessage
+ participant Factory as MessageFactory
+ participant Model as 业务模型(T0200等)
+
+ TCP->>Codec: 原始字节流
+ Codec->>Codec: 0x7E 分割粘包
+ Codec->>Codec: 0x7D 转义还原
+ Codec->>Frame: 单帧数据
+ Frame->>Frame: 读取 MessageKinds/Version/Mobile
+ Frame->>Frame: 校验和验证
+ Frame->>Factory: Parse(JTMessage)
+ Factory->>Factory: Create(kind) 实例化
+ Factory->>Model: IAccessor.Read() 或 反射序列化
+ Model-->>Factory: 业务模型对象
+```
+
+### 3.2 消息发送流水线
+
+```mermaid
+sequenceDiagram
+ participant App as 应用代码
+ participant Factory as MessageFactory
+ participant Model as 业务模型
+ participant Frame as JTMessage
+ participant Codec as JTCodec
+ participant TCP as TCP 数据流
+
+ App->>Model: new T8001 { ... }
+ App->>Frame: new JTMessage { Kind, Mobile, Payload }
+ Model->>Frame: IAccessor.Write() → Payload
+ Frame->>Frame: 组帧(转义/校验/0x7E)
+ Frame->>Codec: 输出二进制帧
+ Codec->>TCP: 发送
+```
+
+### 3.3 版本兼容机制
+
+```
+JTMessage.Version
+ │
+ ├── Version=0 (2013版)
+ │ └── [JT2019] 特性标记的字段 → 跳过
+ │
+ └── Version=1 (2019版)
+ ├── [JT2019] 特性标记的字段 → 读写
+ ├── [FullString2(Version="2013")] → 跳过
+ └── [FullString2(Version="2019")] → 读写
+```
+
+Binary 序列化器通过 `Binary.Version` 字符串("2013"/"2019")驱动特性条件判断,`Helper.CreateReader/CreateWriter` 根据 `JTMessage.Version` 自动设置。
+
+## 4. 序列化策略
+
+### 4.1 反射自动序列化(简单消息体)
+
+适用场景:属性类型为基础类型、无特殊编解码逻辑、无版本差异字段。
+
+```csharp
+[MessageKind(MessageKinds.平台通用应答)]
+public class T8001
+{
+ public UInt16 Sequence { get; set; }
+ public MessageKinds Kind { get; set; }
+ public ResultKinds Result { get; set; }
+}
+```
+
+序列化过程:
+1. `Binary.TryWrite(null, ref obj)` 反射遍历属性
+2. 检查 `[FieldSize]`、`[FullBytes]`、`[JT2019]` 等特性
+3. 按声明顺序读写,大端序
+
+### 4.2 IAccessor 手动序列化(复杂消息体)
+
+适用场景:多版本字段长度不同、嵌套结构、需要特殊处理的分支逻辑。
+
+```csharp
+[MessageKind(MessageKinds.终端注册)]
+public class T0100 : IAccessor
+{
+ public Boolean Read(Stream stream, Object context)
+ {
+ var reader = Helper.CreateReader(stream, context as JTMessage);
+ // 手写精确的读取逻辑,可访问 header.Version 判断版本
+ }
+}
+```
+
+选择依据:
+- 反射序列化优先级更高(减少样板代码)
+- 仅当反射无法满足时(版本差异、嵌套读取、条件跳过)才降级为 IAccessor
+
+## 5. 设计决策
+
+| 决策 | 选项 | 选择 | 理由 |
+|------|------|------|------|
+| 项目分层 | 单层 vs 两层 vs 三层 | 单项目两层 | 协议库无数据库/业务,两层(协议+模型)足够 |
+| 序列化模式 | 全反射 vs 全手动 vs 双模式 | 双模式 | 大多数消息简单可用反射,少数复杂消息需要精确控制 |
+| 版本兼容 | if分支 vs 特性驱动 | 特性驱动 | 声明式的 `[JT2019]` 比内联if更清晰,减少遗漏 |
+| 命名空间 | `NewLife.IoT` vs `NewLife.JT808` | `NewLife.JT808` | JT808 是独立协议领域,与 Modbus 平等 |
+| 目标框架 | net7.0 vs 多目标 | 多目标 | 与 NewLife 生态一致,最大化兼容性 |
+| 网络层 | 内置 vs 单独包 | 内置(同一包) | 粘包编解码是协议库常用功能,无需单独包 |
+| 服务端 | 内置 vs 排除 | 排除 | 服务端涉及数据库和业务逻辑,超出协议库范围 |
+| 音视频 | 完整 vs 仅基础 | 仅基础消息体 | JT/T 1078 音视频完整实现工程量大,暂排除 |
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"
new file mode 100644
index 0000000..32e10cb
--- /dev/null
+++ "b/Doc/\351\234\200\346\261\202\346\226\207\346\241\243.md"
@@ -0,0 +1,84 @@
+# NewLife.JT808 需求文档
+
+> 版本:v1.0 | 日期:2026-07-12
+
+本文档描述 NewLife.JT808 的愿景、核心目标和功能方向。完成状态在[功能清单](功能清单.md)中追踪,详细设计在[架构设计](架构设计.md)中展开。
+
+## 1. 背景与愿景
+
+### 1.1 系统定位
+
+NewLife.JT808 是 JT/T 808 道路运输车辆卫星定位系统终端通讯协议的 .NET 实现库,为车联网平台、GPS 监控系统、车队管理系统提供协议编解码基础设施。
+
+| 维度 | 说明 |
+|------|------|
+| **使用者** | .NET 开发者、车联网平台开发者、GPS 设备接入开发者 |
+| **解决的问题** | 无需从零实现 JT/T 808 协议帧解析、消息体序列化、版本兼容等底层细节 |
+| **定位** | 纯协议库(类库/NuGet 包),不包含数据库、Web 界面、业务逻辑 |
+
+### 1.2 愿景
+
+成为 .NET 生态中最完整的 JT/T 808 协议实现,让开发者用几行代码即可接入车载终端设备。
+
+## 2. 核心目标
+
+| 编号 | 目标 | 所属层级 | 一句话描述 |
+|------|------|----------|------------|
+| M1 | 核心协议层 | 协议基础设施 | 消息帧解析、转义处理、校验、分包,支持 2011/2013/2019 三版本 |
+| M2 | 标准消息体 | 业务模型 | 覆盖 JT/T 808 全部标准消息体(终端上行 + 平台下行),双模式序列化 |
+| M3 | 网络通信层 | 传输层 | TCP 粘包编解码、客户端封装、分包合并、消息处理器路由 |
+| M4 | 苏标扩展 | 扩展协议 | 苏标主动安全(ADAS/DSM/TPMS/BSD)、外设状态、报警附件协议 |
+| M5 | 辅助工具 | 基础设施 | 枚举定义、BCD/GBK 编码辅助、JSON 序列化、调试输出 |
+
+## 3. 功能需求
+
+### 3.1 M1 核心协议层
+
+- **M1-1 消息帧解析**:解析 0x7E 帧头尾、0x7D 转义(0x7D01→0x7D, 0x7D02→0x7E)、异或校验,输出原始消息体和元数据。
+- **M1-2 消息帧构建**:将消息体封装为完整帧,自动添加转义、校验和、0x7E 标识。
+- **M1-3 版本识别**:从消息体属性第14位识别协议版本(0=2013, 1=2019),设置 `JTMessage.Version`。
+- **M1-4 分包支持**:识别分包标志,解析总包数/包序号,支持分包载荷的缓存与合并。
+- **M1-5 消息类型注册**:通过 `[MessageKind]` 特性自动扫描注册消息类型,支持外部程序集注册。
+
+### 3.2 M2 标准消息体
+
+- **M2-1 终端上行消息**:实现全部终端→平台标准消息体(0x0001~0x0FFF),包含通用应答、心跳、注册、鉴权、位置汇报、报警、多媒体、透传等。
+- **M2-2 平台下行消息**:实现全部平台→终端标准消息体(0x8001~0x8FFF),包含通用应答、参数设置、终端控制、文本下发、区域设置等。
+- **M2-3 反射序列化**:简单属性消息体使用 `Binary.TryRead/TryWrite` 反射自动序列化,支持 `[FieldSize]` 等特性。
+- **M2-4 IAccessor 手动序列化**:复杂消息体实现 `IAccessor` 接口,手写 `Read/Write` 方法精确控制编解码。
+- **M2-5 版本条件字段**:通过 `[JT2019]` 特性标记仅 2019 版存在的字段,非 2019 版自动跳过。
+- **M2-6 BCD 编码字段**:`[BCDString]`、`[BCDTime]` 特性支持 BCD 编码的字符串和时间字段。
+
+### 3.3 M3 网络通信层
+
+- **M3-1 粘包编解码器**:实现 `PacketCodec` 模式处理 TCP 粘包/半包,根据 0x7E 边界分割数据流。
+- **M3-2 TCP 客户端**:封装 TCP 连接管理、消息收发、处理器注册,一行代码发送消息并等待应答。
+- **M3-3 消息处理器路由**:`[MessageKind]` 特性驱动消息→处理器自动路由,支持泛型强类型回调。
+- **M3-4 分包合并**:自动缓存未完成的分包序列,集齐后合并为完整消息并触发处理器。
+- **M3-5 协议配置**:集中管理版本、校验和、加密密钥等配置项。
+
+### 3.4 M4 苏标扩展
+
+- **M4-1 ADAS 报警信息**:前向碰撞、车道偏离、车距过近、行人碰撞、道路标志识别等高级驾驶辅助报警。
+- **M4-2 DSM 报警信息**:疲劳驾驶、打电话、抽烟、分神、探头遮挡等驾驶员状态监测报警。
+- **M4-3 TPMS 报警信息**:胎压、胎温、漏气等胎压监测报警。
+- **M4-4 BSD 报警信息**:盲区监测报警。
+- **M4-5 外设信息**:外设工作状态、系统信息(厂商/型号/版本)、外设类型枚举。
+- **M4-6 报警附件协议**:文件上传/完成通知、附件服务器指令(T1210/T1211/T9208)。
+- **M4-7 音视频资源列表**:T1205/T1206 音视频资源列表和文件上传完成通知。
+
+### 3.5 M5 辅助工具
+
+- **M5-1 GBK 编解码**:自动注册 GBK 编码,提供中文字符串读写扩展方法。
+- **M5-2 版本感知 Reader/Writer**:根据消息版本号自动设置 `Binary.Version`,驱动条件序列化。
+- **M5-3 JSON 序列化**:`ToJsonJT808` 扩展,枚举输出中文名、字节数组输出十六进制。
+- **M5-4 TLV 解析**:Type-Length-Value 通用结构编解码,支持强类型字典转换。
+- **M5-5 KLV 解析**:Kind-Length-Value 通用结构编解码,用于位置附加信息。
+- **M5-6 枚举定义**:报警标志、状态位、车牌颜色、参数ID、位置附加类型等完整枚举。
+
+## 6. 不做什么
+
+- 不实现 JT/T 1078 音视频完整协议:仅保留基础消息体,不实现视频流编解码和容器格式转换。
+- 不实现 Web 管理后台:纯协议库,UI 由使用者自行构建。
+- 不包含数据库持久化:不引入 XCode 或任何 ORM 依赖。
+- 不实现加密算法细节:仅预留加密接口,具体算法由使用者注入。
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7aae441
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 新生命开发团队
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Newlife.JT808.sln b/Newlife.JT808.sln
index 31789d5..c3db526 100644
--- a/Newlife.JT808.sln
+++ b/Newlife.JT808.sln
@@ -1,10 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.4.33403.182
+VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.JT808", "Newlife.JT808\NewLife.JT808.csproj", "{EE9339FA-7ED6-4231-AC07-BB5C2FE64B0F}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{719C113A-6804-4150-8473-BF889F06DFD6}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ .github\workflows\publish-beta.yml = .github\workflows\publish-beta.yml
+ .github\workflows\publish.yml = .github\workflows\publish.yml
+ Readme.MD = Readme.MD
+ .github\workflows\test.yml = .github\workflows\test.yml
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/Newlife.JT808/JT808Client.cs b/Newlife.JT808/JT808Client.cs
new file mode 100644
index 0000000..cea29c4
--- /dev/null
+++ b/Newlife.JT808/JT808Client.cs
@@ -0,0 +1,264 @@
+using System.Reflection;
+using NewLife.Data;
+using NewLife.Log;
+using NewLife.Net;
+using NewLife.Reflection;
+using NewLife.JT808.Protocols;
+using NewLife.JT808.UtilTool;
+
+namespace NewLife.JT808;
+
+/// <summary>JT/T 808消息处理器委托</summary>
+/// <param name="session">会话</param>
+/// <param name="message">收到的消息</param>
+/// <returns>响应消息体,返回null表示不回复</returns>
+public delegate Object MessageHandler(JT808Client session, JTMessage message);
+
+/// <summary>JT/T 808 TCP客户端</summary>
+/// <remarks>
+/// 封装TCP连接管理、消息收发、处理器路由。
+/// 支持同步发送和异步等待响应,通过[MessageKind]特性自动路由消息到处理器。
+/// </remarks>
+public class JT808Client : DisposeBase
+{
+ #region 属性
+ /// <summary>名称</summary>
+ public String Name { get; set; }
+
+ /// <summary>服务端地址</summary>
+ public NetUri Server { get; set; }
+
+ /// <summary>终端手机号</summary>
+ public String Mobile { get; set; }
+
+ /// <summary>协议配置</summary>
+ public JT808Config Config { get; set; } = new JT808Config();
+
+ /// <summary>网络粘包编解码器</summary>
+ public JTCodec Codec { get; set; } = new JTCodec();
+
+ /// <summary>收到消息时触发</summary>
+ public event EventHandler<ReceivedEventArgs> Received;
+
+ /// <summary>消息处理器集合。Key为消息类型,Value为处理委托</summary>
+ public IDictionary<MessageKinds, MessageHandler> Handlers { get; set; } = new Dictionary<MessageKinds, MessageHandler>();
+
+ private ISocketClient _client;
+ /// <summary>底层网络连接</summary>
+ public ISocketClient Client => _client;
+
+ /// <summary>日志</summary>
+ public ILog Log { get; set; } = Logger.Null;
+
+ /// <summary>性能追踪</summary>
+ public ITracer Tracer { get; set; }
+ #endregion
+
+ #region 构造
+ static JT808Client()
+ {
+ // 确保消息工厂已初始化
+ MessageFactory.Instance.Init();
+ }
+
+ /// <summary>销毁时释放网络连接</summary>
+ /// <param name="disposing"></param>
+ protected override void Dispose(Boolean disposing)
+ {
+ base.Dispose(disposing);
+
+ if (_client != null)
+ {
+ _client.Received -= OnReceive;
+ _client.TryDispose();
+ }
+ }
+ #endregion
+
+ #region 连接管理
+ /// <summary>打开连接并开始接收消息</summary>
+ public virtual void Open()
+ {
+ MessageFactory.Instance.Init();
+
+ var client = Server.CreateRemote();
+ client.Name = Name;
+ client.Tracer = Tracer;
+ client.Log = Log;
+
+ client.Received += OnReceive;
+
+#if DEBUG
+ client.LogSend = true;
+ client.LogReceive = true;
+#endif
+
+ client.Open();
+
+ _client = client;
+ }
+
+ /// <summary>接收消息回调</summary>
+ /// <param name="sender"></param>
+ /// <param name="e"></param>
+ protected virtual void OnReceive(Object sender, ReceivedEventArgs e)
+ {
+ // 使用JTCodec处理粘包,解码为JTMessage列表
+ var list = Codec.Decode(e.Packet);
+ if (list == null || list.Count == 0) return;
+
+ foreach (var msg in list)
+ {
+ using var span = Tracer?.NewSpan($"jt808:Receive:{msg.Kind}", msg);
+
+ Received?.Invoke(this, new ReceivedEventArgs { Packet = e.Packet, Message = msg });
+
+ // 路由到已注册的处理器
+ if (Handlers.TryGetValue(msg.Kind, out var handler))
+ handler(this, msg);
+ }
+ }
+ #endregion
+
+ #region 处理器注册
+ /// <summary>注册消息处理器实例</summary>
+ /// <remarks>
+ /// 反射扫描处理器类型的所有方法,匹配规则:
+ /// 1. 非静态方法
+ /// 2. 返回类型为Object
+ /// 3. 参数个数为2
+ /// 4. 带有[MessageKind]特性
+ /// </remarks>
+ /// <typeparam name="T">处理器类型</typeparam>
+ /// <param name="handler">处理器实例</param>
+ public virtual void AddHandler<T>(T handler)
+ {
+ var type = handler.GetType();
+ foreach (var method in type.GetMethods())
+ {
+ if (method.IsStatic) continue;
+ if (method.ReturnType != typeof(Object)) continue;
+
+ // 参数匹配:(JT808Client, JTMessage)
+ var pis = method.GetParameters();
+ if (pis.Length != 2) continue;
+
+ // 获取消息类型映射
+ var att = method.GetCustomAttribute<MessageKindAttribute>();
+ if (att != null)
+ {
+ Handlers[att.Kind] = method.As<MessageHandler>(handler);
+
+ WriteLog("注册[{0}]:{1}.{2}", att.Kind, method.DeclaringType.FullName, method.Name);
+ }
+ }
+ }
+
+ /// <summary>手动注册消息处理器</summary>
+ /// <param name="kind">消息类型</param>
+ /// <param name="handler">处理委托</param>
+ public void RegisterHandler(MessageKinds kind, MessageHandler handler)
+ {
+ Handlers[kind] = handler;
+ }
+ #endregion
+
+ #region 消息收发
+ /// <summary>发送消息,不等待响应</summary>
+ /// <param name="body">消息体对象</param>
+ public virtual void Send(Object body)
+ {
+ var msg = new JTMessage
+ {
+ Mobile = Mobile,
+ };
+
+ if (Config != null) msg.Version = (Byte)(Config.Version == JT808Version.JT2019 ? 1 : 0);
+
+ msg.SetResult(body);
+
+ using var span = Tracer?.NewSpan($"jt808:Send:{msg.Kind}", body);
+
+ WriteLog("=>{0}", body.ToJsonJT808());
+
+ var pk = Codec.Encode(msg);
+ _client.Send(pk);
+ }
+
+ /// <summary>发送消息并等待响应</summary>
+ /// <typeparam name="TResult">响应消息体类型</typeparam>
+ /// <param name="body">请求消息体</param>
+ /// <param name="timeout">超时毫秒</param>
+ /// <returns>响应消息体</returns>
+ public virtual async Task<TResult> SendAsync<TResult>(Object body, Int32 timeout = 3000) where TResult : class, new()
+ {
+ var msg = new JTMessage
+ {
+ Mobile = Mobile,
+ };
+
+ if (Config != null) msg.Version = (Byte)(Config.Version == JT808Version.JT2019 ? 1 : 0);
+
+ msg.SetResult(body);
+
+ using var span = Tracer?.NewSpan($"jt808:SendAsync:{msg.Kind}", body);
+
+ WriteLog("=>{0}", body.ToJsonJT808());
+
+ var pk = Codec.Encode(msg);
+
+ // 使用TaskCompletionSource等待响应
+ var tcs = new TaskCompletionSource<Packet>();
+ EventHandler<ReceivedEventArgs> handler = null;
+ handler = (s, e) =>
+ {
+ if (e.Message is JTMessage reply)
+ {
+ tcs.TrySetResult(e.Packet);
+ }
+ };
+
+ try
+ {
+ Received += handler;
+ _client.Send(pk);
+
+ using var cts = new CancellationTokenSource(timeout);
+ cts.Token.Register(() => tcs.TrySetCanceled());
+ var rs = await tcs.Task;
+ if (rs == null) return null;
+
+ var list = Codec.Decode(rs);
+ var reply = list?.FirstOrDefault();
+ if (reply == null) return null;
+
+ var result = reply.ReadModel<TResult>();
+
+ WriteLog("<={0}", result.ToJsonJT808());
+
+ return result;
+ }
+ finally
+ {
+ Received -= handler;
+ }
+ }
+
+ /// <summary>发送原始JTMessage帧,不等待响应</summary>
+ /// <param name="message">消息帧</param>
+ public virtual void SendMessage(JTMessage message)
+ {
+ if (message.Mobile.IsNullOrEmpty()) message.Mobile = Mobile;
+
+ var pk = Codec.Encode(message);
+ _client.Send(pk);
+ }
+ #endregion
+
+ #region 日志
+ /// <summary>写日志</summary>
+ /// <param name="format"></param>
+ /// <param name="args"></param>
+ public void WriteLog(String format, params Object[] args) => Log?.Info(format, args);
+ #endregion
+}
diff --git a/Newlife.JT808/TModels/T0001.cs b/Newlife.JT808/Models/T0001.cs
similarity index 97%
rename from Newlife.JT808/TModels/T0001.cs
rename to Newlife.JT808/Models/T0001.cs
index f40b265..7f49457 100644
--- a/Newlife.JT808/TModels/T0001.cs
+++ b/Newlife.JT808/Models/T0001.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>返回结果</summary>
public enum ResultKinds : byte
diff --git a/Newlife.JT808/TModels/T0002.cs b/Newlife.JT808/Models/T0002.cs
similarity index 88%
rename from Newlife.JT808/TModels/T0002.cs
rename to Newlife.JT808/Models/T0002.cs
index 4402650..d941026 100644
--- a/Newlife.JT808/TModels/T0002.cs
+++ b/Newlife.JT808/Models/T0002.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>终端心跳</summary>
[MessageKind(MessageKinds.终端心跳)]
diff --git a/Newlife.JT808/TModels/T0003.cs b/Newlife.JT808/Models/T0003.cs
similarity index 88%
rename from Newlife.JT808/TModels/T0003.cs
rename to Newlife.JT808/Models/T0003.cs
index 4b3d5f0..b5abd7e 100644
--- a/Newlife.JT808/TModels/T0003.cs
+++ b/Newlife.JT808/Models/T0003.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>终端注销请求</summary>
[MessageKind(MessageKinds.终端注销)]
diff --git a/Newlife.JT808/TModels/T0004.cs b/Newlife.JT808/Models/T0004.cs
similarity index 96%
rename from Newlife.JT808/TModels/T0004.cs
rename to Newlife.JT808/Models/T0004.cs
index a6d3c66..ad924d9 100644
--- a/Newlife.JT808/TModels/T0004.cs
+++ b/Newlife.JT808/Models/T0004.cs
@@ -1,7 +1,7 @@
using NewLife.JT808.Protocols;
using NewLife.JT808.Serialization;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>
/// 查询服务器时间请求
diff --git a/Newlife.JT808/TModels/T0005.cs b/Newlife.JT808/Models/T0005.cs
similarity index 98%
rename from Newlife.JT808/TModels/T0005.cs
rename to Newlife.JT808/Models/T0005.cs
index cf11326..51cb6f2 100644
--- a/Newlife.JT808/TModels/T0005.cs
+++ b/Newlife.JT808/Models/T0005.cs
@@ -1,7 +1,7 @@
using NewLife.JT808.Protocols;
using NewLife.Serialization;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>
/// 终端补传分包请求
diff --git a/Newlife.JT808/TModels/T0100.cs b/Newlife.JT808/Models/T0100.cs
similarity index 99%
rename from Newlife.JT808/TModels/T0100.cs
rename to Newlife.JT808/Models/T0100.cs
index 3e0a107..fd4c538 100644
--- a/Newlife.JT808/TModels/T0100.cs
+++ b/Newlife.JT808/Models/T0100.cs
@@ -2,7 +2,7 @@
using NewLife.JT808.UtilTool;
using NewLife.Serialization;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>终端注册</summary>
[MessageKind(MessageKinds.终端注册)]
diff --git a/Newlife.JT808/TModels/T0102.cs b/Newlife.JT808/Models/T0102.cs
similarity index 96%
rename from Newlife.JT808/TModels/T0102.cs
rename to Newlife.JT808/Models/T0102.cs
index e3ac805..512966d 100644
--- a/Newlife.JT808/TModels/T0102.cs
+++ b/Newlife.JT808/Models/T0102.cs
@@ -1,7 +1,7 @@
using NewLife.JT808.Protocols;
using NewLife.Serialization;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>终端鉴权</summary>
[MessageKind(MessageKinds.终端鉴权)]
diff --git a/Newlife.JT808/TModels/T0104.cs b/Newlife.JT808/Models/T0104.cs
similarity index 98%
rename from Newlife.JT808/TModels/T0104.cs
rename to Newlife.JT808/Models/T0104.cs
index 87f3ff3..6cfbbd5 100644
--- a/Newlife.JT808/TModels/T0104.cs
+++ b/Newlife.JT808/Models/T0104.cs
@@ -2,7 +2,7 @@
using NewLife.JT808.UtilTool;
using NewLife;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>查询终端参数应答</summary>
[MessageKind(MessageKinds.查询终端参数应答)]
diff --git a/Newlife.JT808/TModels/T0107.cs b/Newlife.JT808/Models/T0107.cs
similarity index 99%
rename from Newlife.JT808/TModels/T0107.cs
rename to Newlife.JT808/Models/T0107.cs
index 226b648..f9b8b42 100644
--- a/Newlife.JT808/TModels/T0107.cs
+++ b/Newlife.JT808/Models/T0107.cs
@@ -3,7 +3,7 @@ using NewLife.JT808.UtilTool;
using NewLife;
using NewLife.Serialization;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
[Flags]
public enum T0107_Types : UInt16
diff --git a/Newlife.JT808/TModels/T0108.cs b/Newlife.JT808/Models/T0108.cs
similarity index 95%
rename from Newlife.JT808/TModels/T0108.cs
rename to Newlife.JT808/Models/T0108.cs
index 65b7c76..7fc1812 100644
--- a/Newlife.JT808/TModels/T0108.cs
+++ b/Newlife.JT808/Models/T0108.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>终端升级结果通知</summary>
[MessageKind(MessageKinds.终端升级结果通知)]
diff --git a/Newlife.JT808/TModels/T0200.cs b/Newlife.JT808/Models/T0200.cs
similarity index 98%
rename from Newlife.JT808/TModels/T0200.cs
rename to Newlife.JT808/Models/T0200.cs
index dff7e28..ec0f05c 100644
--- a/Newlife.JT808/TModels/T0200.cs
+++ b/Newlife.JT808/Models/T0200.cs
@@ -4,7 +4,7 @@ using NewLife.JT808.UtilTool;
using NewLife;
using NewLife.Serialization;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>位置信息汇报</summary>
[MessageKind(MessageKinds.位置信息汇报)]
diff --git a/Newlife.JT808/TModels/T0201.cs b/Newlife.JT808/Models/T0201.cs
similarity index 92%
rename from Newlife.JT808/TModels/T0201.cs
rename to Newlife.JT808/Models/T0201.cs
index 2bc72be..143b169 100644
--- a/Newlife.JT808/TModels/T0201.cs
+++ b/Newlife.JT808/Models/T0201.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>位置信息查询应答</summary>
[MessageKind(MessageKinds.位置信息查询应答)]
diff --git a/Newlife.JT808/TModels/T0301.cs b/Newlife.JT808/Models/T0301.cs
similarity index 93%
rename from Newlife.JT808/TModels/T0301.cs
rename to Newlife.JT808/Models/T0301.cs
index 40500f9..f814890 100644
--- a/Newlife.JT808/TModels/T0301.cs
+++ b/Newlife.JT808/Models/T0301.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>
/// 事件报告
diff --git a/Newlife.JT808/TModels/T0302.cs b/Newlife.JT808/Models/T0302.cs
similarity index 95%
rename from Newlife.JT808/TModels/T0302.cs
rename to Newlife.JT808/Models/T0302.cs
index f902643..cf521cc 100644
--- a/Newlife.JT808/TModels/T0302.cs
+++ b/Newlife.JT808/Models/T0302.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>
/// 提问应答
diff --git a/Newlife.JT808/TModels/T0303.cs b/Newlife.JT808/Models/T0303.cs
similarity index 94%
rename from Newlife.JT808/TModels/T0303.cs
rename to Newlife.JT808/Models/T0303.cs
index b6212e0..9d9d9d4 100644
--- a/Newlife.JT808/TModels/T0303.cs
+++ b/Newlife.JT808/Models/T0303.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>
/// 信息点播/取消
diff --git a/Newlife.JT808/TModels/T0500.cs b/Newlife.JT808/Models/T0500.cs
similarity index 94%
rename from Newlife.JT808/TModels/T0500.cs
rename to Newlife.JT808/Models/T0500.cs
index 8801126..3d88221 100644
--- a/Newlife.JT808/TModels/T0500.cs
+++ b/Newlife.JT808/Models/T0500.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>车辆控制应答</summary>
[MessageKind(MessageKinds.车辆控制应答)]
diff --git a/Newlife.JT808/TModels/T0608.cs b/Newlife.JT808/Models/T0608.cs
similarity index 92%
rename from Newlife.JT808/TModels/T0608.cs
rename to Newlife.JT808/Models/T0608.cs
index eee27b2..59b5194 100644
--- a/Newlife.JT808/TModels/T0608.cs
+++ b/Newlife.JT808/Models/T0608.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary>查询区域或线路数据应答</summary>
[MessageKind(MessageKinds.查询区域或线路数据应答)]
diff --git a/Newlife.JT808/TModels/T0700.cs b/Newlife.JT808/Models/T0700.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0700.cs
rename to Newlife.JT808/Models/T0700.cs
diff --git a/Newlife.JT808/TModels/T0701.cs b/Newlife.JT808/Models/T0701.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0701.cs
rename to Newlife.JT808/Models/T0701.cs
diff --git a/Newlife.JT808/TModels/T0702.cs b/Newlife.JT808/Models/T0702.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0702.cs
rename to Newlife.JT808/Models/T0702.cs
diff --git a/Newlife.JT808/TModels/T0704.cs b/Newlife.JT808/Models/T0704.cs
similarity index 98%
rename from Newlife.JT808/TModels/T0704.cs
rename to Newlife.JT808/Models/T0704.cs
index 0f479a6..5646c49 100644
--- a/Newlife.JT808/TModels/T0704.cs
+++ b/Newlife.JT808/Models/T0704.cs
@@ -1,5 +1,5 @@
using NewLife.JT808.Protocols;
-using NewLife.JT808.TModels;
+using NewLife.JT808.Models;
using NewLife.JT808.UtilTool;
using NewLife.Serialization;
diff --git a/Newlife.JT808/TModels/T0705.cs b/Newlife.JT808/Models/T0705.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0705.cs
rename to Newlife.JT808/Models/T0705.cs
diff --git a/Newlife.JT808/TModels/T0800.cs b/Newlife.JT808/Models/T0800.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0800.cs
rename to Newlife.JT808/Models/T0800.cs
diff --git a/Newlife.JT808/TModels/T0801.cs b/Newlife.JT808/Models/T0801.cs
similarity index 97%
rename from Newlife.JT808/TModels/T0801.cs
rename to Newlife.JT808/Models/T0801.cs
index f34360d..92d5a29 100644
--- a/Newlife.JT808/TModels/T0801.cs
+++ b/Newlife.JT808/Models/T0801.cs
@@ -1,5 +1,5 @@
using NewLife.JT808.Protocols;
-using NewLife.JT808.TModels;
+using NewLife.JT808.Models;
namespace NewLife.JT808.Models
{
diff --git a/Newlife.JT808/TModels/T0802.cs b/Newlife.JT808/Models/T0802.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0802.cs
rename to Newlife.JT808/Models/T0802.cs
diff --git a/Newlife.JT808/TModels/T0805.cs b/Newlife.JT808/Models/T0805.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0805.cs
rename to Newlife.JT808/Models/T0805.cs
diff --git a/Newlife.JT808/TModels/T0900.cs b/Newlife.JT808/Models/T0900.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0900.cs
rename to Newlife.JT808/Models/T0900.cs
diff --git a/Newlife.JT808/TModels/T0901.cs b/Newlife.JT808/Models/T0901.cs
similarity index 100%
rename from Newlife.JT808/TModels/T0901.cs
rename to Newlife.JT808/Models/T0901.cs
diff --git a/Newlife.JT808/TModels/T1080.cs b/Newlife.JT808/Models/T1080.cs
similarity index 90%
rename from Newlife.JT808/TModels/T1080.cs
rename to Newlife.JT808/Models/T1080.cs
index 918e7de..e0c8e8c 100644
--- a/Newlife.JT808/TModels/T1080.cs
+++ b/Newlife.JT808/Models/T1080.cs
@@ -1,6 +1,6 @@
using NewLife.JT808.Protocols;
-namespace NewLife.JT808.TModels
+namespace NewLife.JT808.Models
{
/// <summary></summary>
[MessageKind(MessageKinds.T1080)]
diff --git a/Newlife.JT808/TModels/T6006.cs b/Newlife.JT808/Models/T6006.cs
similarity index 100%
rename from Newlife.JT808/TModels/T6006.cs
rename to Newlife.JT808/Models/T6006.cs
diff --git a/Newlife.JT808/TModels/T8001.cs b/Newlife.JT808/Models/T8001.cs
similarity index 94%
rename from Newlife.JT808/TModels/T8001.cs
rename to Newlife.JT808/Models/T8001.cs
index 5496abe..b9a5976 100644
--- a/Newlife.JT808/TModels/T8001.cs
+++ b/Newlife.JT808/Models/T8001.cs
@@ -1,5 +1,5 @@
using NewLife.JT808.Protocols;
-using NewLife.JT808.TModels;
+using NewLife.JT808.Models;
namespace NewLife.JT808.Models
{
diff --git a/Newlife.JT808/TModels/T8003.cs b/Newlife.JT808/Models/T8003.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8003.cs
rename to Newlife.JT808/Models/T8003.cs
diff --git a/Newlife.JT808/TModels/T8100.cs b/Newlife.JT808/Models/T8100.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8100.cs
rename to Newlife.JT808/Models/T8100.cs
diff --git a/Newlife.JT808/TModels/T8103.cs b/Newlife.JT808/Models/T8103.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8103.cs
rename to Newlife.JT808/Models/T8103.cs
diff --git a/Newlife.JT808/TModels/T8104.cs b/Newlife.JT808/Models/T8104.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8104.cs
rename to Newlife.JT808/Models/T8104.cs
diff --git a/Newlife.JT808/TModels/T8105.cs b/Newlife.JT808/Models/T8105.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8105.cs
rename to Newlife.JT808/Models/T8105.cs
diff --git a/Newlife.JT808/TModels/T8106.cs b/Newlife.JT808/Models/T8106.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8106.cs
rename to Newlife.JT808/Models/T8106.cs
diff --git a/Newlife.JT808/TModels/T8107.cs b/Newlife.JT808/Models/T8107.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8107.cs
rename to Newlife.JT808/Models/T8107.cs
diff --git a/Newlife.JT808/TModels/T8108.cs b/Newlife.JT808/Models/T8108.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8108.cs
rename to Newlife.JT808/Models/T8108.cs
diff --git a/Newlife.JT808/TModels/T8201.cs b/Newlife.JT808/Models/T8201.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8201.cs
rename to Newlife.JT808/Models/T8201.cs
diff --git a/Newlife.JT808/TModels/T8202.cs b/Newlife.JT808/Models/T8202.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8202.cs
rename to Newlife.JT808/Models/T8202.cs
diff --git a/Newlife.JT808/TModels/T8203.cs b/Newlife.JT808/Models/T8203.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8203.cs
rename to Newlife.JT808/Models/T8203.cs
diff --git a/Newlife.JT808/TModels/T8204.cs b/Newlife.JT808/Models/T8204.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8204.cs
rename to Newlife.JT808/Models/T8204.cs
diff --git a/Newlife.JT808/TModels/T8300.cs b/Newlife.JT808/Models/T8300.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8300.cs
rename to Newlife.JT808/Models/T8300.cs
diff --git a/Newlife.JT808/TModels/T8301.cs b/Newlife.JT808/Models/T8301.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8301.cs
rename to Newlife.JT808/Models/T8301.cs
diff --git a/Newlife.JT808/TModels/T8302.cs b/Newlife.JT808/Models/T8302.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8302.cs
rename to Newlife.JT808/Models/T8302.cs
diff --git a/Newlife.JT808/TModels/T8303.cs b/Newlife.JT808/Models/T8303.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8303.cs
rename to Newlife.JT808/Models/T8303.cs
diff --git a/Newlife.JT808/TModels/T8304.cs b/Newlife.JT808/Models/T8304.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8304.cs
rename to Newlife.JT808/Models/T8304.cs
diff --git a/Newlife.JT808/TModels/T8400.cs b/Newlife.JT808/Models/T8400.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8400.cs
rename to Newlife.JT808/Models/T8400.cs
diff --git a/Newlife.JT808/TModels/T8401.cs b/Newlife.JT808/Models/T8401.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8401.cs
rename to Newlife.JT808/Models/T8401.cs
diff --git a/Newlife.JT808/TModels/T8500.cs b/Newlife.JT808/Models/T8500.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8500.cs
rename to Newlife.JT808/Models/T8500.cs
diff --git a/Newlife.JT808/TModels/T8600.cs b/Newlife.JT808/Models/T8600.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8600.cs
rename to Newlife.JT808/Models/T8600.cs
diff --git a/Newlife.JT808/TModels/T8601.cs b/Newlife.JT808/Models/T8601.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8601.cs
rename to Newlife.JT808/Models/T8601.cs
diff --git a/Newlife.JT808/TModels/T8602.cs b/Newlife.JT808/Models/T8602.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8602.cs
rename to Newlife.JT808/Models/T8602.cs
diff --git a/Newlife.JT808/TModels/T8603.cs b/Newlife.JT808/Models/T8603.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8603.cs
rename to Newlife.JT808/Models/T8603.cs
diff --git a/Newlife.JT808/TModels/T8604.cs b/Newlife.JT808/Models/T8604.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8604.cs
rename to Newlife.JT808/Models/T8604.cs
diff --git a/Newlife.JT808/TModels/T8605.cs b/Newlife.JT808/Models/T8605.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8605.cs
rename to Newlife.JT808/Models/T8605.cs
diff --git a/Newlife.JT808/TModels/T8606.cs b/Newlife.JT808/Models/T8606.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8606.cs
rename to Newlife.JT808/Models/T8606.cs
diff --git a/Newlife.JT808/TModels/T8607.cs b/Newlife.JT808/Models/T8607.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8607.cs
rename to Newlife.JT808/Models/T8607.cs
diff --git a/Newlife.JT808/TModels/T8608.cs b/Newlife.JT808/Models/T8608.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8608.cs
rename to Newlife.JT808/Models/T8608.cs
diff --git a/Newlife.JT808/TModels/T8700.cs b/Newlife.JT808/Models/T8700.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8700.cs
rename to Newlife.JT808/Models/T8700.cs
diff --git a/Newlife.JT808/TModels/T8701.cs b/Newlife.JT808/Models/T8701.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8701.cs
rename to Newlife.JT808/Models/T8701.cs
diff --git a/Newlife.JT808/TModels/T8702.cs b/Newlife.JT808/Models/T8702.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8702.cs
rename to Newlife.JT808/Models/T8702.cs
diff --git a/Newlife.JT808/TModels/T8800.cs b/Newlife.JT808/Models/T8800.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8800.cs
rename to Newlife.JT808/Models/T8800.cs
diff --git a/Newlife.JT808/TModels/T8801.cs b/Newlife.JT808/Models/T8801.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8801.cs
rename to Newlife.JT808/Models/T8801.cs
diff --git a/Newlife.JT808/TModels/T8802.cs b/Newlife.JT808/Models/T8802.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8802.cs
rename to Newlife.JT808/Models/T8802.cs
diff --git a/Newlife.JT808/TModels/T8803.cs b/Newlife.JT808/Models/T8803.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8803.cs
rename to Newlife.JT808/Models/T8803.cs
diff --git a/Newlife.JT808/TModels/T8804.cs b/Newlife.JT808/Models/T8804.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8804.cs
rename to Newlife.JT808/Models/T8804.cs
diff --git a/Newlife.JT808/TModels/T8805.cs b/Newlife.JT808/Models/T8805.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8805.cs
rename to Newlife.JT808/Models/T8805.cs
diff --git a/Newlife.JT808/TModels/T8900.cs b/Newlife.JT808/Models/T8900.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8900.cs
rename to Newlife.JT808/Models/T8900.cs
diff --git a/Newlife.JT808/TModels/T8A00.cs b/Newlife.JT808/Models/T8A00.cs
similarity index 100%
rename from Newlife.JT808/TModels/T8A00.cs
rename to Newlife.JT808/Models/T8A00.cs
diff --git a/Newlife.JT808/Newlife.JT808.csproj b/Newlife.JT808/Newlife.JT808.csproj
index 2e0f745..1ba0ad2 100644
--- a/Newlife.JT808/Newlife.JT808.csproj
+++ b/Newlife.JT808/Newlife.JT808.csproj
@@ -1,20 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
+ <TargetFrameworks>netstandard2.1;netstandard2.0;net461;net45</TargetFrameworks>
+ <RootNamespace>NewLife.JT808</RootNamespace>
+ <AssemblyTitle>JT808协议</AssemblyTitle>
+ <Description>JT/T 808车辆定位协议完整实现,支持2011/2013/2019版本</Description>
+ <Company>新生命开发团队</Company>
+ <Copyright>©2002-2026 新生命开发团队</Copyright>
<VersionPrefix>1.0</VersionPrefix>
<VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<FileVersion>$(Version)</FileVersion>
<AssemblyVersion>$(VersionPrefix).*</AssemblyVersion>
<Deterministic>false</Deterministic>
- <!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
- <LangVersion>latest</LangVersion>
+ <OutputPath>..\Bin</OutputPath>
+ <GenerateDocumentationFile>True</GenerateDocumentationFile>
+ <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
+ <LangVersion>latest</LangVersion>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <PackageId>$(AssemblyName)</PackageId>
+ <Authors>$(Company)</Authors>
+ <PackageProjectUrl>https://newlifex.com</PackageProjectUrl>
+ <PackageIcon>leaf.png</PackageIcon>
+ <RepositoryUrl>https://github.com/NewLifeX/NewLife.JT808</RepositoryUrl>
+ <RepositoryType>git</RepositoryType>
+ <PackageTags>物联网;IoT;车联网;GPS;北斗;JT808;JT/T808;新生命团队;NewLife;$(AssemblyName)</PackageTags>
+ <PackageReleaseNotes>初始版本:JT/T 808-2013/2019 消息帧解析、65+ 标准消息体、双版本兼容、TLV/KLV、苏标扩展</PackageReleaseNotes>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+ <PublishRepositoryUrl>true</PublishRepositoryUrl>
+ <EmbedUntrackedSources>true</EmbedUntrackedSources>
+ <IncludeSymbols>true</IncludeSymbols>
+ <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+ <PackageReadmeFile>Readme.MD</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="10.2.2023.401" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.300">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="NewLife.Core" Version="10.2.2023.401" />
+ </ItemGroup>
+
+ <ItemGroup Condition="'$(TargetFramework)'=='netstandard2.1' Or '$(TargetFramework)'=='netstandard2.0'">
+ <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Content Include="..\Doc\leaf.png" Link="leaf.png" PackagePath="\" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\Readme.MD">
+ <Pack>True</Pack>
+ <PackagePath>\</PackagePath>
+ </None>
</ItemGroup>
</Project>
diff --git a/Newlife.JT808/Protocols/JT808Config.cs b/Newlife.JT808/Protocols/JT808Config.cs
new file mode 100644
index 0000000..50a2069
--- /dev/null
+++ b/Newlife.JT808/Protocols/JT808Config.cs
@@ -0,0 +1,42 @@
+using NewLife;
+
+namespace NewLife.JT808.Protocols;
+
+/// <summary>JT/T 808协议版本</summary>
+public enum JT808Version : Byte
+{
+ /// <summary>JT/T 808-2011</summary>
+ JT2011 = 0,
+
+ /// <summary>JT/T 808-2013</summary>
+ JT2013 = 0,
+
+ /// <summary>JT/T 808-2019</summary>
+ JT2019 = 1,
+}
+
+/// <summary>JT/T 808协议配置</summary>
+public class JT808Config
+{
+ /// <summary>协议版本</summary>
+ public JT808Version Version { get; set; } = JT808Version.JT2013;
+
+ /// <summary>协议版本号标识。上下级平台之间采用的标准协议版本编号,长度3字节,0x01020F表示v1.2.15</summary>
+ public UInt32 VersionFlag { get; set; } = 0x01020F;
+
+ /// <summary>加密密钥M1</summary>
+ public UInt32 M1 { get; set; }
+
+ /// <summary>加密密钥IA1</summary>
+ public UInt32 IA1 { get; set; }
+
+ /// <summary>加密密钥IC1</summary>
+ public UInt32 IC1 { get; set; }
+
+ /// <summary>跳过校验和验证。默认false,校验和不匹配时仅记录日志</summary>
+ public Boolean SkipCRCCode { get; set; }
+
+ /// <summary>克隆配置</summary>
+ /// <returns></returns>
+ public JT808Config Clone() => MemberwiseClone() as JT808Config;
+}
diff --git a/Newlife.JT808/Protocols/JTCodec.cs b/Newlife.JT808/Protocols/JTCodec.cs
new file mode 100644
index 0000000..93cffc6
--- /dev/null
+++ b/Newlife.JT808/Protocols/JTCodec.cs
@@ -0,0 +1,84 @@
+using NewLife.Data;
+
+namespace NewLife.JT808.Protocols;
+
+/// <summary>JT/T 808网络粘包编解码器</summary>
+/// <remarks>
+/// 处理TCP粘包/半包,根据0x7E帧边界分割数据流。
+/// 每会话一个实例,在TCP收到数据时将字节追加到缓冲区,然后逐帧解析。
+/// </remarks>
+public class JTCodec
+{
+ private Int32 _gid;
+ private MemoryStream _buffer = new();
+
+ /// <summary>获取下一个消息流水号</summary>
+ /// <returns></returns>
+ public UInt16 GetSequence() => (UInt16)Interlocked.Increment(ref _gid);
+
+ /// <summary>编码消息为数据包(分配流水号并转为Packet)</summary>
+ /// <param name="message">消息</param>
+ /// <returns>数据包</returns>
+ public virtual Packet Encode(JTMessage message)
+ {
+ if (message.Sequence == 0) message.Sequence = GetSequence();
+
+ return message.ToPacket();
+ }
+
+ /// <summary>解码数据包为消息列表(处理粘包)</summary>
+ /// <param name="pk">原始数据包</param>
+ /// <returns>解码后的消息列表</returns>
+ public virtual IList<JTMessage> Decode(Packet pk)
+ {
+ // 将新数据追加到缓冲区
+ pk.CopyTo(_buffer);
+
+ return ParseBuffer();
+ }
+
+ /// <summary>从缓冲区解析所有完整帧</summary>
+ /// <returns>消息列表</returns>
+ protected virtual IList<JTMessage> ParseBuffer()
+ {
+ var list = new List<JTMessage>();
+ if (_buffer.Length == 0) return list;
+
+ var data = new Packet(_buffer.ToArray());
+
+ while (data.Total > 0)
+ {
+ // 查找帧头0x7E
+ var startIdx = 0;
+ while (startIdx < data.Total && data[startIdx] != 0x7E) startIdx++;
+ if (startIdx >= data.Total) break;
+
+ // 获取完整帧长度
+ var frameLen = JTMessage.GetLength(data.Slice(startIdx));
+ if (frameLen <= 0 || startIdx + frameLen > data.Total) break;
+
+ // 提取完整帧
+ var frame = data.Slice(startIdx, frameLen);
+ var msg = new JTMessage();
+ if (msg.Read(frame))
+ list.Add(msg);
+
+ // 移动到下一帧
+ data = data.Slice(startIdx + frameLen);
+ }
+
+ // 保留未处理完的数据
+ _buffer.SetLength(0);
+ if (data.Total > 0)
+ data.CopyTo(_buffer);
+
+ return list;
+ }
+
+ /// <summary>重置解码器状态(连接断开时调用)</summary>
+ public virtual void Reset()
+ {
+ _buffer.SetLength(0);
+ }
+}
+
diff --git a/Newlife.JT808/Protocols/MessageFactory.cs b/Newlife.JT808/Protocols/MessageFactory.cs
index 9008b05..b719f35 100644
--- a/Newlife.JT808/Protocols/MessageFactory.cs
+++ b/Newlife.JT808/Protocols/MessageFactory.cs
@@ -57,6 +57,35 @@ namespace NewLife.JT808.Protocols
if (att != null) Register(att.Kind, type);
}
+ /// <summary>从指定程序集扫描并注册所有带有[MessageKind]特性的类型</summary>
+ /// <param name="assembly">要扫描的程序集</param>
+ public void Register(Assembly assembly)
+ {
+ foreach (var type in assembly.GetTypes())
+ {
+ var att = type.GetCustomAttribute<MessageKindAttribute>();
+ if (att != null) Register(att.Kind, type);
+ }
+ }
+
+ /// <summary>注册自定义消息类型映射(可覆盖已有映射)</summary>
+ /// <typeparam name="T">消息体类型</typeparam>
+ public void SetMap<T>()
+ {
+ var type = typeof(T);
+ var att = type.GetCustomAttribute<MessageKindAttribute>();
+ if (att != null) _map1[att.Kind] = type;
+ }
+
+ /// <summary>注册自定义消息类型映射(可覆盖已有映射)</summary>
+ /// <param name="kind">消息类型</param>
+ /// <param name="type">消息体类型</param>
+ public void SetMap(MessageKinds kind, Type type)
+ {
+ _map1[kind] = type;
+ _map2[type] = kind;
+ }
+
/// <summary>创建指定类型的模型对象</summary>
/// <param name="kind"></param>
/// <returns></returns>
diff --git a/Newlife.JT808/UtilTool/Helper.cs b/Newlife.JT808/UtilTool/Helper.cs
index 67d66e5..1e5651c 100644
--- a/Newlife.JT808/UtilTool/Helper.cs
+++ b/Newlife.JT808/UtilTool/Helper.cs
@@ -12,7 +12,9 @@ namespace NewLife.JT808.UtilTool
static Helper()
{
+#if !NETFRAMEWORK
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+#endif
Encoding = Encoding.GetEncoding("GBK");
}
diff --git a/Readme.MD b/Readme.MD
new file mode 100644
index 0000000..0ab116b
--- /dev/null
+++ b/Readme.MD
@@ -0,0 +1,225 @@
+# NewLife.JT808 - JT/T 808 车辆定位协议
+
+
+
+
+
+
+
+JT/T 808 车辆定位协议完整实现,支持 2011/2013/2019 三个版本,涵盖 65+ 标准消息体及苏标主动安全扩展。
+
+源码: https://github.com/NewLifeX/NewLife.JT808
+Nuget:[NewLife.JT808](https://www.nuget.org/packages/NewLife.JT808)
+文档:[需求文档](Doc/需求文档.md) / [架构设计](Doc/架构设计.md) / [功能清单](Doc/功能清单.md)
+
+
+## 主要特点
+
+- **协议完整**:实现 JT/T 808-2013/2019 消息帧解析,支持 0x7E 帧边界、0x7D 转义、异或校验、分包处理
+- **消息体丰富**:覆盖 65+ 标准消息体(0x0001~0x8A00),终端上行 + 平台下行全覆盖
+- **双版本兼容**:通过 `Version` 字段 + `[JT2019]` 特性实现 2013/2019 版本自动适配,无需手写分支
+- **双模式序列化**:简单消息体使用属性驱动反射序列化,复杂消息体实现 `IAccessor` 手动控制读写
+- **TLV/KLV 解析**:内置 Type-Length-Value 和 Kind-Length-Value 通用编解码,支持强类型字典转换
+- **苏标扩展**:支持 ADAS/DSM/TPMS/BSD 主动安全报警信息,外设状态/信息解析,报警附件协议
+- **BCD 编码**:BCD 字符串和时间自定义序列化特性,适配协议特有编码格式
+- **GBK 编码**:内置 GBK 中文字符串编解码,自动注册 `CodePagesEncodingProvider`
+- **多框架支持**:`net45 / net461 / netstandard2.0 / netstandard2.1`
+
+## 支持的协议版本
+
+| 版本 | 标识 | 说明 |
+|------|------|------|
+| JT/T 808-2011 | `Version=0` | 早期版本,部分消息体结构不同 |
+| JT/T 808-2013 | `Version=0` | 当前广泛使用的版本 |
+| JT/T 808-2019 | `Version=1` | 最新版本,增加新版字段和消息类型 |
+
+## 消息对照表
+
+| 序号 | 消息ID | 消息名称 | 方向 | 完成 |
+|:---:|:------:|---------|:----:|:----:|
+| 1 | 0x0001 | 终端通用应答 | 上行 | ✅ |
+| 2 | 0x8001 | 平台通用应答 | 下行 | ✅ |
+| 3 | 0x0002 | 终端心跳 | 上行 | ✅ |
+| 4 | 0x8003 | 补传分包请求 | 下行 | ✅ |
+| 5 | 0x0004 | 查询服务器时间请求 | 上行 | ✅ |
+| 6 | 0x0100 | 终端注册 | 上行 | ✅ |
+| 7 | 0x8100 | 终端注册应答 | 下行 | ✅ |
+| 8 | 0x0003 | 终端注销 | 上行 | ✅ |
+| 9 | 0x0102 | 终端鉴权 | 上行 | ✅ |
+| 10 | 0x8103 | 设置终端参数 | 下行 | ✅ |
+| 11 | 0x8104 | 查询终端参数 | 下行 | ✅ |
+| 12 | 0x0104 | 查询终端参数应答 | 上行 | ✅ |
+| 13 | 0x8105 | 终端控制 | 下行 | ✅ |
+| 14 | 0x8106 | 查询指定终端参数 | 下行 | ✅ |
+| 15 | 0x8107 | 查询终端属性 | 下行 | ✅ |
+| 16 | 0x0107 | 查询终端属性应答 | 上行 | ✅ |
+| 17 | 0x8108 | 下发终端升级包 | 下行 | ✅ |
+| 18 | 0x0108 | 终端升级结果通知 | 上行 | ✅ |
+| 19 | 0x0200 | 位置信息汇报 | 上行 | ✅ |
+| 20 | 0x8201 | 位置信息查询 | 下行 | ✅ |
+| 21 | 0x0201 | 位置信息查询应答 | 上行 | ✅ |
+| 22 | 0x8202 | 临时位置跟踪控制 | 下行 | ✅ |
+| 23 | 0x8203 | 人工确认报警消息 | 下行 | ✅ |
+| 24 | 0x8300 | 文本信息下发 | 下行 | ✅ |
+| 25 | 0x8301 | 事件设置 | 下行 | ✅ |
+| 26 | 0x0301 | 事件报告 | 上行 | ✅ |
+| 27 | 0x8302 | 提问下发 | 下行 | ✅ |
+| 28 | 0x0302 | 提问应答 | 上行 | ✅ |
+| 29 | 0x8303 | 信息点播菜单设置 | 下行 | ✅ |
+| 30 | 0x0303 | 信息点播/取消 | 上行 | ✅ |
+| 31 | 0x8304 | 信息服务 | 下行 | ✅ |
+| 32 | 0x8400 | 电话回拨 | 下行 | ✅ |
+| 33 | 0x8401 | 设置电话本 | 下行 | ✅ |
+| 34 | 0x8500 | 车辆控制 | 下行 | ✅ |
+| 35 | 0x0500 | 车辆控制应答 | 上行 | ✅ |
+| 36 | 0x8600 | 设置圆形区域 | 下行 | ✅ |
+| 37 | 0x8601 | 删除圆形区域 | 下行 | ✅ |
+| 38 | 0x8602 | 设置矩形区域 | 下行 | ✅ |
+| 39 | 0x8603 | 删除矩形区域 | 下行 | ✅ |
+| 40 | 0x8604 | 设置多边形区域 | 下行 | ✅ |
+| 41 | 0x8605 | 删除多边形区域 | 下行 | ✅ |
+| 42 | 0x8606 | 设置路线 | 下行 | ✅ |
+| 43 | 0x8607 | 删除路线 | 下行 | ✅ |
+| 44 | 0x8608 | 查询区域或线路数据 | 下行 | ✅ |
+| 45 | 0x0608 | 查询区域或线路数据应答 | 上行 | ✅ |
+| 46 | 0x8700 | 行驶记录仪数据采集命令 | 下行 | ✅ |
+| 47 | 0x0700 | 行驶记录仪数据上传 | 上行 | ✅ |
+| 48 | 0x8701 | 行驶记录仪参数下传命令 | 下行 | ✅ |
+| 49 | 0x0701 | 电子运单上报 | 上行 | ✅ |
+| 50 | 0x8702 | 上报驾驶员身份信息请求 | 下行 | ✅ |
+| 51 | 0x0702 | 驾驶员身份信息采集上报 | 上行 | ✅ |
+| 52 | 0x0704 | 定位数据批量上传 | 上行 | ✅ |
+| 53 | 0x0705 | CAN总线数据上传 | 上行 | ✅ |
+| 54 | 0x0800 | 多媒体事件信息上传 | 上行 | ✅ |
+| 55 | 0x0801 | 多媒体数据上传 | 上行 | ✅ |
+| 56 | 0x8800 | 多媒体数据上传应答 | 下行 | ✅ |
+| 57 | 0x8801 | 摄像头立即拍摄命令 | 下行 | ✅ |
+| 58 | 0x0805 | 摄像头立即拍摄命令应答 | 上行 | ✅ |
+| 59 | 0x8802 | 存储多媒体数据检索 | 下行 | ✅ |
+| 60 | 0x0802 | 存储多媒体数据检索应答 | 上行 | ✅ |
+| 61 | 0x8803 | 存储多媒体数据上传 | 下行 | ✅ |
+| 62 | 0x8804 | 录音开始命令 | 下行 | ✅ |
+| 63 | 0x8805 | 单条存储多媒体数据检索上传 | 下行 | ✅ |
+| 64 | 0x8900 | 数据下行透传 | 下行 | ✅ |
+| 65 | 0x0900 | 数据上行透传 | 上行 | ✅ |
+| 66 | 0x0901 | 数据压缩上报 | 上行 | ✅ |
+| 67 | 0x8A00 | 平台RSA公钥 | 下行 | ✅ |
+| 68 | 0x0005 | 终端补传分包请求 | 上行 | ✅ |
+| 69 | 0x8204 | 链路检测 | 下行 | ✅ |
+| 70 | 0x6006 | 文本信息下发响应 | 上行 | ✅ |
+| 71 | 0x1080 | 自定义消息 | — | ✅ |
+
+
+## 快速入门
+
+### 安装 NuGet 包
+
+```bash
+dotnet add package NewLife.JT808
+```
+
+### 解析终端上报消息
+
+```csharp
+using NewLife.JT808.Protocols;
+using NewLife.JT808.Models;
+
+// 初始化消息工厂(扫描程序集注册所有消息类型)
+var factory = MessageFactory.Instance;
+factory.Init();
+
+// 终端上报的原始二进制数据
+var raw = new Byte[] { 0x7E, 0x02, 0x00, /* ... */, 0x7E };
+
+// 解析为 JTMessage 帧
+var jtMessage = new JTMessage();
+var stream = new MemoryStream(raw);
+jtMessage.Read(stream, null);
+
+// 根据消息ID反序列化为具体业务模型
+var body = factory.Parse(jtMessage);
+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");
+}
+```
+
+### 构建下发消息
+
+```csharp
+// 构建平台通用应答
+var t8001 = new T8001
+{
+ Sequence = jtMessage.Sequence,
+ Kind = jtMessage.Kind,
+ Result = ResultKinds.成功
+};
+
+// 序列化为字节数组
+var ms = new MemoryStream();
+var writer = Helper.CreateWriter(ms, jtMessage);
+writer.TryWrite(null, ref t8001);
+
+// 封装为 JTMessage 帧
+var reply = new JTMessage
+{
+ Kind = MessageKinds.平台通用应答,
+ Mobile = jtMessage.Mobile,
+ Sequence = (UInt16)(jtMessage.Sequence + 1),
+ Payload = new Packet(ms.ToArray()),
+ Reply = false
+};
+
+// 输出完整帧字节
+var outMs = new MemoryStream();
+reply.Write(outMs, null);
+var frameBytes = outMs.ToArray();
+```
+
+### 手动序列化复杂消息体(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 = context as Binary ?? 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 = context as Binary ?? Helper.CreateWriter(stream);
+ writer.Write((UInt32)Alarm);
+ writer.Write((UInt32)Status);
+ writer.Write(Latitude);
+ writer.Write(Longitude);
+ // ... 更多字段
+ return true;
+ }
+}
+```
+
+
+## 新生命开发团队
+
+[](https://newlifex.com)
+
+新生命开发团队是国内领先的开源物联网与边缘计算团队,专注于 .NET 技术栈在物联网、工业自动化、车联网等领域的深度应用。
+
+- 官网:https://newlifex.com
+- 开源:https://github.com/NewLifeX
+- QQ群:1600800