尧图精选

ThingsBoard 计算字段(Calculated Field)TBEL 脚本函数 `calculate()` 完整指南

🕒 发布时间:2026/10/1 21:38:17 📁 来源:尧图网络
物联网后端数据可视化消息队列【免费下载链接】thingsboardAll-in-one IoT Platform - Device management, data collection, processing and visualization.项目地址https://gitcode.com/GitHub_Trending/th/thingsboard点击查看免费下载计算字段Calculated Field是 ThingsBoard 平台内置的边缘/云端数据计算能力它允许用户不写一行 Java 代码仅通过 TBELThingsBoard Expression Language脚本在遥测数据与属性数据之上完成自定义计算、数据聚合与异常分析。本文以 expression_fn.md 为核心骨架结合仓库中 CalculatedFieldCtx、TbelCfTsRollingArg 等源码实现系统讲解calculate()函数的签名、三类参数访问方式、时间序列滚动参数的聚合与合并能力以及时间序列/属性两种输出格式。读完本文你将能独立编写、调试并部署一个可落地的计算字段脚本。函数签名与执行模型计算字段的 TBEL 脚本由用户自定义函数calculate()构成其完整签名如下function calculate(ctx, arg1, arg2, ...): object | object[]ctx一个上下文对象包含latestTs属性以及args映射用于访问全部参数arg1, arg2, ...计算字段配置中声明的各个参数参数以名称方式传入返回值一个 JSON 对象或 JSON 对象数组具体格式由计算字段的输出类型决定默认是 Time Series即时间序列。从源码角度看该函数的执行链路位于 CalculatedFieldTbelScriptEngine.java脚本通过tbelInvokeService.eval(tenantId, ScriptType.CALCULATED_FIELD_SCRIPT, script, argNames)编译注册随后由invokeScript异步执行最终返回值经 Jackson 序列化为JsonNode交给计算字段状态机处理。参数在传入脚本前会由 CalculatedFieldCtx.evaluateTbelExpression() 统一装配args数组第一个元素固定是TbelCfCtx对象之后的每个参数按名次追加——单值参数直接传入其value而滚动参数则作为完整对象传入。这正是文档中直接访问参数名与通过ctx.args.argName访问两种方式并存的底层原因。支持的参数类型计算字段配置中共支持三类参数它们决定了你在脚本中能以何种形态使用数据。1. 属性与最新遥测参数单值这类参数是单个值类型可以是boolean、int64 (long)、double、string或JSON。它们直接对应实体的最新遥测或属性值。示例将华氏温度转换为摄氏温度var temperatureC (temperatureF - 32) / 1.8; return { temperatureC: toFixed(temperatureC, 2) }这里temperatureF就是配置中声明的一个单值参数此处为最新遥测值脚本中可直接按名称引用。toFixed()是 TBEL 内置函数用于保留指定位小数。2. 通过ctx.args.argName访问参数除了直接访问参数还可以通过ctx.args.argName对象访问。该对象同时携带参数的值value与时间戳ts{ temperatureF: { ts: 1740644636669, value: 36.6 } }改造上面的华氏转摄氏示例使其同时输出时间戳信息var temperatureC (temperatureF - 32) / 1.8; return { ts: ctx.args.temperatureF.ts, values: {temperatureC: toFixed(temperatureC, 2)} };在 TbelCfCtx.java 中args是一个不可变映射Collections.unmodifiableMap且latestTs在传入值非-1时使用传入值否则回退为System.currentTimeMillis()——也就是说即使触发本次计算的数据没有时间戳ctx.latestTs也始终有可用值。3. 时间序列滚动参数滚动参数Rolling Argument包含一个时间窗口内的整段时序数据其 JSON 形态如下{ temperature: { timeWindow: { startTs: 1740643762896, endTs: 1740644662896 }, values: [ { ts: 1740644350000, value: 72.32 }, { ts: 1740644360000, value: 72.86 }, { ts: 1740644370000, value: 73.58 }, { ts: 1740644380000, value: NaN } ] } }关键规则滚动参数内的所有值一律转换为double类型当转换失败时该值被标记为NaN可使用 TBEL 内置函数isNaN(double): boolean判断一个值是否为有效数字。以下示例演示了如何遍历滚动参数并求和包含三种等价写法var startOfInterval temperature.timeWindow.startTs; var endOfInterval temperature.timeWindow.endTs; var firstItem temperature.values[0]; var firstItemTs firstItem.ts; var firstItemValue firstItem.value; var sum 0.0; // iterate through all values and calculate the sum using foreach: foreach(t: temperature) { if(!isNaN(t.value)) { // check that the value is a valid number; sum t.value; } } // iterate through all values and calculate the sum using for loop: sum 0.0; for (var i 0; i temperature.values.size; i) { sum temperature.values[i].value; } // use built-in function to calculate the sum sum temperature.sum();注意TBEL 中集合长度访问的是values.size属性形式而非 Java 风格的size()。滚动参数的内置聚合方法滚动参数内置了一组开箱即用的聚合函数。这些函数均接受一个可选的ignoreNaN布尔参数默认值为true忽略 NaN。完整行为对照如下方法默认行为ignoreNaN true备选行为ignoreNaN falsemax()返回最大值忽略 NaN只要存在 NaN 即返回 NaNmin()返回最小值忽略 NaN只要存在 NaN 即返回 NaNmean(), avg()计算平均值忽略 NaN只要存在 NaN 即返回 NaNstd()计算标准差忽略 NaN只要存在 NaN 即返回 NaNmedian()返回中位数忽略 NaN只要存在 NaN 即返回 NaNcount()统计非 NaN 值的个数统计全部值含 NaNlast()返回最近的有效值跳过 NaN返回最后一个值即使是 NaNfirst()返回最早的有效值跳过 NaN返回第一个值即使是 NaNsum()计算总和忽略 NaN只要存在 NaN 即返回 NaN这些方法的实际行为可以在 TbelCfTsRollingArg.java 中逐一核对。例如max(boolean ignoreNaN)在ignoreNaN false时一旦遇到NaN立即返回该NaNmean()内部实现为sum(ignoreNaN) / count(ignoreNaN)median()会对有效值排序后取中间值偶数个时取中间两数均值last()/first()在忽略 NaN 时向数组两端线性扫描全部为 NaN 或数组为空时抛出IllegalArgumentException(Rolling argument values are empty.)。以下面这组数据为例{ temperature: { timeWindow: { startTs: ..., endTs: ... }, values: [ { ts: 1740644350000, value: 72.32 }, { ts: 1740644360000, value: 72.86 }, { ts: 1740644370000, value: 73.58 }, { ts: 1740644380000, value: NaN } ] } }调用结果对照var avgTemp temperature.mean(); // Returns 72.92 var tempMax temperature.max(); // Returns 73.58 var valueCount temperature.count(); // Returns 3 var avgTempNaN temperature.mean(false); // Returns NaN var tempMaxNaN temperature.max(false); // Returns NaN var valueCountNaN temperature.count(false); // Returns 4实战示例根据海拔与温度估算空气密度下面这个完整的calculate()函数将单值参数altitude海拔与滚动参数temperature温度序列结合起来先求平均温度再依次推算开氏温度、气压与空气密度function calculate(ctx, altitude, temperature) { var avgTemperature temperature.mean(); // Get average temperature var temperatureK (avgTemperature - 32) * (5 / 9) 273.15; // Convert Fahrenheit to Kelvin // Estimate air pressure based on altitude var pressure 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588); // Air density formula var airDensity pressure / (287.05 * temperatureK); return { airDensity: toFixed(airDensity, 2) }; }可以看到单值参数altitude直接参与算术表达式滚动参数temperature则通过.mean()聚合成一个标量后参与运算这正是两类参数最典型的协作方式。时间序列参数的合并merge()与mergeAll()当需要对齐多个数据集的时间戳进行联合分析时可以使用滚动参数的合并能力方法说明返回merge(other, settings)与另一个滚动参数合并。对齐时间戳缺失值用前一个可用值填充。含timeWindow与对齐后values的合并对象mergeAll(others, settings)与多个滚动参数合并。对齐时间戳缺失值用前一个可用值填充。含timeWindow与对齐后values的合并对象参数说明参数说明other或others待合并的另一个滚动参数或滚动参数数组settings可选配置对象支持ignoreNaN控制是否忽略 NaN 值timeWindow定义自定义时间窗口从源码看合并算法位于 TbelCfTsRollingArg.mergeAll()merge()内部即委托给mergeAll它收集所有参与合并的滚动参数的时间戳TreeSet去重排序取所有窗口的并集作为结果timeWindow对每个时间戳使用前向游标lastIndex数组取当前时间戳之前最近一个值进行填充从而天然实现文档所说的缺失值用前一个可用值填充。当ignoreNaN true时任一路径填充不上仍为 NaN的行会被整体跳过settings.timeWindow若提供则还会过滤掉落在自定义窗口之外的时间戳。合并示例一merge()假设输入参数如下完整输入数据见 merge_input.md{ humidity: { timeWindow: { startTs: 1741356332086, endTs: 1741357232086 }, values: [ { ts: 1741356882759, value: 43 }, { ts: 1741356918779, value: 46 } ] }, pressure: { timeWindow: { startTs: 1741356332086, endTs: 1741357232086 }, values: [ { ts: 1741357047945, value: 1023 }, { ts: 1741357056144, value: 1026 }, { ts: 1741357147391, value: 1025 } ] }, temperature: { timeWindow: { startTs: 1741356332086, endTs: 1741357232086 }, values: [ { ts: 1741356874943, value: 76 }, { ts: 1741357063689, value: 77 } ] } }使用方式见 merge_usage.mdvar mergedData temperature.merge(humidity, { ignoreNaN: false });输出结果见 merge_output.md可以看到在temperature有值而humidity尚未上报的时间戳处humidity一侧被填充为NaN且每个时间戳处两个序列的值以数组形式并列对齐{ mergedData: { timeWindow: { startTs: 1741356332086, endTs: 1741357232086 }, values: [ { ts: 1741356874943, values: [76.0, NaN] }, { ts: 1741356882759, values: [76.0, 43.0] }, { ts: 1741356918779, values: [76.0, 46.0] }, { ts: 1741357063689, values: [77.0, 46.0] } ] } }合并示例二mergeAll()一次合并多个滚动参数使用方式见 merge_all_usage.mdvar mergedData temperature.mergeAll([humidity, pressure], { ignoreNaN: true });输出结果见 merge_all_output.mdvalues数组中每个元素按[temperature, humidity, pressure]顺序排列由于这里ignoreNaN: true缺失任一序列的时间戳行会被直接剔除{ mergedData: { timeWindow: { startTs: 1741356332086, endTs: 1741357232086 }, values: [ { ts: 1741357047945, values: [76.0, 46.0, 1023.0] }, { ts: 1741357056144, values: [76.0, 46.0, 1026.0] }, { ts: 1741357063689, values: [77.0, 46.0, 1026.0] }, { ts: 1741357147391, values: [77.0, 46.0, 1025.0] } ] } }注意合并结果的遍历语义合并对象中的ts是各时间戳values是定长数组元素顺序与合并参数顺序一致且支持foreach遍历每个元素可通过item.ts、item.v1、item.v2… 访问对应源码中的TbelCfTsMultiDoubleVal。实战示例冰箱温度异常分析下面这个函数将temperature序列与冰箱的defrost化霜状态0/1序列合并找出未处于化霜状态但内部空气温度高于 -5°C的异常时刻输出一个问题列表可直接喂给告警规则使用function calculate(ctx, temperature, defrost) { var merged temperature.merge(defrost); var result []; foreach(item: merged) { if (item.v1 -5.0 item.v2 0) { result.add({ ts: item.ts, values: { issue: { temperature: item.v1, defrostState: false } } }); } } return result; }由于默认ignoreNaN true任何一侧数据缺失的时间戳都会被合并过程剔除因此这里的item.v1温度与item.v2化霜状态一定是成对出现的有效值不会因NaN比较产生误报。输出结果如下[ { ts: 1741613833843, values: { issue: { temperature: -3.12, defrostState: false } } }, { ts: 1741613923848, values: { issue: { temperature: -4.16, defrostState: false } } } ]函数返回格式calculate()的返回格式取决于计算字段设置中的输出类型Output Type默认Time Series。消息时间戳ctx.latestTsctx对象除了args之外还暴露了latestTs属性——它表示触发本次计算的参数遥测的最新时间戳毫秒。当返回时间序列对象时可用它显式指定输出结果的时间戳var temperatureC (temperatureF - 32) / 1.8; return { ts: ctx.latestTs, values: { temperatureC: toFixed(temperatureC, 2) } }这样可确保计算出的数据点与触发计算的那条遥测记录时间戳严格对齐避免结果数据点被错误地标注在计算发生时刻或默认时刻。时间序列输出Time Series函数必须返回带或不带时间戳的 JSON 对象或数组。以下示例返回 5 个数据点airDensitydouble、humidityinteger、hvacEnabledboolean、hvacStatestring与configurationJSON。不带时间戳{ airDensity: 1.06, humidity: 70, hvacEnabled: true, hvacState: IDLE, configuration: { someNumber: 42, someArray: [1,2,3], someNestedObject: {key: value} } }带时间戳每个键的时间戳相同{ ts: 1740644636669, values: { airDensity: 1.06, humidity: 70, hvacEnabled: true, hvacState: IDLE, configuration: { someNumber: 42, someArray: [1,2,3], someNestedObject: {key: value} } } }数组形式多个时间戳、不同取值例如同一指标airDensity的多个采样点[ { ts: 1740644636669, values: { airDensity: 1.06 } }, { ts: 1740644636670, values: { airDensity: 1.07 } } ]属性输出Attribute当输出类型为 Attribute 时函数必须返回一个不带时间戳的 JSON 对象时间戳信息会被忽略。同样支持 5 种数据类型{ airDensity: 1.06, humidity: 70, hvacEnabled: true, hvacState: IDLE, configuration: { someNumber: 42, someArray: [1,2,3], someNestedObject: {key: value} } }编写建议与注意事项单值 vs 滚动单值参数属性/最新遥测直接以标量参与表达式滚动参数必须通过聚合方法.mean()、.last()、.count()等或逐项遍历才能参与标量运算。NaN 无处不在滚动序列中数据缺失、类型转换失败都会产生NaN。默认ignoreNaN true的聚合方法能让你免于手工过滤但需要精确计数含缺失时请显式传入false。合并对齐语义merge/mergeAll采用前向填充forward-fill即每个时间戳取各序列在此之前最近的值若某序列在结果窗口起始处尚无数据则被填充为NaN。此时配合ignoreNaN: false可以保留完整时间轴。返回结构决定落库形态Time Series 输出既支持无ts的扁平对象系统自动附时间戳也支持{ts, values}或数组形态多数据点Attribute 输出则必须是无ts的对象。若需要将结果与触发遥测的时间对齐务必使用ctx.latestTs。错误处理从 CalculatedFieldCtx.java 可以看到滚动聚合方法在参数值为空时会抛出IllegalArgumentException脚本编译失败时上下文初始化会直接抛异常并标记initialized false。因此在正式部署前务必使用 UI 中的脚本校验/测试功能或在本地执行引擎上验证脚本对空窗口、全 NaN 窗口等边界场景的行为。掌握以上函数签名、参数访问方式与输出约定后你便可以在 ThingsBoard 中以纯配置方式实现温湿度换算、空气质量计算、设备状态交叉分析、滚动统计监控等场景并让计算结果无缝进入遥测存储、属性面板或告警规则。赞分享物联网后端数据可视化消息队列【免费下载链接】thingsboardAll-in-one IoT Platform - Device management, data collection, processing and visualization.项目地址https://gitcode.com/GitHub_Trending/th/thingsboard点击查看免费下载相关推荐ThingsBoard 计算字段 TBEL 中 merge / mergeAll 时间序列合并函数完整指南ThingsBoard 计算字段 TBEL 中 merge / mergeAll 时间序列合并函数完整指南 导读 ThingsBoard 的计算字段Calcu物联网后端数据可视化消息队列ThingsBoard 计算字段Calculated Field中 merge / mergeAll 时间序列合并函数实战详解ThingsBoard 计算字段Calculated Field中 merge / mergeAll 时间序列合并函数实战详解 导读 在 ThingsBoa物联网后端数据可视化消息队列PyPTO 实现 RoPE旋转位置编码算子从 kernel 参考骨架到生产级实现PyPTO 实现 RoPE旋转位置编码算子从 kernel 参考骨架到生产级实现 导读 本文以 PyPTO Gym 仓库中 RoPE kernel 参考骨物联网后端数据可视化消息队列上一篇BuildKit 多平台镜像构建指南platform 参数、QEMU 模拟与交叉编译实战下一篇MMSegmentation 中的 PSPNet 完整指南金字塔池化模块原理、配置解析与多数据集基准实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →