计算指标
计算指标是设备配置文件上的一种指标,它的值由公式计算得出,而不是由设备上报。上报 voltage 和 current 的电表,可以有一个计算指标 power_kw,公式为 voltage * current / 1000。此后 power_kw 会像设备自己上报的测量值一样被存储、绘制成图表、触发告警并显示在设备上。本章面向编写设备配置文件的人员,介绍什么是计算指标、如何编写、公式语言,以及公式无法产生值时会发生什么。
计算指标默认关闭。实例运维人员需要为你所在租户的套餐等级设置一个大于零的上限才能启用。参见启用计算指标。
什么是计算指标
- 公式读取一次上报中的测量指标,也就是设备在同一次测量中一起上报的值。它不会读取更早的上报、其他设备或时钟。
- 结果在上报到达时只计算一次,并作为普通测量值与设备上报的值一起存储。仪表板、历史记录、导出、设备的最新值和检测规则读取它的方式与其他指标相同,都是通过它的键。
- **历史记录不会被改写。**编辑公式并发布配置文件,只会改变之后到达的上报的计算结果。已经存储的值保留计算它们时生效的那个公式。
- 指标是测量指标还是计算指标,在创建时就已确定。你可以编辑计算指标的公式,但不能把测量指标改为计算指标,反过来也不行。请删除它并新建一个。
- 设备不能覆盖计算出的值。如果设备以某个计算指标的键上报了值,设备的值会被丢弃,保留计算得到的值。
编写计算指标
打开一个设备配置文件,选择 Metrics(指标)选项卡(参见指标),然后选择 New metric(新建指标)。
- 将 Kind(类型)设为 Calculated(计算)。这个选择是最终的,因此编辑指标时它会被锁定。
- 填写 Metric key(指标键)、Token(令牌)和 Data type(数据类型):Double、Integer 或 Boolean。公式必须产生恰好该类型的结果(参见类型)。计算指标不能是字符串。
- 可选地设置 Unit(单位),以及 Min(最小值)和 Max(最大值)。超出该范围的结果会被跳过,不会存储。
- 输入 Formula(公式)。
- 选择 Create metric(创建指标)。
计算指标在指标列表中带有 Calculated(计算)标记。
与配置文件的所有编辑一样,新增或修改的公式只有在你发布版本之后才会对设备生效。
边输入边检查公式
你输入时,公式框会请服务器检查公式,使用的是发布时检查公式、并在真实上报上计算公式的同一个编译器。公式框下方会显示:
- 编译错误:如果公式无效,会用文字说明;
- 估算成本与上限 100 的对比。估算成本高于 100 的公式会被拒绝;
- 警告:警告不会阻止发布。例如,已发布的版本将该键声明为测量指标(因此设备可能正在上报它,这些值会被丢弃)、量级达到 1e12 或更大的结果无法存储,或单位不会被换算。
公式编译通过后,样本面板会列出公式读取的测量指标。为每个指标输入一个值,面板就会显示公式会产生的值,或者它不产生值的原因。样本由你手工输入,不会存储任何内容。
发布检查会拒绝什么
发布配置文件时会检查每个计算指标,并一次性报告所有问题。在以下情况下,配置文件会被拒绝:
- 公式无法编译,或产生的类型与声明的类型不符;
- 公式读取的键不是同一配置文件中已声明的测量指标。计算指标不能读取另一个计算指标;
- 公式没有读取任何指标;
- 公式长度超过 1,024 字节,估算成本高于 100,或同一配置文件所有公式的成本合计超过 1,000;
- 计算指标的 Min 大于 Max,声明了允许值列表,或其数据类型不是 Double、Integer 或 Boolean;
- 同一个键被声明了两次;
- 配置文件的计算指标数量超过你所在租户的套餐等级允许的数量。
公式语言
公式使用一种小型表达式语言编写。它没有循环、没有字符串,也没有状态,因此每个公式都在很小且有界的时间内运行。
输入
配置文件的每个测量指标都是一个以其键命名的变量,例如 voltage 或 temp。不是普通标识符的键,例如 flow-rate,要写成 m["flow-rate"] 读取,并包上与其类型对应的转换:double(m["flow-rate"])、int(m["count"]) 或 bool(m["door"])。
运算符
| 类别 | 运算符 |
|---|---|
| 算术 | + - * / % 以及一元 - |
| 比较 | == != < <= > >= |
| 逻辑 | && || ! |
| 条件 | condition ? a : b |
数学函数
math.abs(x)、math.floor(x)、math.ceil(x)、math.round(x)(一半的情况向远离零的方向取整)、math.sqrt(x)、math.pow(x, n)、math.min(a, b)、math.max(a, b) 和 math.clamp(x, low, high)。
math.pow 只接受整数指数:math.pow(x, 2) 可以使用,math.pow(x, 0.5) 不行。小数次幂在不同处理器架构上可能在最后一位出现差异,而存储的值不应取决于由哪台机器计算。需要平方根时,请使用 math.sqrt。
存在性判断
has(m.fan) 和 "fan" in m 用来判断一次上报是否带有 fan 的值。你可以借此为缺失的输入给出明确的默认值:has(m.fan) ? fan : 0.0。
类型
公式会按指标的数据类型检查,检查十分严格:
- 整数和 double 可以在
+ - * /以及大小比较中混用(整数会被提升)。它们不能在==或!=中混用,也不能出现在condition ? a : b的两个分支中:与 double 比较时,请写80.0而不是80。 - Double 指标的公式必须产生 double。产生整数的公式需要使用
double(...)。 - Integer 指标的公式必须产生整数。用
int(...)转换 double。math.sqrt和math.pow返回 double,因此用于 Integer 指标时要包上int()。 - Boolean 指标的公式必须产生布尔值,例如
temp > 80.0 && fan == 0。布尔值以1和0存储。
不可用的内容
字符串(m["key"] 中作为键的情况除外)、列表、映射、时间和时长、随机数、推导式、正则表达式,以及任何读取上报之外内容的东西。
公式不产生值时
如果公式无法为某次上报产生值,该指标在这次上报中会被跳过。上报的其余部分照常存储,而且绝不会用其他值代替:没有默认值,也不会补零。原因如下:
| 原因 | 含义 |
|---|---|
missing_input | 公式读取的某个输入不在这次上报中。除非公式用 has() 或 in 判断过它,否则该输入是必需的。 |
invalid_input | 输入存在,但对其类型而言不是有效值。 |
non_finite | 某个数不是有限值:输入不是有限数、除以零、结果对 double 来说太大,或对负数取平方根。 |
div_zero | 整数除以零,或对零取余。 |
overflow | 整数结果或转换超出 64 位。 |
range | 结果超出了该指标的 Min 和 Max。 |
unstorable | 结果的量级达到 1e12 或更大,测量值无法以这样的量级存储。 |
eval_error | 其他任何失败,例如 math.clamp 的上下界颠倒。 |
无论操作数的顺序如何,缺失的输入都会被发现。temp > 80.0 || fan > 1 在没有 fan 的上报上会被跳过,即使较高温度的上报会使前半部分为真。如果你希望公式在没有 fan 时也能工作,请用 has() 判断它。
存储与精度
- 测量值存储时,小数点前最多 12 位,小数点后 8 位。计算值在存储时会四舍五入到 8 位小数,量级达到 1e12 或更大的结果会以
unstorable跳过。将 Min 和 Max 设在正负 999,999,999,999 之内,就等于告诉编辑器你清楚该范围,这条警告也就不再出现。 - 计算值与任何测量值一样存储,因此遵循你所在租户的测量值保留策略。
- 单位不会被换算或检查。公式把输入当作普通数字读取,结果只带你在计算指标上声明的单位。
启用计算指标
单个配置文件可以发布多少个计算指标,由实例运维人员按套餐等级设置,位置是 Admin → Tiers 中的 Calculated metrics per profile。平台默认值为 0,表示计算指标处于关闭状态:你可以编写并测试公式,但发布含有计算指标的配置文件会被拒绝,并提示:
cannot publish device profile: it has N calculated metric(s), and calculated metrics are not enabled for this tenant's tier
当上限大于零但小于配置文件中的数量时,提示会说明该套餐等级每个配置文件允许的数量。任何套餐等级最多只能授予 32 个。如果你需要计算指标,请让运维人员提高上限。