- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
导读
本文以 ThingsBoard 开源仓库中的 Simple JSON 解码器示例 为骨架,完整讲解 TBEL(ThingsBoard Expression Language)上行数据转换解码器的编写方法:如何把集成收到的原始 JSON 负载解析为平台通用的设备数据格式,如何将字符串时间戳转换为 Unix 毫秒时间戳,以及解码器返回值必须满足的结构规范。读完本文,你将掌握 TBEL 解码器的入参约定、返回格式要求,并能够独立编写可复制的解码函数,把任意 JSON 格式的接入消息映射为设备名、设备类型与遥测数据。
什么是 TBEL 解码器
在 ThingsBoard 的集成(Integration)体系中,上行链路(Uplink)数据转换解码器负责将集成收到的消息解析并转换为平台通用的数据格式。它是集成与平台实体之间的翻译层:MQTT、HTTP、CoAP、SigFox、ChirpStack、The Things Stack 等各种接入协议的原始报文,都要经过解码器才能成为设备遥测数据。
TBEL(ThingsBoard Expression Language)是平台内置的脚本语言,其解码器函数签名固定为:
function Decoder(payload, metadata): object | object[]这个函数定义在仓库的解码器通用文档中,是 UI 内置帮助系统为 TBEL 解码器统一提供的签名。Simple JSON 示例正是这一签名的典型落地。
两个入参:payload 与 metadata
| 参数 | 类型 | 说明 |
|---|---|---|
payload | any(字节数组) | 集成产生的原始消息。集成可能按 JSON、TEXT、Binary(Base64) 三种内容类型产出负载,内容类型只是调试事件的存储提示,不影响解码函数本身的工作方式 |
metadata | {[key: string]: string} | 集成相关的元数据键值映射,可在集成详情中为每个集成配置额外的元数据字段 |
关于payload的类型细节,官方文档有明确说明:
- 大多数集成(如 SigFox、LORIOT、ChirpStack、The Things Stack)始终产出 JSON 负载,且会用有用的元数据(RSSI、SNR 等)包装二进制设备负载;
- 基于 HTTP 的集成和 CoAP 集成根据请求头确定负载的内容类型,同一个集成可能因请求不同而产出 JSON、TEXT 或 BINARY;
- MQTT 集成(MQTT 3.x 之前的发布消息没有 content-type)的负载始终是 BINARY 类型。
解码器内部可以使用decodeToString和decodeToJson工具函数将字节数组转换为字符串或 JSON 对象——这正是 Simple JSON 示例的核心动作。
逐行解读 Simple JSON 解码器
仓库中的示例解码函数代码如下:
// decode payload to JSON. See helper function below var json = decodeToJson(payload); // convert date to epoch in milliseconds var timestamp = Date.parse(json.ts); // Construct result object with time-series data var result = { deviceName: json.serialNumber, deviceType: "Thermostat", deviceLabel: "Kitchen Thermostat", telemetry: { ts: timestamp, values: { temperature: json.t, humidity: json.h, } } }; return result;这段代码只有 4 个逻辑步骤,却是 TBEL 解码器最常见、最典型的写作范式:
第 1 步:解码负载。decodeToJson(payload)将原始字节数组负载解析为 JSON 对象。解析成功后,json.serialNumber、json.ts、json.t、json.h即可按字段名直接访问。
第 2 步:转换时间戳。Date.parse(json.ts)把负载中的人类可读时间字符串解析为 Unix 毫秒级时间戳。原始负载中的"ts": "2021-11-21 14:27:39 UTC"经过转换变成1637504859000,这正是平台遥测数据所要求的时间表示。
第 3 步:构造结果对象。结果对象包含设备标识(deviceName、deviceType、deviceLabel)与遥测数据(telemetry)。注意telemetry采用了{ts, values}结构:ts承载该数据点的时间戳,values承载具体遥测键值对。
第 4 步:返回结果。return result;把结果交给平台后续处理。
一个值得注意的版本差异
在仓库中还保留着同一示例的 JavaScript 早期版本(位于 converter/examples/decoder/simple-json/decoder_fn.md),它的不同之处在于在脚本内部手写了两个辅助函数:
/** Helper function to decode raw payload bytes to string**/ function decodeToString(payload) { return String.fromCharCode.apply(String, payload); } /** Helper function to decode raw payload bytes to JSON object**/ function decodeToJson(payload) { return JSON.parse(decodeToString(payload)); }而在 TBEL 版本中,decodeToJson、decodeToString是语言内置的导入函数,无需自行实现。这一点可以从 TBEL 运行时的源码得到印证:在 TbUtils.java 中,decodeToString与decodeToJson通过parserConfig.addImport(...)注册为脚本可直接调用的内置函数(见 TbUtils.java),其重载实现同时支持字节列表入参与字符串入参两种形式:
public static Object decodeToJson(ExecutionContext ctx, List<Byte> bytesList) throws IOException public static Object decodeToJson(ExecutionContext ctx, String jsonStr) throws IOException(见 TbUtils.java)
也就是说,decodeToJson既能直接解析字符串,也能先把字节列表转成字符串再解析,与 Simple JSON 示例中的调用方式完全一致。
编辑器自动补全中的函数定义
前端为 TBEL 编辑器提供了完整的函数自动补全与语法高亮定义,保存在 tbel-utils.models.ts。其中decodeToJson的定义为:
decodeToJson: { meta: 'function', description: 'Parses a JSON string or converts a list of bytes to a string and parses it as JSON.', args: [{ name: 'data', description: 'The JSON string or list of bytes to parse into JSON object', type: 'string | list' }], return: { description: 'The parsed JSON object', type: 'object' } }同文件还定义了decodeToString、bytesToString、parseInt、parseLong、parseFloat、各类十六进制/字节解析函数、isBinary等几十个 TBEL 内置工具函数。这意味着在 UI 编辑器中输入这些函数名即可获得参数提示,是编写解码器时的一手参考资料。
输入负载与输出结果对照
Simple JSON 示例对应的输入负载保存在 payload.md:
{ "serialNumber": "SN-111", "ts": "2021-11-21 14:27:39 UTC", "t": 36.6, "h": 70 }解码后得到的输出保存在 output.md:
{ "deviceName": "SN-111", "deviceType": "Thermostat", "deviceLabel": "Kitchen Thermostat", "telemetry": { "ts": 1637504859000, "values": { "temperature": 36.6, "humidity": 70 } } }对照可见数据流非常清晰:
| 原始字段 | 解码器动作 | 输出字段 |
|---|---|---|
serialNumber: "SN-111" | 直接引用 | deviceName: "SN-111" |
| — | 硬编码常量 | deviceType: "Thermostat" |
| — | 硬编码常量 | deviceLabel: "Kitchen Thermostat" |
ts: "2021-11-21 14:27:39 UTC" | Date.parse()转为毫秒 | telemetry.ts: 1637504859000 |
t: 36.6 | 直接引用 | telemetry.values.temperature: 36.6 |
h: 70 | 直接引用 | telemetry.values.humidity: 70 |
这一示例在解码器通用文档的示例表格中被命名为"Simple JSON with date",其技术要点正是"Parse specific JSON format with string representation of the timestamp",即解析带字符串时间戳的特定 JSON 格式——这恰好概括了本文讲解的解码器模式。
解码器返回值的格式规范
Simple JSON 示例只是输出格式的一个子集。根据解码器通用文档,解码器必须返回满足以下要求的合法 JSON 文档:
必须满足(must):
- 必须包含
deviceName+deviceType,或者assetName+assetType这样的键值对。这些属性标识设备或资产(设备名与资产名在租户范围内唯一),平台会据此查找已存在的设备/资产;若未找到且集成开启了"允许创建设备或资产"设置,平台将自动创建新实体。DevEUI、MAC 地址或其他唯一标识符常被用作设备名。
可以包含(may):
attributes对象:表示分配给设备/资产的服务器端属性集合;telemetry对象或数组:表示设备/资产的时间序列数据;customerName属性:平台据此自动将设备分配给客户;若同名的客户不存在则创建。注意:该分配只发生在当前集成创建设备或资产的过程中,设备已存在时此参数会被忽略;groupName属性:平台据此自动将设备分配到实体组;若同名分组不存在则创建(默认在租户范围内创建,若同时带有customerName则在客户范围内创建)。同样只在创建实体时生效;deviceLabel或assetLabel属性:用于创建非唯一的用户友好标签,便于在仪表盘上替代设备名展示。也只在实体创建时生效。
四种典型的输出形态
仓库在 converter/tbel/examples/decoder/ 目录下存放了多种输出示例文件,分别演示了不同复杂度:
1. 最简输出(simple_json_output.md)——设备名 + 设备类型 + 属性 + 无时间戳的遥测:
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "attributes": { "serialNumber": "SN-111" }, "telemetry": { "temperature": 42, "humidity": 80 } }2. 带标签、客户与分组的输出(label_json_output.md):
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "deviceLabel": "Room A thermostat", "customerName": "Company Name", "groupName": "Thermostats", "attributes": { "model": "Model A", "serialNumber": "SN-111", "integrationName": "Test integration" }, "telemetry": { "temperature": 42, "humidity": 80 } }3. 带自定义时间戳的输出(simple_json_output_with_ts.md)——平台期望时间戳是 Unix 毫秒级,否则使用服务器时间:
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "attributes": { "serialNumber": "SN-111" }, "telemetry": { "ts": 1527863043000, "values": { "temperature": 42, "humidity": 80 } } }4. 对象数组输出(json_array_output.md)——数据转换输出也可以是包含多个设备/资产的对象数组,其中每个对象还可携带多个不同时间戳的时间序列数据点:
[ { "deviceName": "001B638446E7", "deviceType": "thermostat", "deviceLabel": "Room A thermostat", "attributes": { "model": "Model A" }, "telemetry": [ { "ts": 1527863043000, "values": { "battery": 3.99, "temperature": 27.05 } }, { "ts": 1527863044000, "values": { "battery": 3.98, "temperature": 27.06 } } ] }, { "assetName": "OF-123", "assetType": "office", "attributes": { "model": "Model A" }, "telemetry": { "ts": 1527863041000, "values": { "battery": 3.99, "temperature": 27.05 } } } ]这个数组示例同时展示了设备(deviceName/deviceType)与资产(assetName/assetType)两种实体类型可以在一次转换中混用。
更多解码器示例:从 JSON 走向 CSV、二进制与元数据
Simple JSON 不是 TBEL 解码器的唯一形态。在 decoder 示例目录 下,仓库还提供了多组覆盖不同内容类型与复杂度的对照示例,每组都包含输入(payload)、解码函数(decoder_fn)与预期输出(output),非常适合作为进阶练习:
| 示例名称 | 内容类型 | 技术要点 |
|---|---|---|
| Simple CSV | TEXT | 用decodeToString解析 CSV 文本 |
| Simple binary data | BINARY | 解析含设备序列号、电池电量、温度与饱和度的二进制负载 |
| JSON with multiple hex encoded values | JSON | 转换多个含十六进制value字段及时间戳的 JSON 对象 |
| Use metadata fields | JSON | 利用metadata字段确定设备类型、型号与客户名 |
| example1 | — | 综合基础示例 |
这些示例在解码器通用文档末尾的示例表格中有统一索引,每个示例都给出了可点击的 payload / Decoder / Output 帮助弹窗。对于二进制负载的解析,可重点参考 TBEL 内置的字节与十六进制工具函数(如parseBytesToInt、parseHexToLong、hexToBytes等),它们的完整参数说明同样收录在 tbel-utils.models.ts 中。
实战要点总结
- 解码入口固定:TBEL 解码器签名必须是
function Decoder(payload, metadata),返回object或object[]; - 先解码再取字段:面对字节数组负载,优先调用内置
decodeToJson(payload)获得 JSON 对象,再用字段名访问; - 时间戳必须毫秒级:若负载提供事件时间,需用
Date.parse()或parseDateToTimestampOrNow等函数转换为 Unix 毫秒时间戳;否则平台将使用服务器时间; - 设备标识是硬性要求:返回对象必须包含
deviceName/deviceType或assetName/assetType;deviceLabel提供非唯一的友好展示名; - 善用可选字段:
attributes、customerName、groupName、telemetry数组等字段可让一次解码同时完成属性写入、客户/分组分配与多数据点上报; - 多实体一次返回:当一条上行消息包含多个设备/资产时,返回对象数组即可,每个元素可携带各自的时间序列数据点。
理解并复用 Simple JSON 示例 之后,配合仓库中同目录下的 CSV、二进制、十六进制、元数据示例逐级进阶,即可应对绝大多数 IoT 接入协议的负载解析场景。
- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
相关推荐
ThingsBoard TBEL 解码器实战:simple-metadata 示例如何将 JSON 载荷与设备元数据组合为遥测数据
ThingsBoard TBEL 解码器实战:simple metadata 示例如何将 JSON 载荷与设备元数据组合为遥测数据 本篇文章以 ThingsBo
物联网后端数据可视化消息队列Grok Build 终端支持与故障排查:从 /doctor 诊断到 tmux、SSH、剪贴板与 RTL 实战指南
Grok Build 终端支持与故障排查:从 /doctor 诊断到 tmux、SSH、剪贴板与 RTL 实战指南 Grok Build 以全屏 TUI 形式运
物联网后端数据可视化消息队列ThingsBoard TBEL 解码器函数实战:以 simple-json 示例拆解 Uplink 数据转换
ThingsBoard TBEL 解码器函数实战:以 simple json 示例拆解 Uplink 数据转换 导读 本文围绕 ThingsBoard 集成框架
物联网后端数据可视化消息队列
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考