Appearance
脚本引擎
功能说明
平台内置的采集/存储/告警能覆盖大部分标准需求,但总有非标场景——原始字节要复杂计算、只想存变化值省空间、下发指令要动态拼装、复杂多条件联动。为这些都做二次开发成本高。脚本引擎把这些环节开放成页面可写的 JavaScript 脚本,不改代码就能满足非标需求。
举例: Modbus 寄存器读回来是两个字节,要拼成一个 float 再除以 10,用“数据解析”写几行脚本即可,无需改平台源码。
菜单:脚本引擎
脚本引擎提供四种脚本,覆盖数据从采集、入库到指令下发、联动的各个环节。所有脚本均使用 JavaScript(ECMAScript 5.1 规范) 编写,编辑器支持方法自动补全。
| 页面 | 执行时机 | 用途 |
|---|---|---|
| 数据过滤 | 数据入库/转发前 | 按条件控制哪些数据写入数据库或转发 |
| 数据解析 | 采集上报后 | 对原始值做复杂计算、类型转换 |
| 功能解析 | 下发指令前 | 动态组装要下发的参数值 |
| 联动脚本 | 数据采集/上报或状态变化时 | 编写比场景联动更灵活的自动化逻辑 |
数据过滤
操作位置: 脚本引擎 → 数据过滤
数据过滤是内置的一组过滤器,平台自带两个:
| 过滤器 | 作用 |
|---|---|
| 数据入库 | 对采集后准备写入数据库的数据进行过滤 |
| MQTT 转发 | 对通过 MQTT 转发出去的数据进行过滤 |
每个过滤器通过 filter(data, methods) 函数判断单条数据是否放行,返回 true 放行、false 拦截。
操作步骤
- 进入 脚本引擎 → 数据过滤,在顶部切换到要配置的过滤器(数据入库 / MQTT 转发)
- 在左侧选择脚本语言页签,编写
filter函数 - 底部选择要启用的脚本语言,或选择"禁用"关闭该过滤器
- 点击"保存"
提示: 过滤器默认处于"禁用"状态(即不过滤、全部放行),只有选择语言并保存后脚本才会生效。
入口函数与示例
javascript
/**
* @param data 采集的数据或设备状态数据
* @param methods 过滤器提供的方法
* @return boolean true=入库/转发, false=丢弃
*/
function filter(data, methods) {
// 仅当属性值相比上次有变化时才入库,避免重复数据占用存储
return methods.isChange(data);
}常用方法
| 方法 | 说明 |
|---|---|
methods.isChange(data) | 值是否改变(与上次入库值对比) |
methods.isGeInterval(data, seconds) | 入库间隔是否 ≥ 指定秒数(用于降频存储) |
methods.isUid(data, uid) | 是否为指定设备 |
methods.isProduct(data, productCode) | 是否为指定产品 |
methods.isProtocol(data, protocolCode) | 是否为指定协议 |
methods.isRealtimeData(data) | 是否为实时数据 |
methods.isRealtimeStatus(data) | 是否为状态数据 |
data.getUid() / data.getValue() / data.getField() | 获取设备 UID / 点位值 / 点位属性 |
数据解析
操作位置: 脚本引擎 → 数据解析(新建解析器);物模型属性 → "值解析器"(绑定使用)
当设备上报的原始值需要经过复杂计算才能转为业务值时,先在此页面创建"值解析器"脚本,再到物模型属性的"值解析器"下拉中选用。
操作步骤
- 进入 脚本引擎 → 数据解析,点击"新增"
- 填写解析器名称,选择脚本语言,编写
resolver函数 - 点击"提交"保存
- 进入产品物模型,编辑目标属性,在"值解析器"下拉中选择刚创建的解析器并保存
提示: 在"数据解析"列表中点击"已绑定列表",可查看某个解析器当前被哪些产品的哪些模型属性使用。
入口函数与示例
javascript
/**
* @param value 采集的原始值
* @param model 模型属性配置
* @param methods 常用方法
* @return Object 解析后的值
*/
function resolver(value, model, methods) {
// 示例:字节数组按 float 解析并保留 1 位小数
if (methods.isBytes(value)) {
return methods.getFloat(value, 0).toFixed(1);
}
return value;
}常用方法
| 方法 | 说明 |
|---|---|
methods.addRealtimeData(model, field, value) | 为 group 类型属性动态增加子属性数据 |
methods.isAttrField(model, field) | 是否为指定字段的模型属性 |
methods.toHex(value) / methods.toBytes(value) | 值转十六进制 / 字节数组 |
methods.getInt/getShort/getLong/getFloat/getDouble(value, offset) | 从字节数组指定位置读取对应类型 |
methods.getString(value, offset, length) | 从字节数组读取指定长度字符串 |
methods.getLength(value) | 获取字节数组长度 |
methods.isNull(value) / methods.isNumber(value) / methods.isBytes(value) | 空值 / 数值 / 字节数组判断 |
典型用法: Modbus 寄存器解析、PLC 数据字拼接、多字节解码、进制转换等。
功能解析
操作位置: 脚本引擎 → 功能解析(新建解析器);功能模型下行配置(绑定使用)
下发指令时,如果参数需要动态组装(如 MQTT 下发需要拼装成完整 JSON),先在此页面创建脚本,再绑定到功能模型的下行配置项。
操作步骤
- 进入 脚本引擎 → 功能解析,点击"新增"
- 填写解析器名称,选择脚本语言,编写
write函数 - 点击"提交"保存
- 在功能模型的下行配置项中绑定该解析器
提示: 点击"已绑定列表"可查看该解析器被哪些产品的哪些功能接口使用。
入口函数与示例
javascript
/**
* @param data 调用功能接口的参数
* @param methods 常用方法
* 通过 methods.writeValue(data, 要写入的值) 写入下发数据
*/
function write(data, methods) {
if (methods.isProduct(data, 'mqtt_device')) {
// 组装 JSON 格式下发数据
var payload = '{"cmd":"' + methods.getApiCode(data)
+ '","value":"' + methods.getString(data, 'value') + '"}';
methods.writeValue(data, payload);
} else {
methods.writeValue(data, methods.getDouble(data, 'value'));
}
}常用方法
| 方法 | 说明 |
|---|---|
methods.writeValue(data, value) | 写入要下发的值(只支持基础数据类型) |
methods.getApiCode(data) | 获取调用的接口码 |
methods.getInt/getLong/getDouble/getBoolean/getString(data, key) | 获取下发参数对应类型的值 |
methods.getUid(data) | 获取执行设备 UID |
methods.isProduct(data, productCode) | 执行设备是否为指定产品 |
methods.isProtocol(data, protocolCode) | 执行设备是否为指定协议 |
methods.getDeviceSn(data) / methods.getParentDeviceSn(data) | 获取设备编号 / 父设备编号 |
methods.getDeviceConfig(data, key) | 获取设备的配置项 |
methods.getDeviceStatus(data) | 获取执行设备状态(online/offline) |
联动脚本
操作位置: 脚本引擎 → 联动脚本
联动脚本由采集/上报的数据或设备在线状态变化触发,可实现比场景联动更灵活的自动化逻辑。一个脚本对应一种控制模式,操作员无需写代码,在页面切换脚本即可切换整套逻辑。
每个联动脚本包含两个函数:
matcher()— 声明该脚本关注哪些产品的哪些属性,只有匹配的数据才会触发trigger()trigger(data, methods)— 数据匹配时执行的核心逻辑
操作步骤
- 进入 脚本引擎 → 联动脚本,点击"新增"
- 填写场景名称,选择脚本语言
- 在
matcher()中定义关注的产品与属性 - 在
trigger()中编写触发后的逻辑(调用指令、发送告警等) - 点击"提交"保存
入口函数与示例
javascript
/**
* 声明要监听的产品属性
* @return {productCode: [field | #status | *, ...]}
* field 要监听的属性字段
* #status 监听设备在线状态变化
* * 监听该产品的所有属性
*/
function matcher() {
return {th_sensor: ["temperature", "humidity"]};
}
/**
* @param data 采集/上报的数据
* @param methods 常用方法
*/
function trigger(data, methods) {
if (methods.isRealtimeData(data)) { // 数据采集或上报时触发
var uid = methods.getUid(data);
var temp = methods.getValue(uid, "temperature");
// 调用设备指令
methods.invoke("api->write_th_temp&uid->" + uid + "&value->" + temp);
} else { // 设备在线状态改变时触发
// ...
}
}常用方法
| 方法 | 说明 |
|---|---|
methods.invoke(cmd) / methods.invoke(cmd, uid) | 执行指令(设备 / 产品指令集) |
methods.asyncInvoke(cmd, uid) | 异步执行指令 |
methods.getValue(uid, field) / methods.getPrevValue(uid, field) | 获取字段最新值 / 上一个值 |
methods.getUid(data) | 当前触发的设备 UID |
methods.isChange(uid, field) | 设备属性值是否改变 |
methods.isGeInterval(uid, field, seconds) | 距上次执行间隔是否 ≥ 指定秒数 |
methods.isRealtimeData(data) / methods.isRealtimeStatus(data) | 实时数据 / 状态数据触发判断 |
methods.warn / fatalWarn / urgentWarn / noticeWarn / normalWarn(...) | 发送各级别告警 / 告警解除 |
典型应用场景
| 场景 | 说明 |
|---|---|
| 温度超限分级保护 | 连续多次超温才告警,防误报 |
| 产线计数满自动换箱 | 达到设定数量后调用指令驱动推杆 |
| PID 闭环调节 | 读取当前值和目标值,计算输出调节执行器 |
| 工艺模式切换 | 不同批次切换不同脚本即可切换整套控制逻辑 |
