下面的函数主要用在ds脚本中,通过平台提供的脚本测试工具可以在线运行测试脚本:
在线运行测试脚本
ds脚本由服务器解析执行,可用在下面几个地方:
只要是服务器中的ds后缀文件,加载时会执行脚本内容。
模板事件指实例节点在节点值变化后会执行脚本,脚本中可以使用几个特色的变量:
this -- 当前节点值
$$.value -- 当前节点新值
$$.time -- 当前节点新值关联的时间点
this.parent -- 当前节点上级节点值
this.parent.send -- 同上级的节点(如send)值
this.root -- 根节点值
this.root._id -- 获取的是该实例的id
this.root.data -- 根节点下的节点(如data)值
this.$old -- 节点的变化前的旧值
this.$path -- 节点全路径(如:/admin/test2/222.code)
this.$id -- 节点全id(如:/63dbc6a5a5a79a124cc750e5.code)
this.$name -- 节点名称(如:code)
支持C#包含的常用函数:查看所有C#支持函数
也可以通过下面这个函数获取到所有支持的操作函数:
functions()
字符串替换函数
$("aa{0}{1}",参数1,参数2)
字符串表达式
a=1;
b=2;
return `我是变量 ${a+b}`
将一个对象转成json字符串
将一个json字符串转成对象
将一个对象转成整数
将一个对象转成字符串
将一个对象转成数值,包含小数
将一个字符串转成时间,可以有多种参数:
date("2022-11-11 11:11:11",false)//可以有两个参数,第二个参数为true,表示本地,false表示UTC时间,默认为false, 也就是UTC时间
date(2022,11,11,11,11,11,false)//大于2个参数的时候,就是年月日时分秒,以及是否本地时间,同上
将一个UTC时间转成服务器上的本地时间
local(now())
将一个UTC时间转成时间戳
timestamp(now())
将一个日期时间格式化为字符串
dateformat(now(),"yyyyMMdd HH:mm:ss")
//这个等同于tostring:
now().tostring("yyyyMMdd HH:mm:ss")
将一个对象转成字节数组
如果是字符串,有2种字节表现:
//base64
bytes("11111",true)
//ascii
bytes("11111",false)
将一个字节数组重新排列,一般用于改变高低位
//第二个参数是重组的索引数组
bytes.change([1,2,3,4],[2,3,0,1])
结果:[3,4,1,2]
将一个对象转成字节
任意编码的二进制转换
encoding("gb2312").getstring(bin("C4E3BAC3"))
encoding("utf-8").getbytes("test");
自动判断变量,如果字符串就转成二进制,否则字符串
utf8(二进制)
utf8("11111111111")
自动判断变量,如果字符串就转成二进制,否则字符串
ascii(二进制)
ascii("11111111111")
utf8.bytes("11111111111")
utf8.string(二进制)
ascii.bytes("11111111111")
ascii.string(二进制)
string.bytes("11111111111","UTF-8")
bytes.string(二进制,"UTF-8")
string.base64(二进制)
结果为base64字符串
base64.bytes(base64字符串)
结果为二进制
自动判断变量,如果字符串就转成二进制,否则字符串
base64(二进制)
base64("11111111111")
在一个数组中增加元素,如果参数是字节数组增加字节数组,则会自动跟addrange一致
var a=[];
a.add(1);
a.add([2,3],true)//第二个参数代表将参数的数组分开加入,所以结果为:[1,2,3]
//下面是特殊的字节数组
var a=bytes([1,2,3,4]);
a=a.add(bytes([5,6]));
return a; // 结果为字节数组 [1,2,3,4,5,6]
在一个数组中批量增加元素
var a=[];
a.add(1);
a.addrange([2,3])//结果为:[1,2,3]
定义一个构成协议的单元内容
unit(name, type, length, value,forlength)
var units = [
unit("数据采集时间", "DATE2", 6),
unit("芯片 ID", "STRING", 16),
unit("公钥", "BYTE[]", 64),
unit("VIN", "STRING", 17),
unit("签名信息", "BYTE[]", -1)
];
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 单元描述,无实际意义 |
2 | 字符串 | 类型定义 |
3 | int | 长度(字节)可以定义为一个函数名,在前面参数解析好的情况下获取到自己的长度; -1 表示此长度为剩下未解析的所有内容; -2 表示获取前一个解析的数据作为长度; 0 如果有函数就调用函数获取,没有就获取前一个数据作为长度 |
4 | byte[] | 初始值 |
5 | bool | 表示定义后面一个字段的长度为此数据值 |
类型定义参考
类型名 | 字节数 | 说明 |
---|---|---|
BIT | 1位 | 位 |
ASCII | n | ASCII字符串 |
UTF8 | n | UTF8字符串 |
GBK | n | GBK字符串 |
STRING | n | GBK字符串 |
WORD | 2 | 2字节数字 |
DWORD | 4 | 4字节数字 |
DECIMAL | 8 | 8字节数字 |
BCD | 6 | BCD时间 |
DATE | 6 | 时间 |
DATE2 | 6 | 时间 |
BYTE | 1 | 1字节 |
BYTE[] | n | n字节 |
FLOAT | 4 | 4字节小数 |
16进制转二进制,如果参数多于一个,表示要将数据转成二进制,参考下面例子:
bin(要转换的数据,"WORD",字节数, 缺省字节(不够的时候补这个字节),true:小端,false:大端)
bin(100,"WORD",3,0,true); //结果: 0x006400
参数说明
第二个参数参考上面类型定义
二进制转16进制
二进制转float
二进制转位
//下面可以将二进制数据或字节转为1010类似的字符串
bits(二进制数据);
bits(字节);
位转二进制
//下面可以将1010类似的字符串转成二进制
bits.bin("11111111");//结果:0xFF
二进制转数据
//下面可以将二进制数据转为数字
bytes.obj(二进制数据, "WORD",缺省字节(不够的时候补这个字节),true:小端,false:大端);
bytes.obj(二进制数据, "WORD",0,false);
第二个参数就是数据类型:
类型定义参考
类型名 | 字节数 | 说明 |
---|---|---|
BIT | 1位 | 位 |
ASCII | n | ASCII字符串 |
UTF8 | n | UTF8字符串 |
GBK | n | GBK字符串 |
STRING | n | GBK字符串 |
WORD | 2 | 2字节数字 |
DWORD | 4 | 4字节数字 |
DECIMAL | 8 | 8字节数字 |
BCD | 6 | BCD时间 |
DATE | 6 | 时间 |
DATE2 | 6 | 时间 |
BYTE | 1 | 1字节 |
BYTE[] | n | n字节 |
FLOAT | 4 | 4字节小数 |
16进制转数据,原理与bytes.obj一致
//下面可以将16进制数据转为数字
hex.obj(0xaabb, "WORD");
如果协议包含转义的字符,在数据接收后或发送前都需要转义
转义定义=
{
"7D2E":"7E",//都用16进制表示
"7D01":"7D"
}
bytes.zhuanyi1(待转义的二进制数据,转义定义)
二进制解析
bytes.parse(bytes, units);
例子: 其中bytes是二进制数据
var body = bytes.parse(bytes,
[
unit("状态码", "BYTE", 1),
unit("信息", "BYTE", 1),
]);
单元定义加数据转二进制
units.bytes(units, data);
例子
var body = units.bytes([
unit("状态码", "BYTE", 1),
unit("信息", "BYTE", 1),
], [0x02, 0x01]);
BCC(异或校验)法
循环冗余检验
适用于modbus的循环冗余检验
适用于crc32_mpeg_2算法
sm2密匙对生成
sm2.pair()
sm2签名,返回完整的签名字节串
sm2.sign(byte[] msgBytes, byte[] privatekey, string ident = "1234567812345678")
sm2签名,返回签名的R和S部分,结构为:
{"r":r部分字节串,"s":s部分字节串}
例子
var result=sm2.sign2(byte[] msgBytes, byte[] privatekey, string ident = "1234567812345678")
var r=result.r;
var s=result.s;
sm2校验,该方法需要用签名的R和S组成一个结构性的字节串校验
sm2.verify(byte[] msgBytes, byte[] publickey, byte[] sig, string ident = "1234567812345678")
说明:sign 按下面结构
[
unit("签名R值长度", "BYTE", 1),
unit("签名R值", "BYTE[]", getlength),
unit("签名S值长度", "BYTE", 1),
unit("签名S值", "BYTE[]", getlength),
];
function getlength(bytes) {
return bytes.substr(bytes.length - 1,1);
}
sm2校验,该方法用签名的R和S部分校验
sm2.verify2(byte[] msgBytes, byte[] publickey, byte[] rb, byte[] sb, string ident = "1234567812345678")
签名校验完整例子:
var kp={
"private": "5d4d7960fa613c20eb31c5a0ec32263c4f37a6574c4bbef81330f09e5edeccf3",
"public": "4fc58ec228b9605b7fcd4ecfbae4441fd74be9b2b116843b2b1eb2d71d0febf4074d0969f9f4d5b4f7eac13de8b22e9623a330725746a4d853a46f1ace428d3f"
}
var result=sm2.sign(utf8.bytes("11111111111111"),hex.bytes(kp.private))
sm2.verify2(utf8.bytes("11111111111111"), hex.bytes(kp.public), result.substr(0,32),result.substr(32,32))
sm2加密
sm2.encode(byte[] 待加密消息,byte[] 秘钥, bool isprivate, int style)
说明:isprivate—>默认是公钥加密,如果要用私钥加密,该参数指定为true
参数 | 类型 | 说明 |
---|---|---|
待加密消息 | byte[] | 待加密消息 |
秘钥 | byte[] | 加密密匙,一般是公钥 |
isprivate | bool | 如果想用私钥加密,这个参数可以为true |
style | int | 两种输出模式:0->C1C2C3 1->C1C3C2 |
sm2解密
sm2.decode(byte[] 待解密消息,byte[] 秘钥, bool isprivate,int style)
说明:isprivate—>默认是公钥加密,如果要用私钥解密,该参数指定为true
参数 | 类型 | 说明 |
---|---|---|
待解密消息 | byte[] | 待解密消息 |
秘钥 | byte[] | 加密密匙,一般是私钥 |
isprivate | bool | 默认是私钥,为true,如果要用公钥,该参数需要设为false |
style | int | 两种输出模式:0->C1C2C3 1->C1C3C2 |
平台包含数据对象主要包含文件、模板和实例
实例
对象名称说明
主要是this使用
/this/test --- this代表用户名称,自动指向用户下的文件、模板、实例
文件/模板/实例的基本信息
示例
object.info(对象id或路径,"file");
调用结果
{
"path": "help/1111",
"id": "5f56dd9aa5a79a6b78f8f5b2",
"name": "1111",
"desc": "",
"share": null,
"type":".html",
"uid": "5f503e83a5a79a5d7ccb6549",
"created": "2020-09-08T01:25:46.237Z",
"updated": "2020-09-13T14:16:54.674Z"
}
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 对象id或路径 |
2 | 字符串 | 类型:File/Template/Instance |
文件/模板/实例的删除(文件夹与文件)
//如果删除的是文件夹,需要调用string.encode(文件夹路径),传入对象id
object.delete(类型(File/Template/Instance), 对象的id);
文件/模板/实例的删除
file.delete(文件的id ? file/template/instance);
文件/模板/实例的文件夹的删除
group.delete(文件夹的路径 ? file/template/instance);
文件/模板/实例的结构查询(文件夹与文件)
示例
参考1:
直接传入参数
object.query("File","/admin/文件夹1","_name like 'a'",{"_created":-1},false);
参考2:
将参数放入一个字典里
object.query(
"File",//对象类型:File|Template|Instance
{
"group":"",//文件夹:如 /admin/a/b/c
"query":"",//过滤
"sortby":"",//排序
"nofolder":"",//true将取全部文件
"relative":"",//true将用this命名
"team":""//团队
}
);
调用结果
{
"files": [
{
"type": "folder",
"path": "help/doc",
"id": "_aGVscC9kb2M",
"name": "doc"
},
{
"type": "file",
"id": "5f56dd9aa5a79a6b78f8f5b2",
"path": "help/1111",
"name": "1111",
"desc": "",
"tid": null,
"size": 5,
"created": "2020-09-08T01:25:46.237Z",
"modified": "2020-09-08T01:25:46.237Z",
"share": null
}
],
"path": [
{
"id": "_aGVscA",
"name": "help",
"index": 0
}
]
}
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 类型:File/Template/Instance |
2 | 字符串 | 文件夹:如 /admin/a/b/c |
3 | 字符串 | 过滤: 可以用 _name ,_created , _modified 等过滤 |
4 | json | 排序 |
5 | 布尔 | 是否用无文件夹模式 |
补充说明
无文件模式表示查询指定文件夹所有文件(包含子文件夹中的文件)
与object.query相同,只是第一个参数不需要了
与object.query相同,只是第一个参数不需要了
如果模板直接引用了模板,这个api可以查询被引用的模板相关的模板列表
获取文件的二进制内容
file.bytes(文件id或路径)
文档/模板/实例的获取与更新
该函数的一个参数是类型(file/template/instance),其他参数及含义与file/template/instance 函数一致。
文档/模板/实例的获取与更新
示例
//方法1:
file("名称?类型",值,描述,是否解压,是否忽略存在,是否新增,是否增加min文件,指定id,团队,更新说明);
//方法2
var data = {
"name": "",
"value": "",
"desc": "",
"ispkg": false,
"ignoreexist": false,
"isnew": false,
"min": false,
"id": "",
"team": "",
"tag": "",
}
file(data);
调用结果
只有一个参数(获取数据)的结果:
{
"err": "",
"name": "this/setting.json",
"value": "111111111",
"property": {
"_name": "help/setting.json",
"_desc": "created by system",
"_size": 274,
"_id": "5f503e84a5a79a5d7ccb654b"
}
}
至少2个参数(更新数据)的结果:
{
"err": "",
"name": "this/1111",
"id": "5f56dd9aa5a79a6b78f8f5b2",
"path": "help/1111",
"type": "file",
"filename": "help/1111"
}
参数说明
第一个参数后面可以增加:|新的路径名称
表示需要更新文件路径名。
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 名称?类型 — 类型:File/Template/Instance |
2 | 字符串 | 值 — 如果不指定值以及后面的参数,表示只获取数据,指定后表示更新值 |
3 | 字符串 | 描述 |
4 | 布尔 | 是否解压 |
5 | 布尔 | 是否忽略存在 |
6 | 布尔 | 是否新增 |
7 | 布尔 | 是否增加min文件 |
8 | 字符串 | 指定id |
补充说明
与File函数一致,只是类型默认为Template
获取模板的结构
示例
template.get("/this/test")
调用结果
{
"a": {
"_type": "String"
},
"b": {
"_type": "String"
}
}
获取或者更新实例
示例
instance("/this/msg");//实例获取
instance("/this/msg.message","my message");//实例更新
调用结果
只有一个参数(获取数据)的结果(实例的结构及数据):
{
"message": {
"_id": "/5f503e83a5a79a5d7ccb654a.message",
"_name": "/help/msg.message",
"_desc": "22222222",
"_type": "String",
"_array": false,
"_value": "1111111111",
"_time": "2020-09-03T00:53:24.034Z",
"_from": "",
"_to": [
"*"
]
}
}
2个参数(更新数据)的结果:
如果更新成功,返回空的字符串,如果更新失败,返回错误描述。
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 实例路径 — /this/a/b.c |
2 | 字符串 | 值 — 如果不指定值以及后面的参数,表示只获取数据,指定后表示更新值 |
获取实例数据
示例
用法1:
instance.data("60c1bed19f4a38a4502e1b16");//按模板结构组织
结果:
{
"温度": {
"最大值": 0
},
"湿度": 0
}
用法2:
instance.data("60c1bed19f4a38a4502e1b16","_id");//按含id的键值对组织
结果:
{
"/60c1bed19f4a38a4502e1b16.温度": 55,
"/60c1bed19f4a38a4502e1b16.温度.最大值": 0,
"/60c1bed19f4a38a4502e1b16.湿度": 0
}
用法3:
instance.data("60c1bed19f4a38a4502e1b16","_name");//按含name的键值对组织
结果:
{
"/admin/电机2.温度": 55,
"/admin/电机2.温度.最大值": 0,
"/admin/电机2.湿度": 0
}
获取实例节点数据,包含数据,时间等
示例
instance.get("/admin/test/a.b");
调用结果
{
"id": "/643f9592a5a79a129c7ec81f.b",
"value": "11111111111",
"time": "2023-04-19T08:00:24.089Z",
"from": "admin"
}
直接新增或设置实例的各个节点值
set2 — 设置值后只有当值改变才向订阅的客户端发送数据,不变就不发,使用于轮询获取数据的场景
示例
//如果实例已经存在,第一个参数可以不用
instance.set("/admin/template", "/admin/test/a", {
"终端手机号": sim,
"省域": data[5][0],
"市县域": data[5][1],
"制造商": data[5][2],
"终端型号": data[5][3],
"终端ID": data[5][4],
"车牌颜色": data[5][5],
"车辆标识": data[5][6],
"鉴权码": jqm
});
调用结果
如果更新成功,返回true,如果更新失败,返回错误描述。
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 模板路径或id — 如果实例已经存在,这个参数可以省略 |
2 | 字符串 | 实例路径 |
3 | 字符串 | 待更新的各子节点值 |
4 | int | 一个标识(0-表示有就更新,没有就新增;1-表示新增;2-表示更新;) |
删除实例
示例
instance.delete(实例id 或者 实例路径);
调用结果
如果更新成功,返回正常,如果更新失败,返回异常。
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 实例路径或id |
查询某个模板下产生的实例
示例
instance.query("/admin/client", "终端手机号 like '6222'", 0, 0, {"排序字段":-1}, ["终端手机号","制造商"]);
调用结果
{
"rows": [
{
"_id": "5e93ccc318f1420f40b17c38",
"_value": {
"终端手机号": "157042976222",
"制造商": "CATS"
}
},
{
"_id": "5f3a897d18f142120865f620",
"_value": {
"终端手机号": "857030406222",
"制造商": "WUST"
}
},
{
"_id": "5ebcc68418f14230eca942f1",
"_value": {
"终端手机号": "717039562229",
"制造商": ""
}
}
],
"count": 3
}
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 模板路径或id |
2 | 字符串 | 查询过滤条件 |
3 | 整数 | 分页的当前页 |
4 | 整数 | 分页的每页条数 |
5 | json | 排序 |
5 | array | 显示字段 |
补充说明
字段指模板中定义的节点名加上几个特殊的系统字段:
查询过滤条件格式:字段1 比较符 值 and/or/&&/|| 字段2 比较符 值
比较符 | 说明 |
---|---|
= | 等于 |
== | 同= |
!= | 不等于 |
<> | 同!= |
like | 包含 |
将一个实例的值实时更新到另一个实例上去
示例
instance.binding("/实例A.status", "/实例B.status");// /实例A.status的值会实时同步到 /实例B.status
//也可以用变量,如下面语句可以在模板的节点事件上写入,这样这个模板生成的实例都有了同步功能:
instance.binding("/" + this + ".status", parent(this) + ".status");
获取图形中使用的模板id
symbol.tid(图形id)
查询某个模板下产生的数据,跟实例不同,这个数据仅用于存储,比如历史数据等。
使用方法跟instance.query完全一致。
data.query2 用于当数据量太大时没有建索引时数据库排序会报错,这个函数可以worker服务器上排序,避免在无索引时查询问题。
示例
data.query("/admin/client", "终端手机号 like '6222'", 0, 0, {"排序字段":-1}, ["终端手机号","制造商"]);
调用结果
{
"rows": [
{
"_id": "5e93ccc318f1420f40b17c38",
"_value": {
"终端手机号": "157042976222",
"制造商": "CATS"
}
},
{
"_id": "5f3a897d18f142120865f620",
"_value": {
"终端手机号": "857030406222",
"制造商": "WUST"
}
},
{
"_id": "5ebcc68418f14230eca942f1",
"_value": {
"终端手机号": "717039562229",
"制造商": ""
}
}
],
"count": 3
}
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 模板路径或id |
2 | 字符串 | 查询过滤条件 |
3 | 整数 | 分页的当前页 |
4 | 整数 | 分页的每页条数 |
5 | json | 排序 |
6 | array | 显示字段 |
补充说明
字段指模板中定义的节点名加上几个特殊的系统字段:
查询过滤条件格式:字段1 比较符 值 and/or/&&/|| 字段2 比较符 值
比较符 | 说明 |
---|---|
= | 等于 |
== | 同= |
!= | 不等于 |
<> | 同!= |
like | 包含 |
在某个模板下插入一条数据。
示例
var body={};
body.报文 = bytes;
body.原始值 = 原始值;
body.超标详情 = json({
"限行区": 限行区,
"限值": 限值,
"超标时长": 超标时长
});
data.insert("/admin/a/template", body);
调用结果
成功后返回true
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 模板路径或id |
2 | json | 字段的值 |
在某个模板下插入一条数据。(异步插入,就是调用后会立即返回,如果失败会记录到日志中)
在某个模板下更新一条数据。
示例
var body={};
body.报文 = bytes;
body.原始值 = 原始值;
body.超标详情 = json({
"限行区": 限行区,
"限值": 限值,
"超标时长": 超标时长
});
data.update("/admin/a/template", body,dataid);
调用结果
成功后返回true
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 模板路径或id |
2 | json | 字段的值 |
3 | 字符串 | 记录id |
在某个模板下更新查询到的数据。
示例
var body={};
body.报文 = bytes;
body.原始值 = 原始值;
data.updatebyquery("/admin/a/template"," id='1' || id='2' ", body);
调用结果
成功后返回true
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 模板路径或id |
2 | 字符串 | 数据过滤条件 |
3 | json | 更新的字段和值的字典 |
在某个模板下删除一条数据。
示例
data.delete("/admin/a/template", 数据的id);
调用结果
成功后返回true
删除某个模板下指定查询内数据。
示例
data.DeleteByQuery("/admin/a/template","_id='111111111'");
调用结果
成功后返回true
删除某个模板下所有数据。注意,调用后数据将会清空并无法恢复。
示例
data.deleteall("/admin/a/template");
调用结果
成功后返回true
默认情况下,调用所有api允许的时间不能超过5分钟,这个函数可以自定义一个超时时间(毫秒)
示例
timeout(90000 * 5);
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | int | 超时毫秒 |
更新一个目录的名称
示例
group.update("/admin/abc?file","/admin/newname");
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | string | 旧文件全路径名称?类型(file/template/instance) |
2 | string | 新的文件全路径名称 |
查询实例的更新历史。
if (start && end) {
query = $("time>date('{0}') && time<date('{1}')", start, end);
} else if (start) {
query = $("time>date('{0}')", start);
} else if (end) {
query = $("time<date('{0}')", end);
} else {
throw ("必须指定查询时间段");
}
var datas = instance.history(instance, query, 0, 0);
return datas;
结果:
{
"rows": [
{
"time": "2021-06-17T01:51:02.835Z",
"value": 13
},
{
"time": "2021-06-17T01:51:06.434Z",
"value": 14
},
{
"time": "2021-06-17T02:01:49.074Z",
"value": 12
},
{
"time": "2021-06-17T02:19:24.744Z",
"value": 121
},
{
"time": "2021-06-17T03:20:56.826Z",
"value": 221
},
{
"time": "2021-06-17T03:21:28.708Z",
"value": 321
},
{
"time": "2021-06-17T03:25:26.221Z",
"value": 111
}
],
"count": 7
}
查询对象的更新历史(新增,更新)
var h = history.query(name, page, pagesize);
v.data = h.rows;
v.count = h.count;
结果:
{
"rows": [
{
"_id": "6033b2d09f4a384dc425d43d",
"flag": "update",
"time": "2021-02-22T13:34:08.870Z",
"muser": "admin",
"desc": "系统消息模板",
"tag": "",
"name": "admin/msg",
"size": 173
},
{
"_id": "5f5c22f5a5a79a6fa052ed99",
"flag": "update",
"time": "2020-09-12T01:23:01.503Z",
"muser": "admin",
"name": "admin/msg",
"size": 153
},
{
"_id": "5e8d759ba5a79a08fcddceed",
"flag": "update",
"time": "2020-04-08T06:56:27.409Z",
"muser": "admin",
"desc": "",
"name": "admin/msg",
"size": 155
},
{
"_id": "5e664cd3a5a79a0bf80ac6d2",
"flag": "update",
"time": "2020-03-09T14:04:03.565Z",
"muser": "admin",
"desc": "",
"name": "admin/msg",
"size": 155
}
],
"count": 4,
"type": "t"
}
查询对象的删除历史(删除操作),可用于删除文件的查看
var h = history.querydeleted(name, page, pagesize);
//name: 用于删除的文件名的模糊匹配
删除文件的打包下载,可以用于删除文件的恢复
var bytes = history.restore(name, page, pagesize);
//name: 用于删除的文件名的模糊匹配
//返回结果是打包的二进制文件数据,注意用zip后缀可以下载到本地
导入操作主要是利用zip包的文件操作实现。
将一个目录下的文件导出到zip包里
包里也可以加入模板和实例,用下面的格式:
示例
//导出
group.export("/admin/abc", "file", "abc.zip",团队id,带上源id,导出的目录,过滤字符串);
//克隆--相当导出时带上源id,保持id一致
group.clone("/admin/abc", "file", "abc.zip");
说明:
1. 第3个参数可以是文件或文件夹数组,批量导出/克隆
2. 第一个参数也可以直接是文件或文件夹数组,批量导出/克隆
例子:
//导出文件
bytes = group.clone(
[
"/admin/log/",
"/admin/api/",
]);
//导出模板
templates = group.clone([
"/admin/系统模板/",
], "template");
//将模板加入文件一起
return zip.add(bytes, {
"a.$template$.zip": templates
});
调用结果
自动下载
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 文件目录路径 |
2 | 字符串 | 类型(file/template/instance) |
3 | 字符串 | 导出的zip名称 |
4 | 字符串 | 团队id |
5 | 字符串 | 带上源id:true/false — true会将文件保留源文件的id,这样文件相互引用的时候不会有问题 |
6 | 字符串 | 可以导出到不同的目录下,这里指定那个目录,默认是源目录 |
补充说明
无
将一个文件导出到zip包里
示例
file.export("/admin/abc.html", "file");
调用结果
自动下载
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 文件路径 |
2 | 字符串 | 类型(file/template/instance) |
补充说明
无
将一个zip包里内容导入到平台
示例
object.import({
"group":"文件夹",
"type":"file/template/instance",
"value":zip,
"team":"",
"isnew":false
})
或者:
object.import("文件夹", "file/template/instance",zip,team,isnew);
基于Mongodb数据库的MapReduce,进行数据库数据的分组统计等。可以参考 https://www.runoob.com/mongodb/mongodb-map-reduce.html
示例
var map = function () {
//this._value.字段1 是数据库表中的分组字段
emit(this._value.字段1, 1);
}
var reduce = function (key, values) {
//values 是分组聚合后的第二个字段集合
return values.length;
}
var finalize = function (key, values) {
//最后处理下
return values;
}
var option={
out: "输出的别名",
sort: {"_value.字段2",-1},
limit: 100
}
//平台中的模板下的数据表格式:Data_模板id
//先获取模板的id
var info=template.info("/admin/ship/安全预警");
//表名
var tablename="Data_" + info.id;
//return mapreduce(表名, 查询过滤字符串, map, reduce, finalize, 选项)
return mapreduce(tablename, query, map, reduce, finalize, option)
新建一个tcp或udp连接,开始接收数据
示例
//client.new(地址,单元定义[可为空],接收处理程序,状态处理程序,超时秒断开,保留处理消息数量)
var address = "tcp://117.73.254.2:10828";
//如果是udp:
//var address = "udp://117.73.254.2:10828";
client.new(address, units, function (data, time, tag) {
//接收到的数据,时间,自定义标识字典
return 0;//0:已处理;-1:不处理;大于0:已处理多少字节,其他自动接到后面处理
}, function (data) {
//
this.parent.状态 = data;
if (data) {
this.parent.描述 = "连接成功";
}
else {
this.parent.描述 = "连接断开";
}
});
检查一个连接是否存在,避免重复连接
示例
var address = "tcp://117.73.254.2:10828";
//如果是udp:
//var address = "udp://117.73.254.2:10828";
client.exist(address);
关闭一个连接
示例
var address = "tcp://117.73.254.2:10828";
//如果是udp:
//var address = "udp://117.73.254.2:10828";
client.close(address);
针对一个连接,发送数据
示例
var address = "tcp://117.73.254.2:10828";
//如果是udp:
//var address = "udp://117.73.254.2:10828";
client.send(address,二进制数据);
服务端操作,获取一个连接
示例
//获取所有连接
var session = session.get("di_test")[0];
//获取指定clientid的连接
var session = session.get("di_test", clientid)[0];
服务端操作,发送数据给一个连接的客户端
示例
//获取指定clientid的连接
var session = session.get("di_test", clientid)[0];
//发送数据给这个连接的客户端
session.send(session,bytes)
//也可以三个参数直接发送
session.send("di_test",clientid,bytes)
服务端操作,关闭一个连接的客户端
示例
//获取指定clientid的连接
var session = session.get("di_test", clientid)[0];
//发送数据给这个连接的客户端
session.close(session)
示例
var address = "tcp://117.73.254.2:10828";
//如果是udp:
//var address = "udp://117.73.254.2:10828";
//定义解析单元
var units = [
//FE FE 00 04 04 00 13 00 18 07 77 07 87 3F B8 03 81 26 FD 00 40 00 00 00 09 00 00 00 06 00 1E 00 59 FE FE
unit("起始", "BYTE[]", 2, [0xfe, 0xfe]),
unit("地址", "BYTE[]", 2), //00 04
unit("功能", "BYTE", 1), //04
unit("寄存器位置", "BYTE[]", 2), //0013
unit("数据长度", "WORD", 2), //00 11
unit("数据", "BYTE[]", getlength), //
unit("校验码", "BYTE[]", 2),
]
//解析到该单元时需要根据下面函数决定该单元长度
function getlength(bytes) {
//bytes.substr函数表示截取字节,从第几个开始截取多少个字节
var data = bytes.substr(bytes.length - 2, 2);
//bytes.obj函数表示把字节变成一个数字
return bytes.obj(data, "WORD");
};
//client.close函数可以关闭连接
//client.close(address);
//如果不存在连接,就产生一个连接到服务器
if (!client.exist(address)) {
//产生新的连接(连接地址,解析单元定义,收到数据后执行函数)
client.new(address, units, received);
}
//收到后调用的函数
function received(data) {
//这里的data表示收到的二进制数据
console.log("解析结果(二进制):" + bytes.hex(data));
var 解析结果 = bytes.parse(data, units);
console.log("解析结果(单元):" + 解析结果);
};
//定义发送的数据
//hex.bytes表示将16进制转换成二进制字节
var bytes = hex.bytes("0102");
//crc16函数表示校验码,增加到发送的数据后面
var bs = crc16(bytes);
bytes = bytes.add(bs);
//串口发送数据
//bytes.hex表示将二进制数据转换成16进制,这里只是用日志看下数据
console.log(bytes.hex(bytes));
//com.send表示发送二进制数据到串口
client.send(address, bytes);
/*下面例子可以自定义构成一个协议要求的二进制数据,units.bytes函数用于单元数据转换到二进制
var content=0x0012;
var data = [[0xfe, 0xfe], 4, 4, 0x0013, 2, content.length, content, crc16(content)];
var bytes = units.bytes(units, data);
*/
//可以随便定义自己的返回
return {
"units": units
};
查询所有存在的串口
示例
for (var com in com.list()) {
if (!com.isopen(com)) {
com.open(com, {},units, received)
}
}
function received(bytes, name) {
console.log(name + " :" + bytes);
}
判断串口是否是开的
示例
for (var com in com.list()) {
if (!com.isopen(com)) {
com.open(com, {},units, received)
}
}
function received(bytes, name) {
console.log(name + " :" + bytes);
}
打开串口
示例
var para={};
para.baudRate=9600;
para.parity="None";
para.dataBits=8;
para.stopBits=1;
//定义解析单元
var units=[
unit("起始符", "BYTE[]", 2, [0xfe, 0xfe]),
unit("地址", "BYTE", 1),
unit("功能", "BYTE", 1),
unit("数据长度", "WORD", 2),
unit("数据", "BYTE[]", getlength),
unit("校验码", "BYTE[]", 2),
]
function getlength(bytes) {
var l = bytes.substr(bytes.length - 2, 2);
return bytes.obj(l, "WORD");
}
//
for (var com in com.list()) {
if (!com.isopen(com)) {
com.open(com, para,units, received)
}
}
function received(bytes, name) {
console.log(name + " :" + bytes);
}
参数说明:
参数 | 类型 | 描述 |
---|---|---|
comname | string | 串口名:COM1/COM2/… |
para | 字典 | 串口参数,参考后面说明 |
units | array | 解析单元,参考解析单元部分 |
received | function | 一个函数,定义收到数据后的处理 |
第二个参数是下面字段组成的字典:
参数 | 类型 | 描述 |
---|---|---|
baudRate | int | 波特率:默认 9600 |
parity | stirng | 校验位:默认 None |
dataBits | int | 数据位:8 |
stopBits | stirng | 停止位:1 |
关闭串口
示例
for (var com in com.list()) {
com.close(com)
}
直接运行脚本,可选择异步
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 脚本 | 待执行脚本 |
2 | 参数 | 脚本中的变量初始值 |
3 | 是否异步 | true: 异步执行,不用等待结果 |
4 | 目标机器 | 机器的domain, 如果未指定,按系统定义 |
var code = `
try {
log("1111111")
} catch (e) {
console.error(e);
set(instid,{LastError:e});
}
finally {
set(instid,{running:false});
}
`;
test(code, { instid: instid }, false,"192.168.1.1");
异步运行函数,运行后不用等待结果,立即返回
示例
//定义一个待执行函数
function abc(a, b) {
log(a + b)
}
async.run(abc, ["参数1", "参数2"]);
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 表达式 | 待执行函数 |
2 | 数组 | 参数 |
直接运行脚本
示例
runds("/admin/test.ds", {
"a": "11111111"
},30000);
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 脚本(全路径或id) |
2 | 字典 | 参数 |
3 | int | 超时时间 (毫秒) 默认 60000毫秒 |
4 | 字符串 | 执行目标机器 |
脚本执行超时设置,可以放在脚本执行的任何地方,但不要放在耗时的方法后。
示例
timeout(60000);//脚本执行1分钟后超时
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | int | 超时时间 (毫秒) 默认 60000毫秒 |
将一个值为二进制内容的字典压缩为zip文件
data["目录/data1.xlsx"]=file.bytes("/admin/excel/data.xlsx");
data["目录/data2.xlsx"]=file.bytes("/admin/excel/data.xlsx");
return zip(data);
将zip文件解压为字典
data["目录/data1.xlsx"]=file.bytes("/admin/excel/data.xlsx");
data["目录/data2.xlsx"]=file.bytes("/admin/excel/data.xlsx");
a= zip(data);
b= unzip(a);
压缩的zip内增加一个字典内容
data["目录/data1.xlsx"]=file.bytes("/admin/excel/data.xlsx");
data["目录/data2.xlsx"]=file.bytes("/admin/excel/data.xlsx");
a= zip(data);
b= unzip(a);
c=zip.add(a,data)
从excel文件中读取数据
excel.read(file.bytes("/admin/excel/data.xlsx"),notitle);
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | byte[] | excel文件内容 |
2 | bool | 默认false表示把第一行当做标题行,如果为true表示所有行都是数据 |
将数据写入excel文件
var data={
"sheet1":
[
{
"a":"a",
"b":"b"
},
{
"a":"1",
"b":"2"
}
]
}
excel.write(data);//输出为excel的文件内容,可以将文件名命名为xxx.xlsx.ds
get方式请求
示例
rest.get("/admin/test.ds", {
"app_key": "11111111"
});
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 请求的地址 |
2 | json | 请求的header字典 |
3 | bool | 是否保持连接 |
4 | int | 超时毫秒 |
post方式请求
示例
rest.post("/admin/test.ds", {
"参数1": "11111111",
"参数2": "22222222"
}, {
"app_key": "11111111"
});
result = rest.post("http://127.0.0.1/GetData", data, header, true,
500, "10.12.13.14");
//其中的header可以支持的格式:
{
"Content-Type":"application/x-www-form-urlencoded, multipart/form-data, application/json, application/xml"//中间选择一种
"gzip":true //默认超过2048会自动gzip
}
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | 字符串 | 请求的地址 |
2 | json | 请求的参数字典 |
3 | json | 请求的header字典 |
4 | bool | 是否保持连接 |
5 | int | 超时毫秒 |
6 | string | 指定一个机器ip,表示在该机器上执行,一般在有的机器无法连接外网的情况下,需要指定一台可以连接外网的机器发送请求 |
压缩图片
示例
compressimage(data,300);
调用后获取到压缩后的图片
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | byte[] | 原图字节 |
2 | int | 压缩后的大小 |
生成二维码
示例
qr(content,level,size,format,drawQuietZones);
var content = p[0]._Str();
var level = p[1]._Int(1);
var size = p[2]._Int(20);
var format = p[3]._Str();
var drawQuietZones = p[4]._Bool();
ImageFormat imagetype = ImageFormat.Jpeg;
if (!string.IsNullOrEmpty(format))
{
switch (format.ToLower())
{
case "gif":
imagetype = ImageFormat.Gif;
break;
case "jpeg":
imagetype = ImageFormat.Jpeg;
break;
case "png":
imagetype = ImageFormat.Png;
break;
case "tiff":
imagetype = ImageFormat.Tiff;
break;
}
}
public enum ECCLevel
{
/// <summary>
/// 7% may be lost before recovery is not possible
/// </summary>
L,
/// <summary>
/// 15% may be lost before recovery is not possible
/// </summary>
M,
/// <summary>
/// 25% may be lost before recovery is not possible
/// </summary>
Q,
/// <summary>
/// 30% may be lost before recovery is not possible
/// </summary>
H
}
参数说明
参数序号 | 类型 | 说明 |
---|---|---|
1 | string | 二维码内容字符串 |
2 | int | 级别 |
3 | int | 尺寸 |
4 | string | 格式 |
5 | bool | 是否画其他区域 |
获取日志
示例
var data = log.get("Debug", 1, 100, "_msg like '111'", ["_time", "_type", "_msg", "_server_ip"],{"_time":-1});
新增一条日志
示例
log.set(type, msg, feature);
删除日志
示例
var count = log.delete("_msg like '111'");
清除日志,这是删除整个日志,需要管理员权限
示例
log.clear()
查询数据库的索引
示例
return dbindex.query(模板)
产生新的数据库的索引
示例
return dbindex.create(模板,索引名,索引内容)
删除数据库的索引
示例
return dbindex.drop(模板,索引名)
阿里云的短信服务接口调用,请先在阿里云上申请相关服务
示例
var b = sms(accessKeyId, accessKeySecret, 接收短信的手机号, 签名, 模板代码, 模板参数);
模板参数例子:
{
"code": 验证码
}
结果是阿里云的返回结果,请参考阿里云介绍
发送邮件
利用POP3/SMTP发送,需要先开通
示例
var b = email(string host, int port, string username, string password, string from, object to, string subject, string body, string fromname,dictionary 附件)
*其中的 object to 参数是发送对象,可以用以下几种:
1. 直接用收件人邮箱:"tchua77@126.com"
2. 多人:["tchua771@126.com","tchua772@126.com"]
3. 发送+抄送+密送: {"to":["tchua771@126.com","tchua772@126.com"],"cc":["tchua771@126.com","tchua772@126.com"],"bcc":["tchua771@126.com","tchua772@126.com"]}
//具体例子:
var b = email("smtp.qq.com", 587, "88504660@qq.com", 这个参数是POP3/SMTP的授权码, "88504660@qq.com", "tchua77@126.com", 这个参数是邮件主题, 这个参数是邮件正文, 这个参数是发送方的名称, { "附件1": file.bytes("/admin/文件1"),"附件2": file.bytes("/admin/文件2") });
发送成功后返回 true
页面跳转
示例
redirect("/admin/page1")
或:
go("/admin/page1")
获取一个唯一的标识字符串,长度24
示例
return id()
获取一个用于用户标识的token,是个JWT token
示例
return token()
设置客户端的cookie
示例
c = {};
c.token = "12345465";
cookie.set(c, now() + 30 * 1000 * 60);
当使用脚本获取文件时,可以缓存客户端结果
示例
cache();
自定义服务器对象缓存,目的为提高性能
示例
cache.set(key, obj, timeout【超时秒】, false【true:使用后立即更新时间,延迟超时】);
获取自定义服务器对象缓存,目的为提高性能
示例
cache.get(key);
获取一个随机数字
示例
random(1,100);//从1到100之间的随机数字
示例
local(now()).tostring("yyyy-MM-dd HH:mm:ss.fff");//当前时间本地化后格式化显示
获取所有服务器的配置
示例
config.get()
//结果:
{
"服务1": {
"ServiceName": "TaiwuService",
"DBConnection": "",
"WorkPort": "30010",
"MQTTPort": "1883",
...
},
"服务2": {
"ServiceName": "TaiwuService",
"DBConnection": "",
"WorkPort": "30010",
"MQTTPort": "1883",
...
},
"服务3": {
"ServiceName": "TaiwuService",
"DBConnection": "",
"WorkPort": "30010",
"MQTTPort": "1883",
...
},
}
设置服务器的一个或多个配置, 如果希望配置生效,必须重启服务,重启服务的方式:
示例
//与config.get()得到的结果一致:
var myconfig=
{
"服务1": {
//"ServiceName": "TaiwuService",
"DBConnection": "",
//"WorkPort": "30010",
"MQTTPort": "1883",
...
},
"服务2": {
"ServiceName": "TaiwuService",
//"DBConnection": "",
//"WorkPort": "30010",
//"MQTTPort": "1883",
...
},
"服务3": {
//"ServiceName": "TaiwuService",
//"DBConnection": "",
"WorkPort": "30010",
//"MQTTPort": "1883",
...
},
}
config.set(myconfig);
动态产生一个服务,执行后立即启动服务监听端口
示例
createservice(servername,port[502],maxconnections[100],timeout[300秒],mode[tcp/udp])
动态移除自定义服务
示例
removeservice(servername)
检查服务是否存在,存在为true,否则false
示例
checkservice(servername);
获取服务器上的本地文件内容
示例
localfile.read("c:\\abc.txt")
//结果:
二进制
写入服务器上的本地文件内容
示例
localfile.write("c:\\abc.txt",二进制内容)
//结果:
true
执行操作系统命令
示例
shell.run(命令,命令参数,执行命令的目录)
shell.run("dir","","/home/worker")
//在指定的服务器上运行
test(`
shell.run("systemctl stop worker4")
`,{},false,"worker1-1")
执行操作系统命令
示例
//定义连接
var conn = "server=10.35.124.23,1433;database=EquInfo;uid=PLN;pwd=Pln@20220101;"
//
name = "abc"
time = local(now()).tostring("yyyy-MM-dd")
ws = "2222"
line = "kkkk"
//查询数据
aaa = SqlServer.Query(
`
SELECT *
FROM [EquInfo].[dbo].[Energy_BSS] where Time='${time}' and Name='${name}'
`
, conn
)
if (aaa.table.length == 0) {
sql = `
INSERT INTO [dbo].[Energy_BSS]
([Name]
,[Volume]
,[Time]
,[WS]
,[Line])
VALUES
('${name}'
,${volume}
,'${time}'
,'${ws}'
,'${line}')
`
//插入数据
SqlServer.Query(sql, conn)
//
}