EBHelper
EBHelper 是面向 EB 代码开发的"配置即代码"工具集。无需手动构造 QueryEvent / LoraUpEvent 实例并逐条编写 pushEBData / CRC 规则,只需在一份 UserConfUPItem JSON 配置中描述设备布局,调用 EventInfoItem 即可完成所有工作。
为什么使用 EBHelper 而不是原始 EBSDK?
- 自动生成 Modbus、DL/T 645-2007、CJ/T 188 协议的查询帧。
- 自动计算 CRC 规则(Modbus 使用 CRC16,仪表协议使用 SUM)。
- 自动分配 APP 参数段中的周期、COV 阈值和动态参数槽位。
- 生成统一的上行帧结构(version / dataType / covStatus / status / battery / addr / appData),适用于所有设备类型。
本文档对照源码
source/EBSDK/EBCompiler/plugins/EBHelper.ts(version1.02.011)编写。
1. 支持的协议
protocol 字段取值(大小写敏感):
| 协议 | 适用场景 | 子设备地址长度 |
|---|---|---|
modbus | 标准 RTU Modbus | 1 字节(默认) |
DLT64507 | DL/T 645-2007 电表 | 强制 6 字节(addrSize<6 时自动覆盖为 6) |
CJ188 | CJ/T 188 水/气/热表 | 强制 7 字节(自动覆盖为 7) |
any | 任意自定义协议(手填 cmd / ack) | 沿用 addrSize 配置 |
modbusBitCov | 已弃用,新工程不要再用 | — |
协议强制覆盖在 EventInfoItem.protocolBuild 中执行:若 addrSize 小于协议最小值,将被静默覆盖。
2. 标准上行帧结构
每个 UserConfUPItem 配置生成的 txBuffer 结构如下:
| 字段 | 偏移 | 大小 | 说明 |
|---|---|---|---|
version | 0 | 1 B | 协议版本号,默认 0x83。 |
dataType | 1 | 1 B | 数据类型编码;若未设置,编译时按 0x31 起自动递增。 |
covStatus | indexCovStatus(默认 2) | 1 B | COV 状态位,由各 ValItem 按位或累加。 |
status | indexStatus(默认 3) | 1 B | 来自 EBModel.APP_STATUS[2] 的查询事件状态。 |
battery | indexBattery(默认 4) | 1 B | 来自 EBModel.DEVICE_STATUS[3] 的电池电量。 |
addr[] | indexAddr(默认 5) | addrSize B | 子设备地址;长度受协议覆盖(见 §1)。 |
appData[] | indexAddr + addrSize | 由 quInfo 累加 | 应用层数据;解析由物模型完成。 |
indexApp 自动等于 indexAddr + addrSize,不要手动设置。
3. UserConfUPItem:上行事件配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | String | event_<i> | 上行事件名(用于日志识别)。 |
port | Number | 22 | LoRaWAN 上行端口。 |
version | String / Number | 0x83 | 协议版本字节。 |
dataType | String / Number | 自增(0x31 起) | 数据类型编码。同一编译过程中每个 UserConfUPItem 依次递增。 |
indexCovStatus | Number | 2 | COV 状态字节偏移。 |
indexStatus | Number | 3 | 状态字节偏移。 |
indexBattery | Number | 4 | 电量字节偏移。 |
indexAddr | Number | 5 | 地址字段起始偏移。 |
addrSize | Number | 1(受协议覆盖) | 地址长度(字节)。 |
upPeriod | String | — | 周期字面量:"900s"、"15m"、"1h"、"1d"、"1y"。 |
upPeriodIndex | Number | — | APP 参数段 4 字节小端周期寄存器地址。-1 从 ConfigPeriod.periodIndex(起始 70)自动分配。 |
quInfo | Array | — | 查询事件配置列表,见 §4。 |
周期优先级(ConfigPeriod 逻辑)
- 若设置了
upPeriodIndex(含-1自动分配)→ 索引模式:周期存储在 APP 参数段,可通过 RPC 运行时调整。 - 否则若
upPeriod为"trigger"→ 使用PERIOD_DUMB(约 10 年):永不自动上行,完全由ActAfterCvt触发。 - 否则若设置了
upPeriod→ 由Utils.parseSeconds()解析(单位:smhdy)。 - 两者均未设置 → 自动分配 APP 槽位,值回退为
PERIOD_DUMB。
4. quInfo 条目(UserConfQueryItem)
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | String | <protocol>_<idx> | 查询事件名。 |
protocol | String | modbus | 见 §1。 |
preamble | Number | 5 | CJ188 / DLT64507 帧前导 0xFE 字节数。 |
isLast | Boolean | 自动 | 为 true 时,此查询完成后立即触发上行。当 upPeriodIndex 已使用且周期 ≥ PERIOD_MAX(1 年)时,最后一个 quInfo 条目自动设为 true。 |
period | String | — | 与 upPeriod 语义相同,但作用于此查询事件。 |
periodIndex | Number | — | 与 upPeriodIndex 语义相同,但作用于此查询事件。 |
code | String | — | Modbus 功能码(十六进制),默认 0x03;若为空,取自 cmd[1]。 |
addr | String | — | 子设备地址(十六进制字符串,如 "0x02")。 |
cmd | String | 自动 | 自定义查询帧(十六进制)。设置后将完全覆盖自动生成帧(但 addr 和 code 仍会覆写字节 0–1)。 |
ack | String | — | 自定义响应模板(十六进制)。提供后进入"freeValue"模式,不再自动计算 Modbus 寄存器布局。 |
payIndex | Number | 协议默认值 | ACK 帧数据起始偏移。Modbus:3,CJ188:0,DLT64507:0。 |
ackAddrIndex | Number | 协议默认值 | ACK 帧内地址偏移。Modbus:0,CJ188:2,DLT64507:1。 |
indexAPP / indexCMD / copySize | Number | — | APP 参数注入三元组,见 §5。 |
listTag | Array | — | 逐字节 ACK 验证规则,见 §4.2。 |
listVal | Array | 必填 | 数据提取规则,见 §6。 |
4.1 Modbus 自动生成指令帧
未设置 cmd 时,QuItemModBus 根据 addr、code 以及从 listVal 自动检测的寄存器范围(通过 QuFrameInfo)构建指令帧:
[addr(1B)] [code(1B)] [起始寄存器高位] [起始寄存器低位] [寄存器数量高位] [寄存器数量低位] [CRC低位] [CRC高位]QuFrameInfo 自动计算覆盖所有 listVal 条目的最小连续寄存器范围,无需手动计算寄存器数量。
4.2 listTag — 固定字节 ACK 验证
每个条目 { index, val } 断言 ack[index] === val。用于验证协议起始/结束字节或地址回显。
// 断言第 0 字节为 0x68,最后字节为 0x16(DL/T 645 帧标志)
listTag: [
{ index: 0, val: "0x68" },
{ index: -1, val: "0x16" } // 负索引 = 从末尾计数
]4.3 CJ188 / DLT64507 帧结构
两种协议均使用若干 0xFE 前导字节加协议帧的格式。preamble 字段(���认 5)设置前导字节数量。EBHelper 自动处理:
- 将前导字节数添加到
ConfigPreCopy的indexCMD偏移。 - 为协议起止标志(
0x68/0x16)设置addAckCheckRule。 - 在帧体上配置 SUM 校验和。
5. ConfigPreCopy:向指令帧注入 APP 参数
用于在编译时将子设备地址(或任意 APP 参数段值)动态填入下发查询帧。多设备模式正是通过此机制实现:将存储在 APP 中的不同地址在每次查询前复制到指令帧。
| 字段 | 说明 |
|---|---|
indexAPP | APP 参数段起始偏移。-1 从 ConfigPreCopy.appIndex(起始 150)自动分配,每次按 copySize 递增。 |
indexCMD | 目标偏移(cmd 内)。 |
copySize | 拷贝字节数。 |
示例——将存储在 APP[150] 的 1 字节 Modbus 地址注入 cmd[0]:
{
protocol: "modbus",
addr: "0x01",
indexAPP: 150,
indexCMD: 0,
copySize: 1,
listVal: [...]
}若 indexCMD 或 copySize 等于 Utils.INVALID_NUM,则整个预拷贝配置被禁用。
6. ValItem:数据提取与 COV
每个 listVal 条目:
| 字段 | 类型 | 说明 |
|---|---|---|
start / end | String | 闭区间。Modbus 下为寄存器地址,其他协议下为字节偏移。支持十六进制字符串(如 "0x0102")。 |
covType | String | COV 数据类型(§6.1)。省略或设为 "INVALID" 时禁用 COV——字节直接原样拷贝。 |
covAppIndex | Number | COV 阈值在 APP 参数段的偏移。-1 从 ValItem.covIndex(起始 110)自动分配,每次递增 4。 |
6.1 支持的 covType 值
| 类型 | 说明 |
|---|---|
Uint8 / Int8 | 8 位无符号/有符号整数 |
Uint16BE / Uint16LE / Int16BE / Int16LE | 16 位整数 |
Uint32BE / Uint32LE / Int32BE / Int32LE | 32 位整数 |
FloatBE / FloatLE | 32 位浮点数 |
FloatCDAB / IntCDAB / UintCDAB | CDAB 字节序 32 位值 |
BCD | BCD 编码值 |
HEX | 逐字节比较;任意字节变化即触发 COV |
INVALID | 禁用 COV;仅原样拷贝字节 |
6.2 COV 触发逻辑
INVALID(或省略)→[start, end]范围内的字节原样拷贝到txBuffer。HEX→ 与SENSOR_DATA中的上次快照逐字节比较;有差异时设置txBuffer[indexCovStatus]对应位,上行后更新快照。- 数值类型 → 调用
quEvent.setupCov(...),用 APP 中covAppIndex处存储的阈值进行绝对差值比较。
6.3 Modbus 寄存器范围映射
QuFrameInfo 从所有 listVal.start / end 值中计算最小覆盖范围。每个寄存器映射到 payIndex + (寄存器 − 起始寄存器) × 2 处的 2 字节 ACK 数据。功能码 0x01 / 0x02(位读取)将各位合并为 ceil(bitCount/8) 字节。
7. APP 参数段地址规划
EBHelper 使用三个不重叠的静态类变量自动分配 APP 范围,这些变量在同一编译过程的所有 EventInfoItem 实例间累加:
| 范围 | 用途 | 自动起始 | 步长 |
|---|---|---|---|
70 – 109 | 周期参数(每个 4 字节) | ConfigPeriod.periodIndex = 70 | +4 |
110 – 149 | COV 阈值缓存(每个 4 字节) | ValItem.covIndex = 110 | +4 |
150 – 199 | 动态参数源(ConfigPreCopy) | ConfigPreCopy.appIndex = 150 | +copySize |
不要将上述范围内的偏移硬编码用于其他用途。若显式设置了
upPeriodIndex、covAppIndex或indexAPP的值,EBHelper 将直接使用该值,不会为该槽位推进计数器。
8. Utils 常量
| 常量 | 值 | 含义 |
|---|---|---|
Utils.INVALID_NUM | -1000000000 | 数值哨兵——表示"未配置/自动分配"。 |
Utils.INVALID_STR | "INVALID" | 字符串哨兵。 |
Utils.PERIOD_DEFAULT | 900 | 15 分钟(秒)。 |
Utils.PERIOD_DUMB | 86400 × 3650 | 约 10 年;等效于"永不自动上行"。 |
Utils.PERIOD_TRIGGER | "trigger" | upPeriod 的字符串字面量;映射到 PERIOD_DUMB。 |
Utils.PERIOD_MAX | 86400 × 365 | 1 年;用于判断是否自动将最后一个 quInfo 条目的 isLast 设为 true。 |
9. 导出成员
export {
version, Utils,
UserConfQueryItem, UserConfUPItem, TypeVal, TypeProtocol, TypeQuItem, QuItemMap,
ConfigPreCopy, ConfigPeriod, TagItem, ValItem, EventConfig,
QuFrameInfo, QuItemBase, QuItemModBus, QuItemModBusBitCov, QuItemDLT64507, QuItemCJ188,
EventInfoItem
}实际使用中几乎只需要 UserConfUPItem + EventInfoItem:填写配置,构造实例,调用 upEventSetup(),再调用 eventInstall()。
10. 完整示例
10.1 单 Modbus 设备 + COV
import { EBModel } from "@EBSDK/EBCompiler/all_variable";
import { UserConfUPItem, EventInfoItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import { CheckbitEnum, getOtaConfig } from "@EBSDK/otaConfig";
import { buildOtaFile } from "@EBSDK/run";
const eventInfo: UserConfUPItem[] = [
{
name: "meter1",
dataType: "0x31",
upPeriodIndex: 70, // 周期存储在 APP[70..73],可通过 RPC 调整
quInfo: [
{
protocol: "modbus",
addr: "0x01",
code: "0x03",
periodIndex: 74, // 查询周期存储在 APP[74..77]
listVal: [
{ start: "0", end: "1", covType: "Uint32BE", covAppIndex: 110 },
{ start: "2", end: "3", covType: "Uint32BE", covAppIndex: 114 },
{ start: "4", end: "7" } // 原样拷贝,不启用 COV
]
}
]
}
];
let otaConfig = getOtaConfig({
SwVersion: 31, BaudRate: 9600, StopBits: 1, DataBits: 8,
Checkbit: CheckbitEnum.NONE, Battery: true, ConfirmDuty: 60,
BzType: 10101, BzVersion: 1
});
const MODBUS_TT = (ebModel: EBModel) => {
for (const cfg of eventInfo) {
const ev = new EventInfoItem(cfg);
ev.upEventSetup();
ev.eventInstall();
}
return JSON.stringify(ebModel, null, 2);
};
buildOtaFile(__filename, otaConfig, MODBUS_TT);10.2 同一编译过程中两路上行配置
const eventInfo: UserConfUPItem[] = [
{
name: "channel_A",
dataType: "0x31",
upPeriodIndex: 70,
quInfo: [
{
protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 74,
listVal: [{ start: "0", end: "3", covType: "Uint32BE", covAppIndex: 110 }]
}
]
},
{
name: "channel_B",
dataType: "0x32",
upPeriodIndex: 78, // channel_A 占用 70–77 后的下一个 4 字节槽位
quInfo: [
{
protocol: "modbus", addr: "0x02", code: "0x03", periodIndex: 82,
listVal: [{ start: "0", end: "1", covType: "Uint16BE", covAppIndex: 114 }]
}
]
}
];
// 静态计数器自动推进,无需手动管理偏移。10.3 DL/T 645-2007 电表
const eventInfo: UserConfUPItem[] = [
{
name: "elecMeter",
dataType: "0x41",
upPeriodIndex: 70,
quInfo: [
{
protocol: "DLT64507",
addr: "010203040506", // 6 字节表地址(十六进制)
preamble: 4, // 4 个 0xFE 前导字节
listVal: [
{ start: "0", end: "3", covType: "BCD" }
]
}
]
}
];
// addrSize 自动强制为 6;SUM 校验和与帧标志自动配置。11. 最佳实践
- 尽量减少查询拆分:单次 ACK 可容纳约 250 字节——将连续寄存器合并为一次查询,比多次分开查询更���省功耗。
- 仅在间隔过大时才拆分:只有无效/无用数据超过约 50 字节时,拆分才值得。
- 优先使用索引模式配置:
upPeriodIndex: -1、periodIndex: -1、covAppIndex: -1允许通过 RPC 运行时调整,无需重新编译。 - 不要在 70/110/150 范围内硬编码偏移,除非明确需要固定值并防止自动分配占用该槽位。
- 为每个上行配置显式设置
dataType,确保跨固件版本的物模型解析结果一致。