跳到主要内容

计算公式 (Formula)

在注册标签时,将算术公式填入 fomula 字段,驱动程序会对从 PLC 读取的原始值(raw value)进行后处理,并将转换后的值用于缓存 / Sparkplug / REST 响应。

项目值
实现类plantpulse.app.edge.component.collector.plc.PLCValueFomula
调用位置PLCValueReader.read() 的第 3 步(raw → format → formula → 缓存更新)
库plantpulse-edge-fomula.jar (plantpulse.edge.fomula.FormulaEngine)
变量替换${VALUE} (当前标签值) / ${tag_id} (其他标签的最后值, LastValueMap 缓存)
应用对象 data_type仅适用于 Float / Double / Integer / Long。其他类型(String, Boolean, …)则直接通过
结果Formula.evaluate(...) 中的 BigDecimal → 按类型渲染(整数类型显示为整数,Float/Double 显示为小数)

操作流程​

  1. 从驱动程序接收 raw 字符串值(例如: "16384")。

  2. 调用 PLCValueFomula.fomulaValue(address, value)。

  3. 如果 address.getFomula() 为空,则直接返回。

  4. 如果 data_type 不是 Float / Double / Integer / Long,则直接返回。

  5. 检查公式引用的名称 — VALUE 是刚读取的值,其余是 LastValueMap 中其他标签的最后值。

  6. 如果引用的标签尚未被采集过,则抛出异常:

    Variable value referenced by the formula is not yet in cache : formula=[`<fomula>`], variable=[`<name>`]
  7. 仅将公式引用的名称绑定值后进行求值 → BigDecimal → 返回按类型转换的字符串。


变量类型​

表示法含义来源
${VALUE}当前标签的 raw 值驱动程序刚刚读取的值
${TAG_ID_X}其他标签的最后值LastValueMap (全局标签共用缓存)

LastValueMap 保存所有标签的最后 raw / 后处理值。不仅可以引用同一 OPC 的其他标签,还可以引用其他 OPC 的标签(前提是该标签已被采集过并存在于缓存中)。

变量不存在

如果引用了 ${TAG_ZERO},而 TAG_ZERO 尚未被采集过,则无法绑定值,从而引发异常。请缩短引用标签的 timecycle,或通过配置 auto_collect=true 使引用标签先被采集。(保存时的语法检查会允许这种情况 —— 这是采集顺序问题,而非公式错误。)


表达式语法​

写法与 Excel 相同。 函数名称和含义与 Excel 一致,因此几乎可以直接沿用 Excel 中的公式。不区分大小写(IF = if)。

类别运算符/函数备注
算术+, -, *, /, ^^ 为幂运算
比较>, <, >=, <=, =, <>= 表示相等, <> 表示不等(与 Excel 相同)
分组(, )括号优先
常量PI, E圆周率, 自然常数
条件IF, SWITCH, AND, OR, NOTIF(조건, 참일때, 거짓일때)
舍入ROUND, ROUNDUP, ROUNDDOWN, CEILING, FLOOR, INT, TRUNCROUND(값, 자리수)
数值ABS, SIGN, MOD, POWER, SQRT, CBRT, EXP, FACTMOD 的余数符号与被除数一致
聚合MIN, MAX, SUM, AVERAGE, COALESCE多个参数
对数LOG, LOG10, LNLOG = 常用对数(以10为底), LN = 自然对数
三角SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2以弧度为单位(与 Excel 相同)
双曲SINH, COSH, TANH, ASINH, ACOSH, ATANH
角度转换DEGREES, RADIANS弧度 ↔ 角度
不支持的功能
  • 位运算(&, |)
  • 用户自定义函数
  • 字符串运算 / 日期运算 如有此类需求,请在后续步骤(外部函数 / 后处理节点)中处理。
无效的计算公式无法保存

语法不正确的计算公式在保存时会被拒绝(UI 保存 / CSV 上传 / 备份恢复均适用)。 可以在标签设置对话框中输入示例值,预先查看计算结果。

计算过程中的失败(除零、引用值非数字等)会被记录为采集错误,并降低该标签的质量。不会用看似合理的数值来替代。

变量表示法​

同时使用 ${VALUE} 和简称 VALUE。其他标签也是如此 — ${TAG_ZERO} = TAG_ZERO。已保存的 ${...} 计算公式仍可正常工作。

${VALUE}*0.1 기존 표기
VALUE*0.1 같은 뜻
IF(VALUE>100, 100, VALUE*0.1) 엑셀식

丰富的示例集​

1. 简单缩放(×0.1)​

项目值
data_typeFloat
fomula${VALUE}*0.1
输入 → 输出16384 → 1638.4

用途:整数 raw → 保留一位小数的实数。当温度/压力/流量传感器发送整数值时使用。

2. 单位换算(mV → V)​

项目值
data_typeFloat
fomula${VALUE}/1000
输入 → 输出3300 → 3.3

3. 偏移修正(摄氏度 → 开尔文)​

项目值
data_typeFloat
fomula${VALUE}+273.15
输入 → 输出25 → 298.15

4. 引用其他标签(零点修正)​

项目值
data_typeFloat
fomula${VALUE}-${TAG_ZERO}
输入 → 输出VALUE=1024, TAG_ZERO=24 → 1000

TAG_ZERO 是其他标签的 ID。用于零点 / 去皮修正。

5. 多项式(平方)​

项目值
data_typeFloat
fomula${VALUE}*${VALUE}*0.001
输入 → 输出100 → 10.0

6. 函数 — 平方根​

项目值
data_typeFloat
fomulasqrt(${VALUE})
输入 → 输出144 → 12.0

7. 函数 — 三角(sin, 弧度)​

项目值
data_typeFloat
fomulasin(${VALUE})
输入 → 输出1.5708 (≈π/2) → 1.0

如果输入是角度(degree),请使用 sin(${VALUE}*pi/180)。

8. 多标签 — 校准(增益 × x + 偏移)​

项目值
data_typeFloat
fomula${VALUE}*${TAG_GAIN}+${TAG_OFFSET}
输入 → 输出VALUE=100, GAIN=0.05, OFFSET=2 → 7.0

这是一种按设备管理校准系数的模式,可将系数存储在单独的标签(或 HTTP-bind 标签)中。

9. 类型转换 / 强制转换为浮点数​

项目值
data_typeFloat
fomula${VALUE}*1.0
输入 → 输出123 → 123.0

仅想将 Integer 传入的值强制转换为 Float 时使用。

10. 幂 / 指数​

项目值
data_typeFloat
fomula${VALUE}^2
输入 → 输出5 → 25.0

11. 对数​

项目值
data_typeFloat
fomulalog(${VALUE})
输入 → 输出100 → 2.0

也可使用 ln(...) (自然对数)。

12. 复合 — RMS 的 dB 转换​

项目值
data_typeFloat
fomula20*log(${VALUE})
输入 → 输出1000 → 60.0

REST 注册示例(curl)​

单标签注册:

curl -X POST http://<edge-host>/api/v1/opc/OPC_LS_XBM_0001/tag \
-H "Content-Type: application/json" \
-d '{
"tag_id": "OPC_LS_XBM_0001_TAG_PRESS",
"tag_name": "Pressure (kPa)",
"plc_address": "D00100",
"data_type": "Float",
"format": "REAL",
"fomula": "${VALUE}*0.1",
"description": "스케일 ×0.1 적용"
}'

注册 OPC + 标签组时,在 tag_list[] 中放入 fomula 即可。

读取值(返回后处理后的值):

curl -s http://<edge-host>/api/v1/tag/OPC_LS_XBM_0001_TAG_PRESS/value | jq
# {
# "result": "OK",
# "data": {
# "tag_id": "OPC_LS_XBM_0001_TAG_PRESS",
# "value": "1638.4",
# ...
# }
# }

常见错误 + 解决方案​

消息 / 症状原因解决方案
계산식에 해당하는 변수값이 아직 캐시에 없습니다 : 계산식=[${VALUE}-${TAG_ZERO}]引用的标签(TAG_ZERO)从未被采集过先注册引用标签,并使用 auto_collect=true 运行一个周期
계산식이 참조하는 값이 숫자가 아닙니다引用标签的值不是数字(例如:引用了 data_type=String 类型的标签)修改为引用数字类型的标签
Closing brace not found / Missing second operand括号不匹配或运算符后缺少操作数保存时会被拒绝,请查看对话框的错误消息,它会指出具体位置
后处理未生效(返回原始值)data_type 是 String/Boolean将其改为 Float/Double/Integer/Long,或在需要后处理时使用单独的加工步骤
结果始终为 0raw 值实际上就是 0先用 GET /api/v1/tag/.../value 确认 raw 读取值。(旧解析器会将 ${VALUE}* 这类不完整的公式静默计算为 0,但现在这类公式在保存阶段就会被拒绝。)
采集错误 FORMAT_FAILED + 质量下降除以 0 或对 sqrt 开平方根等计算失败不保存 Infinity/NaN,而是以错误形式暴露。请在公式中添加保护逻辑 — 例如:IF(TAG_ZERO=0, 0, VALUE/TAG_ZERO)

操作备忘​

  • LastValueMap 是单例(内存中)。网关重启时会被初始化 → 第一个周期可能会无法引用其他标签。
  • 计算全程使用 BigDecimal,因此 Long/QWord 的大整数也能保留到最后一位(不会出现旧版 double 求值中的精度损失)。
  • 结果在 Double.toString() → 字符串缓存中。在界面 / Sparkplug 发布时,会再次转换为 data_type (请参考 Sparkplug DataTypeMapper)。
  • 如果同一 OPC 内的多个标签的公式相互引用,则采集顺序会导致第一个周期部分失败。这在运营上不是大问题(从下一个周期开始会恢复正常),但若想避免,请将引用目标标签放在保留周期更短的 timecycle 的独立 OPC 中。