- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
导读
本文围绕 ThingsBoard 集成(Integration)上行数据转换器(Uplink Data Converter)中一个典型的 CSV 场景展开:设备通过 HTTP、CoAP、MQTT 等集成上报形如SN-111,36.6,70的纯文本负载,如何在 TBEL 解码器函数中将其拆解为设备名与温湿度遥测数据,并输出为平台统一要求的 JSON 结构。读完本文,你将掌握 TBEL 中decodeToString、split、parseFloat等内置函数的使用方法、解码器返回结果的字段规范,以及结合源码理解这些辅助函数的底层实现。
一、场景与输入:一段简单的 CSV 文本负载
在 ThingsBoard 集成中,上行消息的负载(payload)是一个字节数组,集成可能按内容类型产生 JSON、TEXT、Binary(Base64) 三种格式。CSV 属于典型的 TEXT 类型负载。关联文档给出了本示例的输入:
CSV payload 示例
SN-111,36.6,70这一行由三个以逗号分隔的字段组成,含义约定如下:
| 字段顺序 | 内容 | 类型 |
|---|---|---|
csv[0] | 设备序列号SN-111 | 字符串 |
csv[1] | 温度36.6 | 数字 |
csv[2] | 湿度70 | 数字 |
CSV 格式的优势在于极低的设备端开销:传感器固件无需拼接 JSON 字符串,只需把数值按固定顺序以逗号连接即可上报,非常适合低功耗、窄带或极简 MCU 设备。
二、解码函数:将 CSV 文本转换为平台统一格式
CSV 场景对应的完整解码函数位于 decoder_fn.md:
// decode payload to string. See helper function below var str = decodeToString(payload); // split the string to an array of strings using ',' delimiter var csv = str.split(','); // Construct result object with time-series data var result = { deviceName: csv[0], deviceType: "Thermostat", deviceLabel: "Kitchen Thermostat", telemetry: { temperature: parseFloat(csv[1]), humidity: parseFloat(csv[2]), } }; return result;整个处理链路可以拆解为三步:
decodeToString(payload):把集成传入的字节数组按平台默认字符集还原为字符串。这一步将SN-111,36.6,70变为可直接操作的文本。str.split(','):按逗号切分为字符串数组,得到["SN-111", "36.6", "70"]。parseFloat(csv[1])与parseFloat(csv[2]):将"36.6"、"70"转为数值类型后写入遥测对象。parseFloat是 TBEL 内置的数值解析函数,等价于在沙箱脚本环境中安全地完成字符串到浮点数的转换。
注意
csv[0](SN-111)不需要parseFloat,因为它作为设备序列号应当保持字符串语义。
输出结果验证
解码器输出对应的参考结果见 output.md:
{ "deviceName": "SN-111", "deviceType": "Thermostat", "deviceLabel": "Kitchen Thermostat", "telemetry": { "temperature": 36.6, "humidity": 70 } }从源码结构看,该输出会进一步被平台的消息转换链路消费:deviceName用于查找或创建同名设备,telemetry中的temperature与humidity作为时序数据写入数据库。其中36.6以浮点数、70以整数形式呈现,正与parseFloat的解析行为一致。
三、解码器返回结果字段规范
TBEL 解码器函数签名与返回要求在 decoder_fn.md 中有完整说明,核心规则如下:
- 必选标识:返回对象必须包含
deviceName+deviceType,或assetName+assetType中的一对。平台依据名称在租户范围内查找已有设备/资产,若不存在且集成的「Allow to create devices or assets」设置开启,则会自动创建新实体。实践中常用 DevEUI、MAC 地址等唯一标识作为设备名。 - 可选
deviceLabel/assetLabel:非唯一的、面向仪表盘展示的用户友好标签。本示例中的Kitchen Thermostat即属此类。 - 可选
attributes:要写入设备/资产的服务器端属性集合。 - 可选
telemetry:时序数据对象或数组,即本示例中的温湿度。 - 可选
customerName与groupName:自动将新创建的设备分配给客户/实体组(仅在设备/资产由本次集成创建时生效,实体已存在时被忽略)。 - 可选时间戳:若需要自定义事件时间,可像其他示例那样将
ts以 Unix 纪元毫秒数随数据输出;缺省时平台使用服务器时间。
与 JSON 解码示例的对比
CSV 示例与同目录下的 Simple JSON 示例 形成了鲜明对照:
- JSON 示例使用
decodeToJson(payload)将负载解析为对象,再通过json.serialNumber、json.t取字段; - CSV 示例由于没有键名,只能通过
split(',')按位置取字段,字段顺序即协议约定。
两者的共同点是都调用了 TBEL 辅助函数完成字节数组到可读结构的首步转换,随后再组装成统一的结果对象。
四、源码佐证:decodeToString与decodeToJson的实现原理
decodeToString等辅助函数由平台以方法桩(MethodStub)形式注入 TBEL 脚本沙箱。在 TbUtils.java 的register(ParserConfiguration)方法中可以看到:
parserConfig.addImport("decodeToString", new MethodStub(TbUtils.class.getMethod("bytesToString", List.class))); parserConfig.addImport("decodeToJson", new MethodStub(TbUtils.class.getMethod("decodeToJson", ExecutionContext.class, List.class))); parserConfig.addImport("decodeToJson", new MethodStub(TbUtils.class.getMethod("decodeToJson", ExecutionContext.class, String.class)));也就是说,decodeToString(payload)实际指向bytesToString。其实现位于 TbUtils.java#L407-L415:
public static String bytesToString(List<?> bytesList) { byte[] bytes = bytesFromList(bytesList); return new String(bytes); } public static String bytesToString(List<?> bytesList, String charsetName) throws UnsupportedEncodingException { byte[] bytes = bytesFromList(bytesList); return new String(bytes, charsetName); }由此可见两点实现事实:
- 默认无参形式使用 JVM 默认字符集把字节数组还原为字符串;若设备上报使用特定字符集(如
UTF-8、ASCII),可选用带charsetName参数的重载版本; - 对应地,
decodeToJson则先走bytesToString再交给TbJson.parse完成 JSON 解析,见 TbUtils.java#L399-L405。
这解释了为什么在 TBEL 中面对 TEXT 型负载应当用decodeToString而面对 JSON 负载应当用decodeToJson——两者共享同一套字节解码基础,只是后续解析路径不同。
五、实战建议:让 CSV 解码器更健壮
参考官方示例的实现思路,在生产环境中使用 CSV 解码器时有几个可以扩展的注意点:
- 容错与字段校验:官方示例直接按固定下标取字段,未处理空行或字段不足的情况。可以在
split之后先判断数组长度(如csv.length < 3时提前返回或丢弃),避免运行时下标越界导致数据丢失。 - 统一分隔符约定:
split(',')要求设备端严格使用半角逗号且不含多余空白。若设备可能混用,与,(全角逗号)或带尾随换行,可在切分前对str做trim()或替换处理。 - 字符集显式化:当设备上报非 ASCII 内容时,参考源码中的
bytesToString(list, charsetName)重载,选择与设备端一致的字符集,避免中文序列号等内容出现乱码。 - 利用可选字段:如需把设备自动归类到客户或实体组,可在返回对象中补充
customerName、groupName;如需自定义数据时间戳,则按telemetry: { ts: ..., values: {...} }结构返回。 - 多行 CSV 的扩展思路:若设备一次上报多行数据(如
SN-111,36.6,70\nSN-222,22.0,55),可以先用split('\n')拆成多行,再将每行用split(',')拆字段,最终按官方json_array_output的思路返回对象数组,一次转换即可写入多条遥测。
六、调试与验证方式
ThingsBoard 提供集成的调试功能:配置好解码器后可在集成页面发送模拟消息,观察解码器的输入 payload、执行日志与输出 JSON。可以借助以下仓库内素材进行对照验证:
- 输入样例:payload.md
- 解码函数:decoder_fn.md
- 期望输出:output.md
- 解码器完整规则说明:decoder_fn.md(总览)
- 辅助函数实现:TbUtils.java
建议在调试时将官方示例的输入、函数与输出三份素材逐一对齐比对,确认字段映射无误后再将函数部署到真实集成中。
结语
CSV 文本负载是 IoT 设备接入中最经济的报文形态之一,而 TBEL 解码器恰好提供了从裸字节到平台统一数据模型的完整转化能力。掌握decodeToString+split+parseFloat这一组合,并理解解码器返回对象中deviceName、telemetry等字段的语义与平台侧的创建/写入行为,就能快速让纯文本设备接入 ThingsBoard 并在仪表盘上呈现实时温湿度数据。
- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
相关推荐
Phi-3-mini-4k-instruct 接入 LangChain 实战:自定义 LLM 类实现统一大模型调用
Phi 3 mini 4k instruct 接入 LangChain 实战:自定义 LLM 类实现统一大模型调用 本指南完整演示如何在 Datawhale s
物联网后端数据可视化消息队列Grok Build 终端支持与故障排查:从 /doctor 诊断到 tmux、SSH、剪贴板与 RTL 实战指南
Grok Build 终端支持与故障排查:从 /doctor 诊断到 tmux、SSH、剪贴板与 RTL 实战指南 Grok Build 以全屏 TUI 形式运
物联网后端数据可视化消息队列在 Flame 中开启 3D 游戏开发:flame_3d 环境配置、场景搭建与自定义着色器完全指南
在 Flame 中开启 3D 游戏开发:flame_3d 环境配置、场景搭建与自定义着色器完全指南 本文以 packages/flame_3d/README.m
物联网后端数据可视化消息队列
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考