Skip to content

脚本引擎

功能说明

平台内置的采集/存储/告警能覆盖大部分标准需求,但总有非标场景——原始字节要复杂计算、只想存变化值省空间、下发指令要动态拼装、复杂多条件联动。为这些都做二次开发成本高。脚本引擎把这些环节开放成页面可写的 JavaScript 脚本,不改代码就能满足非标需求。

举例: Modbus 寄存器读回来是两个字节,要拼成一个 float 再除以 10,用“数据解析”写几行脚本即可,无需改平台源码。

菜单:脚本引擎

脚本引擎提供四种脚本,覆盖数据从采集、入库到指令下发、联动的各个环节。所有脚本均使用 JavaScript(ECMAScript 5.1 规范) 编写,编辑器支持方法自动补全。

页面执行时机用途
数据过滤数据入库/转发前按条件控制哪些数据写入数据库或转发
数据解析采集上报后对原始值做复杂计算、类型转换
功能解析下发指令前动态组装要下发的参数值
联动脚本数据采集/上报或状态变化时编写比场景联动更灵活的自动化逻辑

数据过滤

操作位置: 脚本引擎 → 数据过滤

数据过滤是内置的一组过滤器,平台自带两个:

过滤器作用
数据入库对采集后准备写入数据库的数据进行过滤
MQTT 转发对通过 MQTT 转发出去的数据进行过滤

每个过滤器通过 filter(data, methods) 函数判断单条数据是否放行,返回 true 放行、false 拦截。

操作步骤

  1. 进入 脚本引擎 → 数据过滤,在顶部切换到要配置的过滤器(数据入库 / MQTT 转发)
  2. 在左侧选择脚本语言页签,编写 filter 函数
  3. 底部选择要启用的脚本语言,或选择"禁用"关闭该过滤器
  4. 点击"保存"

提示: 过滤器默认处于"禁用"状态(即不过滤、全部放行),只有选择语言并保存后脚本才会生效。

入口函数与示例

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 / 点位值 / 点位属性

数据解析

操作位置: 脚本引擎 → 数据解析(新建解析器);物模型属性 → "值解析器"(绑定使用)

当设备上报的原始值需要经过复杂计算才能转为业务值时,先在此页面创建"值解析器"脚本,再到物模型属性的"值解析器"下拉中选用。

操作步骤

  1. 进入 脚本引擎 → 数据解析,点击"新增"
  2. 填写解析器名称,选择脚本语言,编写 resolver 函数
  3. 点击"提交"保存
  4. 进入产品物模型,编辑目标属性,在"值解析器"下拉中选择刚创建的解析器并保存

提示: 在"数据解析"列表中点击"已绑定列表",可查看某个解析器当前被哪些产品的哪些模型属性使用。

入口函数与示例

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),先在此页面创建脚本,再绑定到功能模型的下行配置项。

操作步骤

  1. 进入 脚本引擎 → 功能解析,点击"新增"
  2. 填写解析器名称,选择脚本语言,编写 write 函数
  3. 点击"提交"保存
  4. 在功能模型的下行配置项中绑定该解析器

提示: 点击"已绑定列表"可查看该解析器被哪些产品的哪些功能接口使用。

入口函数与示例

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) — 数据匹配时执行的核心逻辑

操作步骤

  1. 进入 脚本引擎 → 联动脚本,点击"新增"
  2. 填写场景名称,选择脚本语言
  3. matcher() 中定义关注的产品与属性
  4. trigger() 中编写触发后的逻辑(调用指令、发送告警等)
  5. 点击"提交"保存

入口函数与示例

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 闭环调节读取当前值和目标值,计算输出调节执行器
工艺模式切换不同批次切换不同脚本即可切换整套控制逻辑

下一步