- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
导读
在 ThingsBoard 规则引擎中,Create Alarm(创建告警)节点允许你通过一段 TBEL(ThingsBoard Expression Language)脚本自定义存入告警(Alarm)实体的附加详情(Alarm Details),例如把入站消息 payload 中的temperature属性、元数据中的设备名称等保存到告警中。本文将围绕 create_alarm_node_script_fn.md 中定义的Details(msg, metadata, msgType)构建函数,讲解它的签名、参数、返回值约定,以及如何通过metadata.prevAlarmDetails读取并合并历史告警详情,实现告警次数的累计计数。读完本文,你将能够在 ThingsBoard 中独立编写、配置并调试这段 TBEL 脚本。
Details 函数:Create Alarm 节点如何生成 Alarm Details
Create Alarm 节点在创建或更新告警时,会调用一段用户编写的脚本函数来生成Alarm Details对象。该对象最终作为告警实体的details字段存储,用于承载告警之外的附加业务参数。
函数的完整声明如下:
function Details(msg, metadata, msgType): any- 这段脚本属于 TBEL 语言(ThingsBoard 基于 JavaScript 语法的表达式语言),配置位置在 Create Alarm 节点的Alarm details配置项中;
- 脚本的返回值被序列化为 JSON,作为告警的Alarm Details存储;
- 常见用法是:从原始消息(Original Message)的 payload 或 metadata中挑选出若干键值对,保存到告警详情里,便于后续规则链节点或仪表板直接使用。
参数说明
| 参数 | 类型 | 含义 |
|---|---|---|
msg | {[key: string]: any} | 消息 payload 的键值对象,值可以是任意类型(数字、字符串、布尔、嵌套对象等) |
metadata | {[key: string]: string} | 消息元数据键值映射,键和值都必须是字符串 |
msgType | string | 消息类型字符串,对应规则引擎中常见的MessageType枚举值(如POST_TELEMETRY_REQUEST、ENTITY_CREATED等) |
关于三个参数的完整定义,可参见 common_node_script_args.md。其中metadata强调键值均为字符串,因此从msg中取值可以保留数字类型,而从metadata中取出的任何值本质上都是字符串,需要时记得自行转换。
返回值约定
函数必须返回一个对象(object),该对象即告警的 Alarm Details。返回{}空对象也是合法的,此时告警详情为空。如果返回null或抛出异常,将导致该节点的消息处理失败(节点会走 Failure 链路),这一点在源码测试中也有明确验证(见下文“源码级验证”部分)。
通过 metadata.prevAlarmDetails 读取历史告警详情
这是本函数最有价值、也最容易踩坑的一个点。
当规则链中有多个消息持续触发同一告警(例如同一设备连续上报超温数据)时,Create Alarm 节点可能会更新已存在的告警。此时,TBEL 引擎会把上一次保存的 Alarm Details 以原始字符串形式注入到本次脚本的metadata.prevAlarmDetails字段中,供你读取、合并。
需要特别注意:
- 仅在存在上一次告警详情时才存在该字段;如果这是第一次创建该告警,
metadata.prevAlarmDetails不会出现在 metadata 中; metadata.prevAlarmDetails是raw String(原始字符串),不是对象,必须先用JSON.parse(...)反序列化后才能访问其中的属性;- 解析完成后,建议立即用
delete metadata.prevAlarmDetails(TBEL 写法)把它从 metadata 中移除,避免该字段被后续规则节点继续携带。
标准解析模板
var details = {}; if (metadata.prevAlarmDetails) { // remove prevAlarmDetails from metadata delete metadata.prevAlarmDetails; details = JSON.parse(metadata.prevAlarmDetails); }这段代码的语义是:优先继承上一次告警的全部详情字段,作为本次详情的基础对象;然后再在上面叠加本次消息带来的新数据。
从源码看,该字段的注入发生在 TbAbstractAlarmNode.java 的buildAlarmDetails方法中:
static final String PREV_ALARM_DETAILS = "prevAlarmDetails"; ... protected ListenableFuture<JsonNode> buildAlarmDetails(TbMsg msg, JsonNode previousDetails) { try { TbMsg dummyMsg = msg; if (previousDetails != null) { TbMsgMetaData metaData = msg.getMetaData().copy(); metaData.putValue(PREV_ALARM_DETAILS, JacksonUtil.toString(previousDetails)); dummyMsg = msg.transform().metaData(metaData).build(); } return scriptEngine.executeJsonAsync(dummyMsg); } ... }可以看到:当存在历史详情(previousDetails != null)时,节点会把历史详情序列化成字符串写入prevAlarmDetails元数据,再交给 TBEL 脚本引擎执行。这正解释了为什么脚本中拿到的metadata.prevAlarmDetails是一个需要JSON.parse的字符串。
实战示例:计数累加 + 温度属性入库
文档中给出的完整示例覆盖了最常见的两类操作:从历史详情中读取计数并累加、从入站消息 payload 中取属性写入详情。
var details = {temperature: msg.temperature, count: 1}; if (metadata.prevAlarmDetails) { var prevDetails = JSON.parse(metadata.prevAlarmDetails); // remove prevAlarmDetails from metadata delete metadata.prevAlarmDetails; if (prevDetails.count) { details.count = prevDetails.count + 1; } } return details;逐行解读:
- 初始化新详情:
var details = {temperature: msg.temperature, count: 1};- 直接把入站消息 payload 中的
temperature属性存入详情; count初始为1,代表这是第一次告警(或首次检测到的次数)。
- 直接把入站消息 payload 中的
- 合并历史详情:如果存在
metadata.prevAlarmDetails,先JSON.parse得到上一次的详情对象prevDetails。 - 清理 metadata:
delete metadata.prevAlarmDetails,保证向下游节点传递的 metadata 与进入本节点时一致。 - 递增计数:如果上一次详情里已有
count字段,则在其基础上+1,实现“该告警累计触发次数”的持久化统计。 - 返回:
return details;,最终count会变成历史次数 + 1,temperature则是本次消息的最新值。
这一“历史计数 + 实时属性”的组合,在实际中非常有用:例如统计某设备连续超温的告警次数,同时保留最近一次的超温温度值。
与 Clear Alarm 节点的关联
类似地,Clear Alarm(清除告警)节点 也使用同样的Details函数形态来生成更新后的详情对象,同样依赖metadata.prevAlarmDetails读取当前告警详情,并通过metadata.remove('prevAlarmDetails')清理元数据。两者的写法可以相互借鉴。
源码级验证:默认模板、脚本语言选择与测试覆盖
两种脚本语言的默认模板
Create Alarm 节点同时支持TBEL和JS两种脚本语言,对应源码中的两个默认模板,见 TbAbstractAlarmNodeConfiguration.java:
- JS 模板(
ALARM_DETAILS_BUILD_JS_TEMPLATE):使用if (metadata.prevAlarmDetails)判断,并用delete metadata.prevAlarmDetails清理; - TBEL 模板(
ALARM_DETAILS_BUILD_TBEL_TEMPLATE):使用if (metadata.prevAlarmDetails != null)判断,并用metadata.remove('prevAlarmDetails')清理。
两者的判断条件与清理方式略有差异,但语义一致。TBEL 更推荐使用!= null判断与metadata.remove(...)写法(本文沿用原文档的 TBEL 风格)。
脚本语言的实际选择发生在 TbAbstractAlarmNode.java 的init方法中:
scriptEngine = ctx.createScriptEngine(config.getScriptLang(), ScriptLanguage.TBEL.equals(config.getScriptLang()) ? config.getAlarmDetailsBuildTbel() : config.getAlarmDetailsBuildJs());即:当scriptLang为TBEL时,执行的是alarmDetailsBuildTbel字段中的脚本;否则执行alarmDetailsBuildJs中的脚本。
测试用例验证的行为
仓库中的单元测试 TbCreateAlarmNodeTest.java 对这一机制做了完整验证,可以佐证以下几点:
- 默认配置下,
alarmDetailsBuildJs与alarmDetailsBuildTbel分别等于上文两套默认模板(测试第 106–129 行); - 当存在历史告警时,脚本执行前会被注入一个带有
prevAlarmDetails元数据的 dummy 消息(测试第 625 行断言actualDummyMsg.getMetaData().getData()包含prevAlarmDetails键); - 当
overwriteAlarmDetails为false时,历史详情会被读取并合并进新详情;当其为true时则直接用新详情覆盖(测试第 856–1000 行分别覆盖了两种分支); - 当告警详情脚本抛出异常时,消息会走失败链路,且不会执行其他动作(测试第 1224–1243 行的
whenAlarmDetailsScriptThrowsException用例)。
这些测试同时印证了一个重要行为:告警详情的写入是否覆盖历史数据,还取决于节点配置中的 “Overwrite alarm details” 选项,而不仅仅是脚本逻辑本身。
调试与验证
在 ThingsBoard 界面配置该节点时:
- 在规则链编辑器中打开Create Alarm节点;
- 在Script language中选择
TBEL; - 在Alarm details文本框中粘贴上述脚本(此为该函数对应的配置项);
- 开启节点的 Debug 模式,即可在规则链的调试面板中近乎实时地查看每次经过该节点的消息(含入站消息与经过脚本处理后出站的消息),确认生成的 Alarm Details 是否符合预期。
Debug 模式的详细使用方法可参考仓库中 common_node_script_args.md 中的指引。
延伸阅读
- 告警(Alarm)实体的整体模型、生命周期与告警规则配置,可进一步查阅仓库内
ui-ngx/src/assets/help中关于告警的教程类文档; - 结合 create_alarm_node_script_fn.md 同目录下的其他节点脚本文档(如 filter_node_script_fn.md、transformation_node_script_fn.md),可以系统掌握 TBEL 在规则引擎各节点中的通用写法;
- 若要实际演练“创建与清除告警”的完整规则链配置步骤,可参考官方针对 Create and Clear Alarms 场景的逐步教程。
- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
相关推荐
ThingsBoard 规则引擎:Clear Alarm 节点 Alarm Details 构建函数(clear_alarm_node_script_fn)完全指南
ThingsBoard 规则引擎:Clear Alarm 节点 Alarm Details 构建函数(clear_alarm_node_script_fn)完全
物联网后端数据可视化消息队列巴菲特《错误的25年》精读:机构惯性、卓越公司与工程管理的稳健决策
巴菲特《错误的25年》精读:机构惯性、卓越公司与工程管理的稳健决策 本文是对工程管理资源库 engineering management https://lin
物联网后端数据可视化消息队列ThingsBoard AI 请求节点提示词设置实战:基于 Telemetry 与 Alarm 的提示模板与 JSON 结构化输出
ThingsBoard AI 请求节点提示词设置实战:基于 Telemetry 与 Alarm 的提示模板与 JSON 结构化输出 AI 请求节点(AI req
物联网后端数据可视化消息队列
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考