跳到主要内容

Formula(计算公式,fomula)使用指南

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

项目
实现类plantpulse.app.edge.component.collector.plc.PLCValueFomula
调用位置PLCValueReader.read() 的第 3 步(raw → format → fomula → 缓存更新)
依赖库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 上传 / 备份恢复均适用)。 可在标签设置弹窗中输入示例值,预先确认计算结果。

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

变量写法

${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
输入 → 输出163841638.4

用途:整数 raw → 一位小数的实数。适用于温度 / 压力 / 流量传感器以整数形式发送值时。

2. 单位换算(mV → V)

项目
data_typeFloat
fomula${VALUE}/1000
输入 → 输出33003.3

3. 偏移补偿(摄氏 → 开尔文)

项目
data_typeFloat
fomula${VALUE}+273.15
输入 → 输出25298.15

4. 引用其他标签(零点补偿)

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

TAG_ZERO 为其他标签的 ID。用于零点 / Tare 补偿。

5. 多项式(平方)

项目
data_typeFloat
fomula${VALUE}*${VALUE}*0.001
输入 → 输出10010.0

6. 函数 —— 平方根

项目
data_typeFloat
fomulasqrt(${VALUE})
输入 → 输出14412.0

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

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

若输入为角度(degree),则使用 sin(${VALUE}*pi/180)

8. 多标签 —— 校准(gain × x + offset)

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

这是将各设备的校正系数用独立标签(或 HTTP-bind 标签)管理的模式。

9. 类型转换 / 强制转为实数

项目
data_typeFloat
fomula${VALUE}*1.0
输入 → 输出123123.0

适用于只想把以 Integer 传入的值强制转换为 Float 的场景。

10. 幂 / 指数

项目
data_typeFloat
fomula${VALUE}^2
输入 → 输出525.0

11. 对数

项目
data_typeFloat
fomulalog(${VALUE})
输入 → 输出1002.0

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

12. 复合 —— RMS 的 dB 换算

项目
data_typeFloat
fomula20*log(${VALUE})
输入 → 输出100060.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括号不配对,或运算符后缺少操作数保存时即被拒绝,弹窗的错误消息会指出具体位置
后处理未生效(原样返回 raw)data_type 为 String/Boolean改为 Float/Double/Integer/Long,或在需要后处理时使用独立的加工环节
结果始终为 0raw 值实际上就是 0先用 GET /api/v1/tag/.../value 确认 raw read 值。(旧解析器会把 ${VALUE}* 这类不完整公式静默计算为 0,但现在这类公式在保存阶段即被拒绝。)
采集错误 FORMAT_FAILED + 质量下降除以 0sqrt 为负数等计算失败系统不会保存 Infinity/NaN,而是显式暴露为错误。请在公式中加入保护条件 —— 例如:IF(TAG_ZERO=0, 0, VALUE/TAG_ZERO)

运行备注

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