场景联动类似平台端的规则引擎、或者实时计算,可以实现本地的采集控制闭环,或者远程设备的采集控制联动。
采用CRON定时器触发,定时到触发一次动作,注意因为是定时触发,不会传入输入数据,一般用于定时读取或者定时控制设备
CRON表达式在线解析: 在线CRON解析
常用表达式例子:
(1)0/2 * * * * ? 表示每2秒 执行任务
(1)0 0/2 * * * ? 表示每2分钟 执行任务
(1)0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作
(4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
(5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
(6)0 0 12 ? * WED 表示每个星期三中午12点
(7)0 0 12 * * ? 每天中午12点触发
(8)0 15 10 ? * * 每天上午10:15触发
(9)0 15 10 * * ? 每天上午10:15触发
(10)0 15 10 * * ? 每天上午10:15触发
(11)0 15 10 * * ? 2005 2005年的每天上午10:15触发
(12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
(13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
(14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
(15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
(18)0 15 10 15 * ? 每月15日上午10:15触发
(19)0 15 10 L * ? 每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
输入属性表示来自输入数据源的属性,满足全部条件才会触发一次动作。
以下实例表示来自输入数据源的属性a=1
和b=2
同时成立才会触发动作。
输入属性表示来自输入数据源的属性,满足一个条件就会触发一次动作。
以下实例表示来自输入数据源的属性a=1
或者b=2
任意一个成立就会触发动作。
每次输入数据源产生数据,都会触发一次动作。
自定义一个函数,在每次输入数据的时候执行,若结果为true则执行动作
编写函数一定要注意对输入进行判空,不能对属性和nil进行比较,内容写长一点也可以。
程序会自动测试是否异常,有错误此条规则会创建失败,不会导致整体崩溃
例如:
-- 以下实例对输入数据进行h和hp1进行判断,当1大于hp1时返回true,触发动作
function(msg)
local h = msg.h or 0
local hp1 = msg.hp1 or 9999
return h > hp1
end
--简写版本
function(msg)
return (msg.h or 0) > (msg.hp1 or 9999)
end
动作之间是并行执行的,没有顺序可言
用于创建一条消息,把属性输入到输出数据源。如果有多条属性新增,会合并成一条消息输出。
以下实例会向输出数据源发送一条消息{"temp":1234}
比较常用,主要用于向Modbus写入数据,也可以向远处的设备写入数据,实现远程触发
对输入数据的进行替换。
以下实例结果:
--输入数据,如modbus采集的
{"a":1}
--输出数据
{"testaprop":1}
类似自定义,设置一个计算函数,返回计算的结果输出到属性里面,如果属性不存在会新建。
凡是写函数的地方都需要判空,程序会自动测试是否异常,有错误此条规则会创建失败,不会导致整体崩溃
function(msg)
return (msg.a or 0) + (msg.b or 0)
end
--输入数据
{"a":1, "b":10}
--输出数据
{"a":1, "b":10,"testqaa":11}
就是简单的吧输入数据源转发到输出数据源。
设置一个函数,返回一个table
function(msg)
local res = {
a= msg.a or 0
b= msg.b or 0
}
res.c = res.a * res.b
return res
end
--输入数据
{"a":1, "b":10}
--输出数据
{"a":1, "b":10,"c":10}
如果要实现全局变量的效果,可以在全局模块里面直接定义比如:
function(msg)
-- msg 为空
if not msg or not next(msg) then
sys.errflag = (sys.errflag or 0 ) + 1
end
if sys.errflag == 3 then
--创造一个bool的键值
msg.flag = false
end
return msg
end
将采集的温度数据转存到虚拟设备寄存器地址2。
目标:设置水位上限和水位下限,水位超过上限水泵停止,水位低于下限水泵启动
使用设备MD620,485接口外接modbus 液位计,继电器1接水泵。
设置好modbus采集液位计和水泵控制,虚拟设备控制继电器1,功能码01,地址0。分别映射到数据h
和io1
。
参数的指令
液位计读取的指令
继电器的指令
h
大于上限hp1
并且模式控制m1
为1的时候在触发,触发动作给自定义1也就是Modbus主机写入一个io1控制数据,关闭水泵。function(msg)
return (msg.h or 0) > (msg.hp1 or 9999) and ((msg.m1 or 0) > 0)
end
h
小于上限lp1
并且模式控制m1
为1的时候在触发,触发动作给自定义1也就是Modbus主机写入一个io1控制数据,启动水泵。function(msg)
return (msg.h or 0) < (msg.lp1 or 9999) and ((msg.m1 or 0) > 0)
end
在案例1的基础上改进,主机采集水位,运行场景联动,3个远程的MD620外接了3个水泵。
4个设备采用D2G连接到一起,主机发出的数据会传到3个从机水泵,从机控制自己的水泵开关。
主机配置较为复杂,请复制在上位机JSON还原查看:
{"type":"base","version":"V4","base":{"host":"","param_ver":5,"flow":0,"fota":1,"log":1,"shell":[1,120,1],"report":0,"pmod":0,"pswd":"","pins":[13,8,12],"reboot":0,"mqr":[1,20,100],"apn":[0,"","",""]},"uconf":[[1,9600,8,0,1,19,1,20,"",0],[],[]],"nconf":[[],["d2g","d2d.developlink.cloud",1883,300,6,"${imei}","${imei}","${imei}","beng1231232323","86124105780386"],[],[],[],[]],"param_ver":5,"mb":[[2,1,21,[1,22],1,0,0,"","","",0,500,0,5],[[10,96,"010300040001"],[["h",4,"H",1]]],[[10,96,"640300000006"],[["hp1",0,"H",1],["lp1",1,"H",1],["hp2",2,"H",1],["lp2",3,"H",1],["hp3",4,"H",1],["lp3",5,"H",1]]]],"d6":[],"up":{},"vd":{"d":[100,22,[101]],"c3":[],"c2":[26,27],"c1":[20,7],"c1u":[2,0],"c3u":[0,10]},"rule":[[21,102,5,"function(msg)\nreturn (msg.h or 0) > (msg.hp1 or 9999) and ((msg.m1 or 0) > 0)\nend",[],[[1,"io1","0"]]],[21,102,5,"function(msg)\nreturn (msg.h or 0) < (msg.lp1 or 9999) and ((msg.m1 or 0) > 0)\nend",[],[[1,"io1","1"]]],[21,102,5,"function(msg)\nreturn (msg.h or 0) > (msg.hp2 or 9999) and ((msg.m2 or 0) > 0)\nend",[],[[1,"io2","0"]]],[21,102,5,"function(msg)\nreturn (msg.h or 0) < (msg.lp2 or 9999) and ((msg.m2 or 0) > 0)\nend",[],[[1,"io2","1"]]],[21,102,5,"function(msg)\nreturn (msg.h or 0) > (msg.hp3 or 9999) and ((msg.m3 or 0) > 0)\nend ",[],[[1,"io3","0"]]],[21,102,5,"function(msg)\nreturn (msg.h or 0) < (msg.lp3 or 9999) and ((msg.m3 or 0) > 0)\nend",[],[[1,"io3","1"]]]],"lora":[]}
从机配置较为简单,只需要设备好水泵控制的指令,然后接受来自网络通道2的主机数据:
{"type":"base","version":"V4","base":{"host":"","param_ver":5,"flow":0,"fota":1,"log":1,"shell":[1,120,1],"report":0,"pmod":0,"pswd":"","pins":[13,8,12],"reboot":0,"mqr":[1,20,100],"apn":[0,"","",""]},"uconf":[[1,9600,8,0,1,19,1,20,"",0],[],[]],"nconf":[[],["d2g","d2d.developlink.cloud",1883,300,21,"${imei}","${imei}","${imei}","beng1231232323","86124105780386"],[],[],[],[]],"param_ver":5,"mb":[[2,1,21,[22],1,0,0,"","","",0,500,0,5],[[10,100,"640500000001"],[["io2",0,"c",1]]]],"d6":[],"up":{},"vd":{"d":[100,22,[101]],"c3":[],"c2":[26,27],"c1":[20,7],"c1u":[2,0],"c3u":[0,0]},"rule":[],"lora":[]}
采集cron定时器周期性控制水泵开关:
如何设置定时器请看触发条件,有个在线解析cron表达式网页
0-3 * 8,13,18 * * *
这个表示每天8点、13点、18点,从0秒到3秒,每秒执行一次,也就是发送4次开启水泵指令,发4次其实多了,只是保证不漏开。0-3 * 9,14,19 * * *
这个表示每天9点、14点、19点,从0秒到3秒,每秒执行一次,也就是发送4次关闭水泵指令,发4次其实多了,只是保证不漏关。如何设置定时器请看触发条件,有个在线解析cron表达式网页
0/2 * * * * ?
表示每2秒 执行任务默认的属性是针对json进行操作的。
要实现灵活的自定义数据,建议使用自定义函数实现。
新建执行动作,选择自定义:
-- 返回的必须是个table,添加字段d可以被串口提取
function(msg)
return { d = "AT+REBOOT=1" }
end
function(msg)
local cmd = "010302065214"
return { d = cmd:fromHex() }
end
0 0,30 * * * ?
表示0分和30分整点触发采集,具体查看此文开头cron设置选择自定义输出,输入以下内容,其中 totalFlow
是对应的采集指令映射键值(需要按需修改),填入这个就能自动搜索到并触发对应的指令
function()
sys.publish("D_SEND_21", {topic="", tab={ totalFlow = 0 }, read=true})
return {xx=1}
end
0 0,30 * * * ?
表示0分和30分整点触发采集,具体查看此文开头cron设置选择自定义输出,输入以下内容,其中 va
是对应的采集指令映射键值(需要按需修改),id
是表号,填入这个就能自动搜索到并触发对应的指令
function()
sys.publish("D_SEND_24", {id="1234556", tab={"va", "vb"}, read=true})
return {xx=1}
end
D645配置:
输入数据源->串口1,表示从串口1采集电表数据。
虚拟设备配置:
输入数据源->串口2,表示外部设备可以从串口2读虚拟设备的数据。预留地址为0:表示起始地址为0; 预留长度为6:表示可用地址长度为6,即用户可用地址为0-5。
ModBus配置:
输入数据源->自定义2,表示可以将电表数据转存到虚拟设备中,写入的地址选取1和3。写入的地址要在虚拟设备预留的地址中,以上虚拟设备中设置的地址0-5可用。
场景联动配置:
方法1:新增属性,魔法值获取数据
输入数据源->自定义4,输出数据源->自定义1。前一个属性名和modbus中的键值对应,后一个属性和D645中的键值对应。表示ia_的数据从ia中获取。
方法2:转存虚拟设备
不需要配置modbus,直接将数据转存到虚拟设备中。
外部读取:
使用Modbus poll连接到网关的串口2, 设备ID就是虚拟设备的id,默认是100.寄存器地址则为modbus指令地址,以上设置是1和3。
从串口1采集的电压和电流:
从串口2通过modbus读取:
虚拟设备配置:
输入数据源->串口3,表示外部设备可以从串口3读虚拟设备的数据。预留地址为0:表示起始地址为0; 预留长度为10:表示可用地址长度为10,即用户可用地址为0-9。
ModBus配置:
输入数据源->自定义2和串口1,表示可以将温湿度数据转存到虚拟设备中,写入的地址选取1和3。写入的地址要在虚拟设备预留的地址中,以上虚拟设备中设置的地址0-9可用。tmep和humi是从传感器采集的数据;temp_和humi_是将采集的数据转存到虚拟设备,供外部设备读取,例如显示屏。
场景联动配置:
输入数据源->自定义1,输出数据源->自定义1。
temp是modbus的键值,1是虚拟设备的寄存器地址,表示将temp的值存入虚拟设备的寄存器地址1
外部读取:
使用Modbus poll连接到网关的串口3, 设备ID就是虚拟设备的id,默认是100。寄存器地址则为modbus指令地址,以上设置是1和3。
10 0 0 * * ?
表示每日凌晨0点整点触发,具体查看此文开头cron设置选择自定义输出,输入以下内容
function(tm)
if not tm.a then
sys.timerStart(sys.restart, 1000, "User reboot!", false)
end
return {xx=1}
end
选择定时触发,输出对应的定时表达式。如1/5 * * * * ?
表示从第一秒开始每5秒执行一次触发,具体查看此文开头cron设置
(不要刚好设置成0秒,可能会由于开机时间没同步误触发)
输入输出数据源选一个没有用过的自定义即可
此功能实际就是实现定时执行一个函数片段,判断有没有网络
选择自定义输出,输入以下内容
function(tm)
if not tm.a then
if not sock.isReady() then
return {DO1 = 1} --没网就给DO1写1
end
end
return {xx = 1}
end
modbus中输入数据源-->串口1和串口3,接着配置传感器和屏幕。
第二个指令是屏幕的配置,3|9表示将数据发送到串口3,9是设备ID。需要点击编辑,勾上只写
传感器的配置,这里从传感器中读取了两个数据,键值是107和103。
屏幕的配置,正确填写需要写入的屏幕寄存器地址,这里将传感器的数据写入屏幕寄存器地址1和2。
输入和输出数据源都选自定义1,执行动作-->属性新增。第一个执行动表示:将传感器键值107的数据,写入到屏幕w107。
0 0/30 * * * ?
表示每30分钟 执行任务,具体查看此文开头cron设置选择自定义输出,输入以下内容, 注意定向卡肯定域名受限的,可以手动修改一下其中的ntp服务器地址,网上自己找个
function()
sock.ntpSync("ntp.aliyun.com")
return {xx=1}
end
选择自定义输出,输入以下内容
function(tm)
local topic = "mytopic"
-- b字段传table, d字段传字符串, t字段传发布主题
return {b=tm, t=topic}
end