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 为小数) |
执行流程
-
从驱动获取 raw 字符串值(例如
"16384")。 -
调用
PLCValueFomula.fomulaValue(address, value)。 -
若
address.getFomula()为空,则原样返回。 -
若
data_type不是 Float / Double / Integer / Long,则原样返回。 -
确认计算公式引用的名称 ——
VALUE为刚刚读取的值,其余为LastValueMap中其他标签的最后一次值。 -
若被引用标签尚未被采集过,则抛出异常:
Variable value referenced by the formula is not yet in cache : formula=[`<fomula>`], variable=[`<name>`] -
仅将计算公式引用的名称绑定为值后进行求值 →
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, NOT | IF(조건, 참일때, 거짓일때) |
| 舍入 | ROUND, ROUNDUP, ROUNDDOWN, CEILING, FLOOR, INT, TRUNC | ROUND(값, 자리수) |
| 数值 | ABS, SIGN, MOD, POWER, SQRT, CBRT, EXP, FACT | MOD 的余数符号跟随除数 |
| 聚合 | MIN, MAX, SUM, AVERAGE, COALESCE | 支持多个参数 |
| 对数 | LOG, LOG10, LN | LOG = 常用对数(底为 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_type | Float |
fomula | ${VALUE}*0.1 |
| 输入 → 输出 | 16384 → 1638.4 |
用途:整数 raw → 一位小数的实数。适用于温度 / 压力 / 流量传感器以整数形式发送值时。
2. 单位换算(mV → V)
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}/1000 |
| 输入 → 输出 | 3300 → 3.3 |
3. 偏移补偿(摄氏 → 开尔文)
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}+273.15 |
| 输入 → 输出 | 25 → 298.15 |
4. 引用其他标签(零点补偿)
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}-${TAG_ZERO} |
| 输入 → 输出 | VALUE=1024, TAG_ZERO=24 → 1000 |
TAG_ZERO 为其他标签的 ID。用于零点 / Tare 补偿。
5. 多项式(平方)
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}*${VALUE}*0.001 |
| 输入 → 输出 | 100 → 10.0 |
6. 函数 —— 平方根
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | sqrt(${VALUE}) |
| 输入 → 输出 | 144 → 12.0 |
7. 函数 —— 三角(sin,弧度)
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | sin(${VALUE}) |
| 输入 → 输出 | 1.5708(≈π/2) → 1.0 |
若输入为角度(degree),则使用 sin(${VALUE}*pi/180)。
8. 多标签 —— 校准(gain × x + offset)
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}*${TAG_GAIN}+${TAG_OFFSET} |
| 输入 → 输出 | VALUE=100, GAIN=0.05, OFFSET=2 → 7.0 |
这是将各设备的校正系数用独立标签(或 HTTP-bind 标签)管理的模式。
9. 类型转换 / 强制转为实数
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}*1.0 |
| 输入 → 输出 | 123 → 123.0 |
适用于只想把以 Integer 传入的值强制转换为 Float 的场景。
10. 幂 / 指数
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | ${VALUE}^2 |
| 输入 → 输出 | 5 → 25.0 |
11. 对数
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | log(${VALUE}) |
| 输入 → 输出 | 100 → 2.0 |
也可使用 ln(...)(自然对数)。
12. 复合 —— RMS 的 dB 换算
| 项目 | 值 |
|---|---|
data_type | Float |
fomula | 20*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 | 括号不配对,或运算符后缺少操作数 | 保存时即被拒绝,弹窗的错误消息会指出具体位置 |
| 后处理未生效(原样返回 raw) | data_type 为 String/Boolean | 改为 Float/Double/Integer/Long,或在需要后处理时使用独立的加工环节 |
结果始终为 0 | raw 值实际上就是 0 | 先用 GET /api/v1/tag/.../value 确认 raw read 值。(旧解析器会把 ${VALUE}* 这类不完整公式静默计算为 0,但现在这类公式在保存阶段即被拒绝。) |
采集错误 FORMAT_FAILED + 质量下降 | 除以 0 或 sqrt 为负数等计算失败 | 系统不会保存 Infinity/NaN,而是显式暴露为错误。请在公式中加入保护条件 —— 例如:IF(TAG_ZERO=0, 0, VALUE/TAG_ZERO) |
运行备注
LastValueMap为单例(in-memory)。网关重启时会被初始化 → 第一个周期中对其他标签的引用可能失败。- 计算全程使用
BigDecimal,因此Long/QWord的大整数也能保留到最后一位(不会出现旧double求值时的精度损失)。 - 结果为
Double.toString()→ 字符串缓存。在画面显示 / Sparkplug 发布时会再次转换为data_type(参见 SparkplugDataTypeMapper)。 - 若同一 OPC 内多个标签的 fomula 相互引用,则根据采集顺序,第一个周期可能部分失败。运行上问题不大(从下一个周期起恢复正常),但若想彻底避免,请将被引用标签放到
timecycle更短的独立 OPC 中。