引言
在工业物联网和智能制造领域,我们每天都在与海量的传感器数据打交道。电压、电流、温度、压力、流量……这些原始数据往往需要经过计算才能得到真正有价值的业务指标。比如:
- 通过电压和电流计算功率
- 通过长宽高计算体积
- 通过多个温度传感器计算平均温度
- 通过速度和时间计算距离
传统的做法是什么呢?要么在采集端写代码处理,要么在数据入库后写复杂的SQL查询。这不仅需要专业的编程技能,而且每次业务需求变化都要修改代码、测试、部署,周期长、成本高、容易出错。
现在,IDMP 的公式表达式功能彻底改变了这一切! 无需编写一行代码,只需在界面上输入简单的数学公式,系统就能自动完成复杂的数据计算,还能智能处理计量单位的转换。
功能亮点
1. 像 Excel 一样简单
还记得在 Excel 中输入 =A1+B1 这样的公式吗?IDMP 的公式表达式就是这么简单!
示例:计算电器功率
假设您有一个智能电表,采集了电压和电流数据。要计算功率,只需:
- 创建一个新属性”功率”
- 选择”数据引用类型”为”公式”
- 输入公式:
${attributes['电压']} * ${attributes['电流']} - 点击”评估”按钮预览结果
- 保存
就这么简单!系统会自动:
- 识别电压和电流是来自数据库的实时数据
- 在查询时自动将公式转换为高效的数据库查询
- 实时计算每一条记录的功率值

2. 智能计量单位转换
这是 IDMP 公式表达式最强大的功能之一!在工业场景中,不同设备、不同时期采集的数据可能使用不同的计量单位,这给数据分析带来了巨大挑战。
IDMP 能自动帮您做什么?
场景一:自动单位转换
您有两个电流传感器:
- 传感器A:单位是毫安(mA)
- 传感器B:单位是安培(A)
想要计算总电流:传感器A + 传感器B
传统做法:您需要手动计算转换系数(1A = 1000mA),写出 `传感器A + 传感器B * 1000`
IDMP 做法:直接写 `${attributes[‘电流mA’]} + ${attributes[‘电流A’]}`
系统自动识别:
- 两个都是电流(同一类物理量)
- 但单位不同(mA vs A)
- 自动转换:将第二个值乘以1000
- 生成正确的计算SQL
结果:您不用操心单位转换,系统自动搞定!

单位转换的具体规则请参考官方文档(暂未发布):
场景二:智能单位推导
当您进行乘除运算时,系统能自动推导出结果的单位。
实际案例:计算体积
公式:${attributes['长度cm']} * ${attributes['宽度m']} * ${attributes['高度m']}
系统会自动:
- 识别长度单位是厘米(cm),需要先转换为米(m)
- 计算:长度×宽度×高度
- 推导结果单位:米×米×米 = 立方米(m³)
- 在属性界面上显示”结果单位:立方米”
如果您设置属性的显示单位是”立方厘米”,系统还会自动再转换一次!


场景三:错误提前发现
如果您不小心写了一个单位不兼容的公式,系统会立即提醒您:
错误公式:${attributes['电流']} + ${attributes['电压']}
点击”评估”按钮后,系统提示:
❌ 错误:操作符'+'不能应用于不同的计量单位分类:'电流'和'电压'
这就避免了错误的计算进入生产环境!

3. 支持复杂的嵌套计算
公式不仅可以引用原始数据,还可以引用其他公式的结果,实现多层嵌套计算。
实际案例:能效分析
假设您要分析工厂的能效,需要多步计算:
步骤1 - 总功率:
公式:${attributes['设备1功率']} + ${attributes['设备2功率']} + ${attributes['设备3功率']}
步骤2 - 日耗电量:
公式:${attributes['总功率']} * 24
步骤3 - 能效比:
公式:${attributes['产量']} / ${attributes['日耗电量']}
每个公式都可以引用前面公式的结果,就像搭积木一样构建复杂的计算逻辑。
系统保护机制:
- 自动检测循环引用(A引用B,B又引用A)
- 限制嵌套深度(默认最多5层),防止性能问题
- 清晰的错误提示,帮您快速定位问题
4. 丰富的函数库
IDMP 公式支持 TDengine 的所有标量函数,包括但不限于:
– 数学函数:`ABS()`, `SQRT()`, `POW()`, `LOG()`, `SIN()`, `COS()`, `TAN()` 等
– 字符串函数:`CONCAT()`, `SUBSTR()`, `LENGTH()`, `UPPER()`, `LOWER()` 等
– 聚合函数:`AVG()`, `SUM()`, `MAX()`, `MIN()`, `COUNT()` 等
– 时间函数:`NOW()`, `TIMETRUNCATE()`, `TIMEDIFF()` 等
示例:
ABS(${attributes['温度']} - 25)
SQRT(POW(${attributes['x']}, 2) + POW(${attributes['y']}, 2))
温度异常检测
公式:ABS(${attributes['当前温度']} - ${attributes['目标温度']}) > 10
温度偏差超过10度时,结果为真(1),可以用于告警判断。
5. 实时预览和调试
在保存公式之前,您可以随时点击”评估”按钮查看计算结果:

评估功能的好处:
– 即时反馈:不用保存就能看到结果
– 单位提示:系统告诉您结果的单位是什么
– 自动填充:如果属性还没设置单位,系统会自动填充推导出的单位,作为属性的 UOM 配置。
– 错误定位:清楚地告诉您哪里出错了
示例:公式结果的计量单位和属性上已经配置的计量单位不属于相同的计量单位分类。

实际应用场景
场景1:环保设备监控
背景:污水处理厂需要监控多个环保指标,不同指标使用不同的单位。
需求:
- 监控进水和出水的污染物浓度
- 计算污染物去除率
- 计算处理效率
- 确保所有指标符合环保标准
IDMP 解决方案:
# 污染物处理效率
属性1:进水COD(mg/L) - 来自数据库
属性2:出水COD(mg/L) - 来自数据库
属性3:COD去除率(%) - 公式:
(${attributes['进水COD']} - ${attributes['出水COD']}) / ${attributes['进水COD']} * 100
属性4:是否达标 - 公式:
${attributes['出水COD']} <= 50
场景2:生产设备能效管理
背景:制造企业有多条生产线,需要精确计算每条生产线的能效。
需求:
- 计算设备的实时功率
- 统计生产周期内的总能耗
- 计算单位产品能耗
- 对比不同生产线的能效
IDMP 解决方案:
# 生产线能效分析
属性1:电机功率(kW) - 来自数据库
属性2:照明功率(W) - 来自数据库
属性3:总功率(kW) - 公式:
${attributes['电机功率']} + ${attributes['照明功率']} / 1000
属性4:生产周期(小时) - 来自数据库
属性5:总能耗(kWh) - 公式:
${attributes['总功率']} * ${attributes['生产周期']}
属性6:产量(件) - 来自数据库
属性7:单位能耗(kWh/件) - 公式:
${attributes['总能耗']} / ${attributes['产量']}
技术优势
虽然这篇文章主要面向非技术人员,但了解一些技术原理能帮助您更好地理解这个功能为什么如此强大。
为什么这么快?
IDMP 采用了”解析在应用层,计算在数据库”的架构:

优势:
- 充分利用数据库性能:TDengine 专为时序数据设计,计算速度极快
- 减少数据传输:计算在数据库完成,只返回结果,不需要传输大量原始数据
- 支持历史数据:可以对海量历史数据执行相同的计算
- 自动优化:数据库会自动优化查询性能
为什么单位转换这么智能?
IDMP 采用了类似国际单位制(SI)的设计理念:

工作原理:
- 系统内置7个基本物理量
- 所有其他单位都用基本单位的组合表示
- 计算时自动转换为基本单位
- 根据基本单位的组合推导结果单位
- 自动查找系统中匹配的单位类别
举例:
- 速度 = 长度¹ × 时间⁻¹
- 加速度 = 长度¹ × 时间⁻²
- 力 = 质量¹ × 长度¹ × 时间⁻²
- 功率 = 质量¹ × 长度² × 时间⁻³
当您计算”电压 × 电流”时,系统通过基本单位组合自动推导出结果是”功率”!
除了内置的基本物理量,IDMP 还支持自定义计量单位,对于自定义的计量单位同样可以自动匹配。
为什么表达式解析这么可靠?
IDMP 使用了 ANTLR 这个业界标准的语法解析工具:
优势:
– 语法严格:像编程语言一样严格,不会产生歧义
– 错误精确:能准确指出错误的位置和原因
– 扩展性强:轻松添加新的运算符和函数
– 性能优异:解析速度快,支持复杂表达式
这就是为什么 IDMP 能给您清晰的错误提示,而不是”表达式错误”这种模糊的信息。
语法定义分为两个文件:
词法分析器(FormulaLexer.g4):定义 Token 类型
lexer grammar FormulaLexer;
// 运算符
PLUS: '+';
MINUS: '-';
MULTIPLY: '*';
DIVIDE: '/';
LPAREN: '(';
RPAREN: ')';
COMMA: ',';
// 比较运算符
EQ: '=';
NEQ: '<>' | '!=';
GT: '>';
LT: '<';
GTE: '>=';
LTE: '<=';
// 位运算符
BIT_OR: '|';
BIT_AND: '&';
// 数字字面量
NUMBER: [0-9]+ ('.' [0-9]+)?;
// 函数名
FUNCTION: [A-Za-z_][A-Za-z0-9_]*;
// 占位符 ${...}
PLACEHOLDER: '${' (~[}])+ '}';
// 空白字符
WS: [ \t\r\n]+ -> skip;
语法分析器(FormulaParser.g4):定义表达式的语法规则和优先级
parser grammar FormulaParser;
options {
tokenVocab = FormulaLexer;
}
// 根规则
formula: expression EOF;
// 表达式层次(从低优先级到高优先级)
// 1. 比较运算符(最低优先级)
expression
: bitwiseExpression ((EQ | NEQ | GT | LT | GTE | LTE) bitwiseExpression)*
;
// 2. 位运算符
bitwiseExpression
: addSubExpression ((BIT_OR | BIT_AND) addSubExpression)*
;
// 3. 加减法
addSubExpression
: term ((PLUS | MINUS) term)*
;
// 4. 乘除法
term
: factor ((MULTIPLY | DIVIDE) factor)*
;
// 5. 因子(处理括号、数字、占位符、函数,最高优先级)
factor
: NUMBER # numberFactor
| PLACEHOLDER # placeholderFactor
| FUNCTION LPAREN argumentList? RPAREN # functionFactor
| LPAREN expression RPAREN # parenFactor
| MINUS factor # unaryMinusFactor
;
// 函数参数列表
argumentList
: expression (COMMA expression)*
;
这个语法定义清晰地表达了运算符的优先级:
- 比较运算符(
=,<>,>,<,>=,<=)- 最低优先级 - 位运算符(
|,&) - 加减法(
+,-) - 乘除法(
*,/) - 一元负号、括号、函数、字面量 – 最高优先级
内部我们实现了访问者模式,访问者为每种 AST 节点类型提供了相应的处理方法:

工作流程

常见问题解答
Q1:公式中可以使用哪些属性?
A:您可以引用同一个元素下的任何属性,包括:
- 来自数据库的实时数据属性
- 静态属性(固定值)
- 其他公式属性(嵌套引用)
暂时不支持跨元素引用(这个功能在规划中)。
Q2:公式的计算会影响性能吗?
A:不会!因为计算是在 TDengine 数据库中完成的,而 TDengine 专为高性能时序数据处理设计。实际上,使用公式往往比传统的应用层计算更快。
Q3:修改公式后,历史数据会重新计算吗?
A:是的!因为公式不存储计算结果,而是在查询时实时计算。所以修改公式后,查询历史数据时会用新公式计算。
Q4:如果属性没有设置单位怎么办?
A:没有单位的属性可以参与计算,系统会尽可能推导结果的单位。但建议为所有物理量设置正确的单位,这样能获得更好的单位检查和转换。
Q5:公式能嵌套多少层?
A:默认最多5层嵌套。这个限制是为了防止过于复杂的公式影响性能。如果您需要更多层级,可以联系系统管理员调整配置。



























