# protocol-sdk **Repository Path**: hslei/protocol-sdk ## Basic Information - **Project Name**: protocol-sdk - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-19 - **Last Updated**: 2026-08-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # leobit-protocol-sdk 通用协议解析 / 组帧 SDK:以 **YAML 协议定义** 驱动,完成 **字节帧 ↔ 结构化 Map** 的双向转换,消除代码中的偏移量 / 长度魔法值。 - **纯 Java 8**,无 Spring / MyBatis / Netty 依赖,后端服务与终端模拟器客户端均可使用 - **独立 SDK**:不继承任何父 POM、不依赖任何业务模块,其他模块通过 Maven 依赖它 - 零魔法值:帧结构完全由 YAML 描述,新增协议帧无需改动代码 ## 目录 - [模块结构](#模块结构) - [核心功能](#核心功能) - [快速开始](#快速开始) - [YAML 协议定义](#yaml-协议定义) - [字段类型](#字段类型) - [表达式语法](#表达式语法) - [错误码](#错误码) - [构建与测试](#构建与测试) ## 模块结构 | 包 | 职责 | 核心类 | |---|---|---| | `com.leobit.protocol.model` | 协议定义与结果模型 | `ProtocolDefinition`、`FrameDefinition`、`FieldDefinition`、`FieldType`、`ProtocolFrame` | | `com.leobit.protocol.engine` | 编解码引擎与表达式求值 | `GenericFrameEngine`、`ExpressionEvaluator` | | `com.leobit.protocol.loader` | YAML 协议定义加载 | `YamlProtocolLoader` | | `com.leobit.protocol.util` | 字节缓冲区工具 | `ByteBufferUtils` | | `com.leobit.protocol.exception` | 统一异常体系 | `ProtocolEngineException` | ### model —— 协议定义模型 - **`ProtocolDefinition`**:协议元信息(name / version / active)、外层帧头 `outerHeader`、消息体帧索引(按 type+code)、嵌套子帧索引(按 id)、SN/TYPE/CODE/LEN 字段名约定及 `lenIncludesHeader` 语义。 - **`FrameDefinition`**:单帧结构,`typeCode` + `codeCode` 唯一定位一帧(`frameKey()`),包含有序字段列表。 - **`FieldDefinition`**:字段定义,支持类型、长度、字节序、缩放、条件、默认值、嵌套引用、校验算法等完整描述。 - **`ProtocolFrame`**:解析结果对象(不可变),暴露 `sn / type / code / body`(body 为只读 Map,支持泛型取值 `get("field")`)。 ### engine —— 编解码引擎 - **`GenericFrameEngine`**:核心引擎,绑定一个 `ProtocolDefinition`,提供 `decode` / `encodeFrame` / `decodeContent` / `encodeContent` 等双向转换 API。 - **`ExpressionEvaluator`**:轻量级表达式求值器,支持字段引用 `${fieldName}`、算术、比较、逻辑运算,用于 `lengthExpr` / `conditionExpr`。 ### loader —— 协议定义加载 - **`YamlProtocolLoader`**:将 YAML 协议描述装配为 `ProtocolDefinition`,支持 classpath / 文件 / 流 / Reader 四种加载方式;`nestedFrameId` 与 `discriminatorMap` 可写帧名或数字 id(两阶段解析,自动解析为 id)。 ### util / exception - **`ByteBufferUtils`**:大小端、有符号 / 无符号定长整数读写。 - **`ProtocolEngineException`**:运行时异常,携带枚举 `ErrorCode`(见[错误码](#错误码)),所有异常通过统一错误码区分失败类别。 ## 核心功能 - **字节帧 ↔ Map 双向映射**:依据 YAML 定义自动解析/组帧,无需手写偏移量。 - **13 种字段类型**:UINT8~UINT64、INT8~INT64、TEXT、BYTES、BITMASK、LENGTH_REF、CHECKSUM、NESTED。 - **嵌套子帧 + 判别路由**:解决"同一字段的字节内容按判别值解析成不同结构"的场景(如 `appType=0x00` 是运动包、`0x80` 是设备包)——NESTED 字段支持 `nestedFrameId` 直连,或 `discriminatorField` + `discriminatorMap` 按判别值路由(支持多级嵌套),无需在代码中手写 if-else 偏移量逻辑。子帧没有 type/code,加载时分配自增 id 建立 `nestedFrames` 索引,运行期按 id O(1) 查找。 - **变长字段**:解决"字段长度依赖其他字段或剩余字节"的场景——`lengthExpr` 表达式驱动(含 `remaining` 特殊字面量),也可由 `lengthBytes` 定长。 - **条件字段**:解决"字段只在特定条件下存在"的场景——`conditionExpr` 为假时该字段跳过解析/编码。 - **物理量换算**:`scale` / `offset` 实现 raw ↔ 物理值自动换算(解码 `raw * scale + offset`,编码反向取整)。 - **校验和**:`SUM8` / `CRC16` 自动计算与校验,校验失败抛 `INVALID_FRAME`。 - **位提取**:`BITMASK` + `bitRange`(如 `"4-7"`)提取指定位段。 - **自动长度**:编码时自动计算 LEN 字段(含或不含帧头由 `lenIncludesHeader` 决定),内容超限抛 `LENGTH_EXCEEDED`。 - **嵌套字段重编码**:`encodeNestedField(type, code, fieldName, values, outerContext)` 按判别字段路由子帧,把结构化 NESTED 字段值重编码为字节(如报文日志 dataHex),路由失败返回 `null`。 - **大小端**:字段级字节序配置,默认大端。 ## 快速开始 ### 1. 引入依赖 ```xml com.leobit leobit-protocol-sdk 1.0.0 ``` > snakeyaml 为 `optional` 依赖:仅使用 YAML 加载时需要额外引入;Lombok 为编译期注解(`provided`),不会传递。 ### 2. 代码示例 ```java // 加载协议定义(classpath 下的 YAML) ProtocolDefinition protocol = new YamlProtocolLoader() .loadFromClasspath("protocols/zdmock-protocol.yaml"); // 创建引擎(绑定协议) GenericFrameEngine engine = new GenericFrameEngine(protocol); // ===== 解码:byte[] → ProtocolFrame ===== ProtocolFrame frame = engine.decode(recvBytes); int sn = frame.getSn(); Map body = frame.getBody(); // 只读 Map String signalLevel = frame.get("signalLevel"); // 泛型取值 // ===== 编码:Map → byte[] ===== Map values = new HashMap<>(); values.put("powerOn", 1); values.put("targetSpeed", 300); // 物理值,引擎自动换算 raw byte[] out = engine.encodeFrame(sn, 0x01, 0x20, values); ``` ## YAML 协议定义 ```yaml protocol: name: zdmock version: v1 active: true snFieldName: sn typeFieldName: type codeFieldName: code lenFieldName: len lenIncludesHeader: true # LEN 是否包含帧头字节数 outerHeader: # 外层帧头(定长字段) frameName: outer.header fields: - {fieldName: sn, fieldType: UINT8} - {fieldName: type, fieldType: UINT8} - {fieldName: code, fieldType: UINT8} - {fieldName: len, fieldType: UINT16, byteOrder: LITTLE} frames: # 消息体帧(按 type/code 索引) - frameName: ctrl.param.set typeCode: 0x01 codeCode: 0x20 direction: C2S fields: - {fieldName: powerOn, fieldType: UINT8, defaultValue: "1"} - {fieldName: targetSpeed, fieldType: INT16, scale: 0.1, signed: true} - {fieldName: data, fieldType: NESTED, discriminatorField: appType, discriminatorMap: "0x00=appdata.motion,0x80=appdata.equip"} nestedFrames: # 嵌套子帧(按 id 索引) - frameName: appdata.equip fields: - {fieldName: payload, fieldType: NESTED, discriminatorMap: "1=equip.position,2=equip.fuel"} ``` ### 字段常用属性 | 属性 | 说明 | 示例 | |---|---|---| | `fieldName` | 字段名(解码后的 Map 键) | `targetSpeed` | | `fieldType` | 字段类型(见[字段类型](#字段类型)) | `UINT16` | | `lengthBytes` | 定长字段字节数 | `2` | | `lengthExpr` | 变长字段长度表达式 | `"${len}-5"`、`remaining` | | `byteOrder` | `BIG` / `LITTLE`(默认 BIG) | `LITTLE` | | `signed` | 有符号(数值类型,默认 false) | `true` | | `scale` / `offset` | 物理量换算:物理值 = raw × scale + offset | `scale: 0.1` | | `defaultValue` | 编码时缺省值 | `"1"`、`"0x10"` | | `conditionExpr` | 条件字段,为假时跳过 | `"${mode} == 2"` | | `nestedFrameId` | NESTED 字段直连子帧(数字 id 或帧名) | `appdata.motion` | | `discriminatorField` / `discriminatorMap` | 按判别值路由子帧 | 见上例 | | `checksumAlgorithm` | `NONE` / `SUM8` / `CRC16`(默认 NONE) | `CRC16` | | `bitRange` | BITMASK 位段,如 `4-7` | `"4-7"` | | `sortOrder` | 字段处理顺序 | `10` | ## 字段类型 | 类型 | 说明 | |---|---| | `UINT8` ~ `UINT64` | 无符号整数(1~8 字节) | | `INT8` ~ `INT64` | 有符号整数(配合 `signed: true`) | | `TEXT` | 文本,按长度读取后 UTF-8 解码 | | `BYTES` | 原始字节数组 | | `BITMASK` | 位掩码,配合 `bitRange` 提取位段 | | `LENGTH_REF` | 长度引用字段(供其他字段 `lengthExpr` 引用) | | `CHECKSUM` | 校验和 / CRC,自动计算与校验 | | `NESTED` | 嵌套子协议帧(支持判别路由与多级嵌套) | ## 表达式语法 表达式求值器(`ExpressionEvaluator`)是**变长字段与条件字段的引擎**:YAML 中声明的 `lengthExpr` / `conditionExpr` 在编解码时由它实时求值,使帧结构定义完全数据化——协议调整只改 YAML、不改代码。典型场景: - 帧体长度 = LEN 字段 − 帧头 5 字节:`lengthExpr: "${len} - 5"` - 字段长度 = 剩余全部字节:`lengthExpr: remaining` - 仅当类型为运动包时解析该字段:`conditionExpr: "${appType} == 0x00"` 支持的语法(求值失败抛 `EXPRESSION_ERROR`): - **字段引用**:`${fieldName}`(引用同帧已解析字段或外层帧头字段) - **数字常量**:十进制、十六进制(`0x...`) - **特殊字面量**:`remaining`(剩余字节数,用于 `lengthExpr`) - **算术**:`+ - * / %` - **比较**:`== != < > <= >=` - **逻辑**:`&& || !` - **括号**:`( )` 示例:`"${len} - 5"`、`"${mode} == 2 && ${count} > 0"`、`"remaining - 4"`。 ## 错误码 `ProtocolEngineException.ErrorCode`: | 错误码 | 含义 | |---|---| | `CONFIG_ERROR` | 协议定义配置错误(YAML 缺失、未知子帧名等) | | `INVALID_FRAME` | 帧数据非法(长度不足、校验和不匹配等) | | `UNKNOWN_FRAME` | 未找到 type+code 对应的帧定义 | | `INVALID_FIELD` | 字段值非法 | | `LENGTH_EXCEEDED` | 编码内容超出 LEN 上限(65535 - 帧头) | | `EXPRESSION_ERROR` | 表达式语法或求值错误 | | `UNSUPPORTED_TYPE` | 不支持的字段类型 | ## 构建与测试 ```bash mvn clean test # 需要 JDK 1.8+(编译目标锁定 Java 8,字节码 major version 52) mvn install # 安装到本地仓库,供其他模块依赖 ``` - 编译目标在 `maven-compiler-plugin` 中**显式写死 source/target 1.8**,不受全局 Maven settings 的 profile 属性覆盖影响 - 单元测试:26 个用例覆盖表达式求值、YAML 加载、帧编解码(含嵌套路由、校验和、条件/变长字段) ## 依赖关系 ``` ┌─────────────────────┐ │ hbotsdk / hsl-iot / netty server·simulator ... (消费方) └──────────┬──────────┘ │ 依赖 ┌──────────▼──────────┐ │ leobit-protocol-sdk ← 本 SDK(独立、自包含) └─────────────────────┘ ``` SDK 对外部依赖为零传递:snakeyaml(`optional`,仅 YAML 加载需要)、Lombok(`provided`,编译期)、JUnit(`test`)。其他模块按需引入后即可使用,SDK 自身的演进不影响任何消费方。