☰
ThingsBoard TBEL 上行数据转换器 Converter Output 详解:从 Simple JSON 示例看解码输出的完整结构
2026/10/2 17:29:31 网站建设 项目流程
  • 物联网
  • 后端
  • 数据可视化
  • 消息队列

【免费下载链接】thingsboard

All-in-one IoT Platform - Device management, data collection, processing and visualization.

项目地址:https://gitcode.com/GitHub_Trending/th/thingsboard
点击查看免费下载

导读

在 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 } }

从结构上可将其划分为三层:

  1. 实体定位层:entityType、name、profile——决定数据落到哪个设备/资产上;
  2. 遥测层(telemetry):带时间戳ts的时序数据values;
  3. 属性层(attributes):设备/资产的服务端属性(server-side attributes)。

2.1 实体定位字段:entityType / name / profile

字段示例值语义
entityTypeDEVICE实体类型,取值必须为Asset或Device,决定平台数据模型中的分类(见 decoder_fn_v2.md 中 type 属性的说明)
nameDevice 1000000000000001实体名称,在租户范围内唯一标识设备/资产。平台用其定位已存在的实体;若不存在且集成允许创建实体,则新建(常使用 eui、MAC 等硬件唯一值,此例中即"Device + eui"拼接而来)
profiledefault实体关联的设备/资产配置档案(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 OutputConverter Output差异来源
entityType/name/profile无DEVICE/Device 1000000000000001/default转换器预配置(可被函数覆盖)
attributes仅snsn+fPort/dr/frequency/eui集成元数据/预配置注入
telemetry.valuesbattery/temperature/saturation上述三项 +rssi/data/snr/fCnt网关附加信息注入

这与文档中对两种输出的定义完全一致:Converter Output 合并预配置默认键值与解码函数结果,且函数可覆盖预配置键;Decoder Output 则是函数裸输出。


四、元数据(metadata)在转换中的角色

metadata是解码函数的第二个参数,携带集成消息的键值信息,可在集成详情中追加自定义配置。simple-json 示例配套的 metadata.md 给出了典型的 LoRaWAN 元数据表:

KeyValue
integrationNameTest LORIOT
includeGatewayInfofalse
rssi-21
seqno3040
fPort85
data01ed03335f0e4c63
toa206
ackfalse
battery94
drSF9 BW125 4/5
frequency867500000
offlinefalse
snr10
eui1000000000000001
cmdrx
fCnt2
ts1684478801936

对照第三节的 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,从源码可以确认以下实现事实:

  1. 实体类型判定(L269-L291):输出必须携带deviceName或assetName(对应本文示例的name+entityType),二者不能同时出现,否则抛出JsonParseException——这是输出的硬性校验;
  2. 遥测解析(L181-L187):输出 JSON 中的telemetry字段经parseTelemetry→JsonConverter.convertToTelemetryProto转为平台内部的PostTelemetryMsg;
  3. 属性解析(L188-L194):attributes字段经parseAttributesUpdate→JsonConverter.convertToAttributesProto转为PostAttributeMsg;
  4. 按需更新过滤(L208-L250):若转换器配置了"仅值变化才上报"(update-only)键,遥测与属性还会经过filterKeyValueAndUpdateMap去重过滤,避免重复值反复入库。

也就是说,本文示例中的entityType/name/profile + telemetry + attributes最终会被拆解为:实体定位信息(用于查找或创建设备)、PostTelemetryMsg(时序数据)与PostAttributeMsg(属性数据),分别进入平台的消息队列与存储链路。


七、编写 Converter 输出的实操建议

结合本示例与源码校验逻辑,编写上行转换器时建议遵循以下要点:

  1. 保证 Decoder Output 合法:返回对象必须包含非空的attributes与非空的telemetry(对象或数组),否则平台无法消费;
  2. 善用 name 的实体定位语义:使用eui、MAC 等硬件唯一值拼装name,保证同一设备重复上报时能被正确定位(本示例的Device 1000000000000001即源于 eui);
  3. 明确 entityType 二选一:DEVICE/ASSET与name必须唯一对应,不能同时出现deviceName与assetName;
  4. 区分两类字段来源:业务传感器数据由解码函数从 payload 提取(如battery/temperature/saturation),网关射频与集成信息(如fPort/rssi/snr/eui)可从metadata读取或依赖预配置注入;
  5. 可选字段按需使用:仅当需要自动归属客户、加入分组或展示友好名称时,才使用customer、group、label;
  6. 调试对照三份文档: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.

项目地址:https://gitcode.com/GitHub_Trending/th/thingsboard
点击查看免费下载

相关推荐

上一篇:SpringBlade项目文档自动化:Asciidoctor与GitBook使用
下一篇:革命性多语言语音合成:MeloTTS语音清晰度突破方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询