Skip to content

概述

TDS脚本可以用于:

  • 对监控点数值进行二次计算和统计
  • 根据条件判断进行输出控制

脚本使用标准javascript编写,并且提供了一些内置函数,与TDS系统进行交互

脚本类型

设备脚本

在【系统组态】-【设备接入】页面中,可选择自定义脚本(即设备脚本)进行绑定,会自动执行脚本

执行方式说明
周期采集自动周期执行
控制输出根据条件判断进行输出控制
主动上送处理处理设备主动上送的数据

实例请看 使用内置脚本模拟第三方设备

在脚本管理中配置脚本

全局脚本

脚本模式功能描述
周期执行cyclic保存后开始轮询
监控点值改变时onMpValChange监控点值改变时自动执行
接口调用apiCall点击执行跑一次脚本

表达式脚本

当系统组态-监控对象-IO类型设置为【内部变量】时,可以通过其他监控点计算得出值,设置的【计算表达式】即为表达式脚本

配置用于计算该监控点实时值的表达式,自动周期执行

比如val("温度")/2 ,是由 科技大楼.一楼.温度 值 / 2 实时计算的

可在【脚本管理】-【表达式脚本状态监控】查看运行情况

环境位号

全局脚本/设备脚本的环境位号

全局脚本可以配置一个环境位号,默认环境位号为根节点。

配置环境位号后,所有脚本当中的位号,都是相对于该环境位号的相对位号。

例如配置某脚本环境位号为 A幢.1层.空调1#

那么脚本不需要写成:

javascript
output("A幢.1层.空调1#.开关",true)

写成,相对环境位号来指定位号

javascript
output("开关",true)

计算表达式的环境位号

计算表达式一般配置在某个监控点上,该表达式脚本的环境位号是该监控点的父节点。

例如有位号:

javascript
"A幢.1层.空调1#电量"      //可从设备中读取
"A幢.1层.空调2#电量"      //可从设备中读取
"A幢.1层.总电量"          //需要二次计算

可以给A幢.1层.总电量位号配置计算表达式,此时环境位号为 A幢.1层

javascript
val("空调1#电量") + val("空调2#电量")

计算表达式中使用相对位号,可以计算表达式在不同对象使用同一个配置,并可以进行配置的批量应用。

例如假设有位号

javascript
"A幢.2层.空调1#电量"      //可从设备中读取
"A幢.2层.空调2#电量"      //可从设备中读取
"A幢.2层.总电量"          //需要二次计算

此时 A幢.2层.总电量 使用和 A幢.1层.总电量 相同的计算脚本。

位号表达式

相对位号

相对位号是相对于当前环境位号的位号,例如环境位号为 A幢.1层.空调1#

默认表达式都是相对于该位号的位号,例如:

位号表达式对应的绝对位号
开关A幢.1层.空调1#.开关
湿度A幢.1层.空调1#.湿度

上级位号

tds使用 ^ 符号表示上一级对象树节点:

位号表达式对应的绝对位号
^A幢.1层
^.空调2#A幢.1层.空调2#
^.空调2#.开关A幢.1层.空调2#.开关
^^A幢
^^.2层.空调1#A幢.2层.空调1#

绝对位号

:: 开头的位号,表示绝对位号,假设当前脚本的环境位号为A幢.1层.空调1#

那么使用 ::A幢.2层.空调1#.温度 来引用环境位号范围外的位号

局部全局统一查找

可以使用A幢.2层.空调1#.温度来表示全局位号,忽略::符号,但是会优先匹配环境位号下的位号 A幢.1层.空调1#.A幢.2层.空调1#.温度,匹配不到,才会去匹配全局位号

无效计算表达式

表达式脚本中如果使用了val函数,该函数返回null时,将不会生成计算结果。

例如,需要计算今天的用电量 val("总电量") - val("总电量","today","first"),使用当前值减去今天的第一个值,

如果今天没有采集到过任何数据,后面那个val函数返回null

前面的val函数返回最新值,可能是昨天采集的

那么该二次计算表达式将不返回计算结果

数据服务交互函数

sum 求和函数

javascript
sum(tag, invalidAsZero)
参数值类型说明
tagstring / json参考位号选择器
invalidAsZerobool无效值做为0处理,否则遇到无效值,将无法计算最终结果,返回null

avg 求平均函数

javascript
avg(tag)
参数值类型说明
tagstring / json参考位号选择器,比如使用 *.温度 求整个项目中的温度的平均值

val 取位号值函数

取实时值

如果该位号是一个监控点且为数字型或者布尔型,返回当前值。其他情况返回null

javascript
val("三楼.温度")

取历史值

取某个指定时间的历史值

javascript
val("三楼.温度","2020-02-02 11:30:00")

使用时间和聚合参数配置取历史值

取本月的第一个数值

javascript
val("三楼.温度","this-month","first")

参数说明:

参数值类型是否必填说明
tagstring必填精确指定的位号。 不支持位号选择器多位号选择
timestring可选精确指定的时间。参考时间选择器的精确指定模式。

第三个参数可选,取值有多种方式

参数值类型说明
aggregatestringtime参数指定一个范围,则必须指定aggregate参数,聚合成为1个返回值
maxstring取最大值
minstring取最小值
avgstring取平均值
sumstring取和
firststring取最早值
diffstring求最大与最小值之差
diff.last-firststring分组最后一条记录 减去 第一条记录的值
diff.first-laststring分组第一条记录 减去 最后一条记录的值
laststring取最晚值
countstring统计数量

output 输出到设备

该函数一般用于设备控制输出

javascript
//输出一个 模拟量
output("三楼.空调.设定温度",27.3);

//输出一个开关量
output("三楼.智能空开.开关",true);

//输出一个枚举值
//枚举值的具体含义在监控点属性当中配置
output("三楼.空调.运行模式","制热")

input 数据输入

一般用于进行二次计算之后,将计算的结果输入到系统

javascript
//取到原始值
var pUp = val("三楼.某设备.上压力");
var pDown = val("三楼.某设备.下压力");
//根据某种计算公式,计算出实际值
var k = (0.87*pUp + 3)/(0.45*pDown + 5);
//输入到某个位号
input("三楼.某设备.压力系数",k);

log 打印日志

可以在脚本编辑下方的输出窗口看到打印信息,一般用于脚本调试

javascript
var a = 1;
var b = 1;
var c = a + b;
log("c = " + c); 
console.log("c = " + c); //这行代码和上面是等效的,考虑到编码习惯,上下两种都支持

将日志打印到 tds 的日志和服务程序命令行窗口

javascript
console.log("log to tds logfile",true); //第二个参数设置为true

一般用于进行二次计算之后,将计算的结果输入到系统

db是一个内置的全局变量,可以调用相关数据库操作

javascript
db.insert("温控器.温度","2023-12-25 11:30:00",24.5); //插入浮点
db.insert("温控器.开关","2023-12-25 11:30:00",true); //插入布尔
db.insert("温控器.模式","2023-12-25 11:30:00",1);    //插入整形

time函数

返回当前时间对象TIME,支持increaseSeconds对时间进行加减

支持toStr(),fromStr 函数与字符串互转

javascript
var t = time();
t.fromStr("2023-01-01 00:00:00");
t.increaseSeconds(10);  // 2023-01-01 00:00:10
log(t.toStr());
t.increaseSeconds(10);  // 2023-01-01 00:00:20
log(t.toStr());

http.request 同步http请求

目前tds脚本当中所有的脚本执行都是同步的,不使用回调函数

以下演示通过脚本,调用http请求监控点数据

javascript
//http的请求参数
var opt = {
  hostname: 'localhost',
  port: 667, 
  path: '/rpc', 
  method: 'POST'
};

//http是tds脚本环境的内置对象,使用http.request函数发起http请求
//http.request是一个同步函数,无需设置回调函数,阻塞代码运行
var resp = http.request(opt); 

if(resp!=null){
    console.log(resp.body);
}
else{
    console.log("请求失败");
}

sleep 睡眠

用于延时控制等,单位毫秒 以下示例表示当温度小于20度,延时4秒打开空调开关

javascript
if(val("三楼.温度") < 20){
    sleep(4000);
    output("三楼.空调.开关",true);
}

setReturn设置返回值

某些脚本需要设置返回值,比如 控制输出脚本,需要设置

rpc的output函数的返回

JSON.stringify 格式化json字符串

javascript
var a = 1;
var s = "123";
a = JSON.stringify(a);
s = JSON.stringify(s);

STR.toHexStr 转为16进制字符串

javascript
var arr = [1,2,3,20];
var s = STR.toHexStr(arr);
s == "01 02 03 14"

设备IO脚本函数

IO脚本中,外部会传递环境变量到脚本,在脚本中可以直接调用,环境变量都是用大驼峰模式

Dev 当前设备

属性类型说明
Dev.addrobject
Dev.addr.ipstring设备IP地址
Dev.addr.portstring设备端口
Dev.addr.idstring设备id
Dev.childrenobject如果当前设备是网关设备,可以访问设备子设备

Dev.input(val,chanAddr)

参数值类型说明
valany输入值
chanAddrstring通道地址

Dev.doTransaction(req)

javascript
//同时支持以下两种参数格式
Dev.doTransaction([0x01,0x01])
Dev.doTransaction("01 01")
参数值类型说明
requint8 array请求二进制数据包,字节数组

RecvData 当前接收到的设备主动上送数据

数据接收脚本支持

uint8 array 收到的二进制数据包,字节数组

Output 当前数据输出命令

属性说明
Output.val输出值
Output.chan输出通道

Req 当前TDSP请求

TDSP请求脚本支持

Req符合jsonRPC格式,格式用户自定义,并在脚本中实现rpc到自定义协议格式的转换

json
{
    "method":"setTempLimit",
    "params":{
        "highLimit": 30,
        "lowLimit" : 20
    }
}

使用内置脚本模拟第三方设备

http脚本

仓库地址:https://svn.liangtusoft.com:8443/demo-conf

运行仿真程序 httpServer.js

找到【设备接入-扩展IO-内置脚本模式】文件夹

使用nodejs运行设备仿真工具 testServer/httpServer.js

该测试工具内置了http文件服务器,打开测试界面 http://127.0.0.1:30001/ 测试http接口协议

通过 /get 接口可以获取到设备的当前所有实时值

使用 /set 接口设置某个参数,可以同时设置多个参数

配置监控对象树

名称对象层级IO类型
测试项目
科技大楼组织结构
一楼监控对象
温度(当前温度)监控点
湿度监控点
开关监控点(布尔型)输入/输出 (来自硬件数据采集,支持控制输出)
设定温度(空调)监控点输入/输出 (来自硬件数据采集,支持控制输出)

添加设备

在tds的io配置界面新增一个【自定义设备】,设置地址模式为【http服务端】,并配置为仿真设备的对外服务 ip:127.0.0.1 和端口:30001

将设备内的数据抽象为4个通道,此处根据不同的设备协议,需要自主设计不同的通道抽象方法

每个通道都需绑定监控点

添加完设备,在node脚本运行的情况下,可以看到设备上线

添加全局脚本

进入脚本管理,添加2个【设备脚本】,一个负责周期采集,一个负责控制输出

HTTP设备_周期采集脚本

javascript
// http的请求参数
const res = http.request({
    hostname: 'localhost',
    port: 30001, //仿真设备的服务端口
    path: '/get', //获取当前所有实时数据的接口
    method: 'POST'
});

console.log(JSON.stringify(res));

//http是tds脚本环境的内置对象,使用http.request函数发起http请求
//http.request是一个同步函数,无需设置回调函数,阻塞代码运行
if (res && res.body) {
    // Dev 是 tds脚本内置对象,具有 setOnline,setOffline,input函数
    Dev.setOnline();
    const resObj = JSON.parse(res.body);
    Dev.input(resObj.temp, "temp"); //第一个参数是输入值,第二个参数是通道地址
    Dev.input(resObj.humi, "humi");
    Dev.input(resObj.power, "power");
    Dev.input(resObj.tempSet, "tempSet");
} else {
    Dev.setOffline();
    console.log('请求失败')
}

HTTP设备_控制输出脚本

javascript
console.log(JSON.stringify(Dev.addr));
var token = Dev.getDevVar("token");
console.log(token);

var setParams = {
}

//当脚本被设置为设备的控制输出脚本时,脚本会有一个 Output 内置对象
//Output.val 是输出值, Output.chan 是输出通道地址
setParams[Output.chan] = Output.val;
console.log(JSON.stringify(setParams));
var s = JSON.stringify(setParams);
var opt = {
  hostname: Dev.addr.ip,
  port: Dev.addr.port,
  path: '/set',
  method: 'POST',
  body: s,
  headers:{
      "token":token,
      "user":"admin"
  }
};

var resp = http.request(opt);
var ret = {};
if(resp!=null){
    console.log(resp.body);
    Dev.setOnline();
    ret["result"] = "ok";
}
else{
    Dev.setOffline();
    ret["error"] = "请求失败";
}

// 控制输出脚本使用该函数返回执行信息,返回是否输出成功
setReturn(ret);

环境变量脚本

javascript
var output = {
    chan: "tempSet",
    val: 37.3
}

勾选环境变量时会出现一个新的编辑框在右侧,作为正文脚本的上下文调试运行

当【HTTP设备_控制输出脚本】被绑定到设备时,环境变量脚本会被忽略,由设备提供数据

设备绑定自定义脚本

进入到【自定义设备】的编辑页面,设置【周期采集】和【控制输出】使用的脚本。下拉框中,可以选择在脚本管理中预先编辑好的脚本

设置控制输出命令的返回信息,第三方api一般是http请求,为了将自定义脚本的执行结果返回给tds,在tds脚本当中使用 setReturn函数,将一个错误信息对象返回给tds

当设置 ret["result"]="ok"时,tds可以获得成功信息,如图点击开关进行开关输出,弹出成功信息

关闭设备仿真程序,模拟设备不在线时,执行控制输出脚本的效果,返回的错误信息就是在脚本中设置的ret["error"] 中的信息

UDP脚本

使用nodejs运行设备仿真工具 testServer/udpServer.js,文件目录和 httpServer.js同级

监控对象复制一楼的数据后,粘贴改名为二楼

接入设备

添加设备脚本

javascript
var req = [0x01,0x01];

var resp = Dev.doTransaction(req);

if(resp!=null){
    console.log(resp);

    Dev.input(resp[3],String(resp[2]));
    Dev.input(resp[5],String(resp[4]));
    Dev.setOnline();
}
else{
    Dev.setOffline();
}

点击调试运行向node仿真程序发送数据

在【点位列表】可以看到更新的数据

TCP 脚本

使用nodejs运行设备仿真工具 testServer/tcpServer.js,文件目录和 httpServer.js同级

添加tcp设备

TCP设备-周期采集
javascript
var req = [0x01,0x01];

var resp = Dev.doTransaction(req);

if(resp!=null){
    console.log(resp);

    Dev.input(resp[3],String(resp[2]));
    Dev.input(resp[5],String(resp[4]));
    Dev.setOnline();
}
else{
    Dev.setOffline();
}

TCP设备-控制输出

javascript
//01 02 03 30  
//地址 01
//命令码 02 表示输出
//通道 03 
//输出值 30

var req = [01,02,03,30];
var resp = Dev.doTransaction(req);

console.log(resp)
var ret = {};

if(resp){
    console.log("输出成功");
    ret["result"] = "ok";
} else {
    ret["error"] = "请求失败";
}

setReturn(ret);