- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
导读
在 ThingsBoard 的集成(Integration)体系中,上行(Uplink)数据转换器(Converter)负责把来自各类设备接入协议(MQTT、HTTP、ChirpStack/LoRaWAN 等)的原始报文,转换为平台统一识别的实体与遥测数据。本文以仓库中 TBEL 解码器示例 simple-json 的converter_output.md文档为核心,完整拆解 Converter Output 的 JSON 结构、与 Decoder Output 的区别、各字段的语义与覆盖机制,并结合仓库源码(decodeToJson工具函数、AbstractUplinkDataConverter解析逻辑)说明该输出如何被平台消费。读完本文,你将能准确编写和调试 TBEL 上行解码函数,理解"解码函数返回结果"与"转换器最终输出"之间的关系。
一、示例全景:一次 LoRaWAN 上行报文的数据流转
Simple JSON 示例演示了一条来自 LoRaWAN(ChirpStack)网关的上行消息,从原始 JSON payload 到 Converter Output 的完整转换过程。仓库中该示例由四个配套文档组成,构成一条完整的可复现链路:
| 文件 | 作用 |
|---|---|
| payload.md | 解码函数的输入报文(JSON 格式) |
| decoder_fn.md | 编写的 TBEL 解码函数 |
| decoder_output.md | 解码函数直接返回的结果 |
| converter_output.md(本文主体) | 转换器最终提交给平台的完整输出 |
转换器输出(Converter Output)是把预配置的实体信息与解码函数动态产出的数据融合后的最终 JSON:解码函数可以覆盖预配置的键值,从而保证"静态配置与动态数据无缝集成"(此语义在 decoder_fn_v2.md 中有明确说明)。
二、Converter Output 完整结构(simple-json 示例)
仓库文档 converter_output.md 给出的最终输出如下:
{ "entityType": "DEVICE", "name": "Device 1000000000000001", "profile": "default", "telemetry": { "ts": 1684478801936, "values": { "battery": 95, "temperature": 36.6, "saturation": 99, "rssi": -21, "data": "01ed03335f0e4c63", "snr": 10, "fСnt": 2 } }, "attributes": { "sn": 32310067, "fPort": 85, "dr": "SF9 BW125 4/5", "frequency": 867500000, "eui": 1000000000000001 } }从结构上可将其划分为三层:
- 实体定位层:
entityType、name、profile——决定数据落到哪个设备/资产上; - 遥测层(telemetry):带时间戳
ts的时序数据values; - 属性层(attributes):设备/资产的服务端属性(server-side attributes)。
2.1 实体定位字段:entityType / name / profile
| 字段 | 示例值 | 语义 |
|---|---|---|
entityType | DEVICE | 实体类型,取值必须为Asset或Device,决定平台数据模型中的分类(见 decoder_fn_v2.md 中 type 属性的说明) |
name | Device 1000000000000001 | 实体名称,在租户范围内唯一标识设备/资产。平台用其定位已存在的实体;若不存在且集成允许创建实体,则新建(常使用 eui、MAC 等硬件唯一值,此例中即"Device + eui"拼接而来) |
profile | default | 实体关联的设备/资产配置档案(Profile)。若在预配置和解码函数中都未设置,则自动应用默认值default |
name、profile均为**可覆盖(overridable)**属性:转换器的初始配置定义了默认键值,解码函数可以写入新值覆盖之。
2.2 遥测层:telemetry
"telemetry": { "ts": 1684478801936, "values": { "battery": 95, "temperature": 36.6, "saturation": 99, ... } }ts为毫秒级 Unix 时间戳,本例1684478801936来自集成元数据metadata.ts(见下文第四节的 metadata 表);values为时序数据键值对。对比解码函数的直接输出可以发现:转换器输出中的遥测键比解码函数产出多出了rssi、data、snr、fCnt等网关级字段,这些键由转换器预配置或由集成附加信息补充而来——这正是"预配置键 + 函数结果融合"机制的体现。
2.3 属性层:attributes
"attributes": { "sn": 32310067, "fPort": 85, "dr": "SF9 BW125 4/5", "frequency": 867500000, "eui": 1000000000000001 }属性既包含解码函数产出的业务属性(sn,即设备序列号),也包含 LoRaWAN 网关上报的射频参数(fPort端口、dr数据速率、frequency频率、eui设备标识)。这些值大多能在集成元数据中找到原始来源(见第四节)。
三、完整链路还原:payload → 解码函数 → Decoder Output → Converter Output
该示例的输入、函数与中间输出同样保存在仓库中,还原整条链路有助于理解 Converter Output 中每个字段的来源。
3.1 输入 payload
payload.md 定义的原始报文:
{ "sn": 32310067, "battery": 95, "temperature": 36.6, "saturation": 99 }这是解码函数的第一个参数payload。按 decoder_fn_v2.md 的说明,payload 默认以二进制(Base64)编码,但也可能直接携带已解码的 JSON 数据,本例即属于后者。
3.2 TBEL 解码函数
decoder_fn.md 给出了核心解码逻辑:
function decodePayload(input) { var result = { attributes: {}, telemetry: {}}; var data = decodeToJson(input); var timestamp = metadata.ts; result.attributes.sn = data.sn; var values = {}; values.battery = data.battery; values.temperature = data.temperature; values.saturation = data.saturation; result.telemetry = { ts: timestamp, values: values }; return result; } var result = decodePayload(payload); return result;关键点:
decodeToJson(input)是 TBEL 运行时提供的工具函数。其底层实现位于 TbUtils.java,提供了decodeToJson(ExecutionContext, List<Byte>)与decodeToJson(ExecutionContext, String)两个重载,分别处理二进制字节数组与 JSON 字符串输入;metadata.ts直接读取集成附带的元数据时间戳(函数签名中metadata为{[key: string]: object}键值数据,见 decoder_fn_v2.md);- 函数返回值构造成
{ attributes, telemetry }结构,sn进入属性、三个传感器读数进入遥测。
3.3 Decoder Output:解码函数的直接结果
decoder_output.md 给出的中间结果:
{ "attributes": { "sn": 32310067 }, "telemetry": { "ts": 1684478801936, "values": { "battery": 95, "temperature": 36.6, "saturation": 99 } } }Decoder Output 是解码函数的直接返回,不附加任何额外配置或处理,必须是合法的 JSON 对象,且须满足(依据 decoder_fn_v2.md):
- 必须包含
attributes对象:至少含一个键值对,不得为空; - 必须包含
telemetry对象或数组:至少含一条数据点; - 可选的
name、type、profile、customer、group、label等属性可由解码函数覆盖返回(见第五节扩展示例)。
3.4 两者对比:Converter Output = 预配置 + Decoder Output 融合
将 Decoder Output 与 Converter Output 对比即可看出融合逻辑:
| 部分 | Decoder Output | Converter Output | 差异来源 |
|---|---|---|---|
entityType/name/profile | 无 | DEVICE/Device 1000000000000001/default | 转换器预配置(可被函数覆盖) |
attributes | 仅sn | sn+fPort/dr/frequency/eui | 集成元数据/预配置注入 |
telemetry.values | battery/temperature/saturation | 上述三项 +rssi/data/snr/fCnt | 网关附加信息注入 |
这与文档中对两种输出的定义完全一致:Converter Output 合并预配置默认键值与解码函数结果,且函数可覆盖预配置键;Decoder Output 则是函数裸输出。
四、元数据(metadata)在转换中的角色
metadata是解码函数的第二个参数,携带集成消息的键值信息,可在集成详情中追加自定义配置。simple-json 示例配套的 metadata.md 给出了典型的 LoRaWAN 元数据表:
| Key | Value |
|---|---|
| integrationName | Test LORIOT |
| includeGatewayInfo | false |
| rssi | -21 |
| seqno | 3040 |
| fPort | 85 |
| data | 01ed03335f0e4c63 |
| toa | 206 |
| ack | false |
| battery | 94 |
| dr | SF9 BW125 4/5 |
| frequency | 867500000 |
| offline | false |
| snr | 10 |
| eui | 1000000000000001 |
| cmd | rx |
| fCnt | 2 |
| ts | 1684478801936 |
对照第三节的 Converter Output 可以清晰追溯字段来源:ts来自metadata.ts(函数内var timestamp = metadata.ts显式引用);fPort、dr、frequency、eui、rssi、snr、fCnt、data等遥测/属性键均能在元数据中找到原始值。这说明解码函数并非字段唯一来源——集成网关的元数据同样会以预配置映射的形式进入最终输出。
五、扩展 Converter Output:label / customer / group 可选字段
simple-json 是一个聚焦实体定位与数据分流的"简洁示例"。仓库中 extended_converter_output.md 展示了带更多可选属性的扩展输出:
{ "entityType": "DEVICE", "name": "Device 1000000000000001", "profile": "default", "label": "Device name", "customer": "MyCustomer", "group": "SensorsGroup", "telemetry": [{ "ts": 1742770246830, "values": { "temperature": 50 } }, { "fCnt": 4, "rssi": -35 }], "attributes": { "fPort": 85, "tenantName": "ChirpStack", "applicationName": "Chirpstack application", "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", "eui": 1000000000000001, "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75" } }各可选字段的语义(依据 decoder_fn_v2.md):
customer:将设备/资产自动归属到指定客户,客户不存在则创建;仅在当前集成创建实体时生效,实体已存在则忽略;group:将实体自动加入指定实体分组,分组不存在则创建;默认在租户范围创建,若同时提供了customer则创建在客户下;同样仅在建实体时生效;label:非唯一、面向展示的友好名称,可用于仪表盘展示;仅建实体时生效。
此外注意telemetry既可以是单对象(如 simple-json 示例的{"ts": ..., "values": {...}}),也可以是数组(如上例的两个数据点),平台对两种形态均支持。
六、源码视角:Converter Output 如何被平台消费
Converter Output 的解析与校验逻辑集中在 AbstractUplinkDataConverter.java,从源码可以确认以下实现事实:
- 实体类型判定(L269-L291):输出必须携带
deviceName或assetName(对应本文示例的name+entityType),二者不能同时出现,否则抛出JsonParseException——这是输出的硬性校验; - 遥测解析(L181-L187):输出 JSON 中的
telemetry字段经parseTelemetry→JsonConverter.convertToTelemetryProto转为平台内部的PostTelemetryMsg; - 属性解析(L188-L194):
attributes字段经parseAttributesUpdate→JsonConverter.convertToAttributesProto转为PostAttributeMsg; - 按需更新过滤(L208-L250):若转换器配置了"仅值变化才上报"(update-only)键,遥测与属性还会经过
filterKeyValueAndUpdateMap去重过滤,避免重复值反复入库。
也就是说,本文示例中的entityType/name/profile + telemetry + attributes最终会被拆解为:实体定位信息(用于查找或创建设备)、PostTelemetryMsg(时序数据)与PostAttributeMsg(属性数据),分别进入平台的消息队列与存储链路。
七、编写 Converter 输出的实操建议
结合本示例与源码校验逻辑,编写上行转换器时建议遵循以下要点:
- 保证 Decoder Output 合法:返回对象必须包含非空的
attributes与非空的telemetry(对象或数组),否则平台无法消费; - 善用 name 的实体定位语义:使用
eui、MAC 等硬件唯一值拼装name,保证同一设备重复上报时能被正确定位(本示例的Device 1000000000000001即源于 eui); - 明确 entityType 二选一:
DEVICE/ASSET与name必须唯一对应,不能同时出现deviceName与assetName; - 区分两类字段来源:业务传感器数据由解码函数从 payload 提取(如
battery/temperature/saturation),网关射频与集成信息(如fPort/rssi/snr/eui)可从metadata读取或依赖预配置注入; - 可选字段按需使用:仅当需要自动归属客户、加入分组或展示友好名称时,才使用
customer、group、label; - 调试对照三份文档:payload → decoder_output → converter_output 三份文件一一对应,任何一步字段缺失都可逐级排查(
decodeToJson工具函数见 TbUtils.java)。
结语
Converter Output 是 ThingsBoard 集成体系"最后一公里"的关键数据结构。以 simple-json 示例为镜,我们完整还原了从原始 JSON payload、TBEL 解码函数到 Decoder Output、再到最终 Converter Output 的字段流转,并借助TbUtils.decodeToJson与AbstractUplinkDataConverter源码确认了实体定位校验、遥测/属性解析等平台侧消费逻辑。掌握这一结构,你就能为任何接入协议编写可靠、可复用的 TBEL 上行解码器。
- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
相关推荐
ThingsBoard 数据转换器 Decoder 输出格式详解:simple-json 示例与源码级剖析
ThingsBoard 数据转换器 Decoder 输出格式详解:simple json 示例与源码级剖析 本文以 ThingsBoard 开源 IoT 平台内
物联网后端数据可视化消息队列Grok Build 终端支持与故障排查:从 /doctor 诊断到 tmux、SSH、剪贴板与 RTL 实战指南
Grok Build 终端支持与故障排查:从 /doctor 诊断到 tmux、SSH、剪贴板与 RTL 实战指南 Grok Build 以全屏 TUI 形式运
物联网后端数据可视化消息队列ThingsBoard 上行数据解码实战:simple-binary 二进制报文解码与输出示例深度解析
ThingsBoard 上行数据解码实战:simple binary 二进制报文解码与输出示例深度解析 本文基于 ThingsBoard 官方帮助文档中 sim
物联网后端数据可视化消息队列
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考