本帖最后由 pidtfork 于 2020-10-28 18:37 编辑
UBAINS中控设备级联操作
对于一个大型的项目有非常多的设备需要多台中控来控制,或者客户有在一台总中控上控制其他中控的这种场景需求。这时候就需要将中控级联起来控制。
串口线级联
使用串口线将两台中控的任意两个相同类型端口连接在一起,配置相同通信参数使之通信。然后自定义通信协议来调用具体功能。
例如设备A的 COM1 和设备B的 COM1用串口连接在一起,设备A为主设备,设备B为从设备。我们定义设备间通信都使用ASCII码字符串,设备B收到“open power”字符串时打开时序电源,“close power”时关闭时序电源。
这样我们就完成了一个简单的设备级联控制,通过定义不同字符来表示不同功能。这对于调用其他中控功能这是非常简单实用的。
网络级联
和串口一样使用类似,只不过是把串口换成了网口,因为UBAINS针对TCP连接做了优化封装处理,所以使用起来和串口一样简单。
主设备的NETx使用TCP/UDP指向从设备的IP和端口,从设备上添加一个TCP/UDP服务器,然后服务器收到特定数据执行不同操作。
思考
对于主设备调用从中控某些功能而言这种级联是非常简单的,大部分的场景下只需要这些功能就能满足需求了,即使需要小量的从设备的状态同步到主设备也不会增加很大的工作量。
对于主从设备上连接的设备在两台设备上都可以控制,全部设备状态也需要全部同步的情况。
•解决方案1
将从设备作为一台拓展器,所有的程序逻辑和控制界面都从主中控设备上提供,从设备只做配置端口参数,将设备指令稍微整合供主中控调用,处理设备返回指令并格式化主动推动给主中控。
这样优点是从中控设备对发送和接收的数据做了预处理,主中控使用简单的指令就可以控制设备和获取状态,也能分担一部分设备负载。缺点也很明显,一旦主中控掉电、网络通信异常,主从中控都无法正常使用。
实际情况是展览展示我们大量使用功能了此方案,拓展中控做预处理来封装功能,如灯光控制时,拓展中控可以将几十个通道封装开关封装成方法,里面包含一些逻辑运算和大量延时指令发送,这样也降低了主中控的负载,提供了代码可读性。
• 解决方案2
主设备、从设备先完成自身设备功能控制,然后将自身设备控制功能封装成接口给其他中控调用,设备状态也发送给其他中控,然后主从设备互相增加对方的功能和状态。这其实是相对麻烦的,而且对设备通信协议设置有一定要求的。
验证方案• 从中控设备
在拓展中控中实例化级联服务器并自定义TCP端口号,不需要再调用其他方法,允许被多台中控级联,也可实例化多个供不同中控连接。
- var dev1 = new deviceLinkServer(); // 默认端口号 23760
- // var dev1 = new deviceLinkServer(12780); //或者使用自定义端口
复制代码
• 主中控
根据拓展中控信息,实例化多个级联客户端用于控制级联中控。
通过dev2.run()方法来调用执行拓展中控方法,并传入参数。也可以将主中控的一个方法放到拓展中控去运行dev2.userRun(),参数是主中控的方法名;
- //实例化连接对象
- vardev2 = new DeviceLinkClient("192.168.10.207",12381,function(data){
- setButtonText("AAA",data);
- });
- //实例化连接对象2 不需要接收返回信息,可以不传回调函数
- // var dev2 = newDeviceLinkClient("192.168.10.207",12381);
- //实例化连接对象3 使用功默认端口 23760
- // var dev2 = newDeviceLinkClient("192.168.10.207");
- //调用中控存在的方法函数
- dev2.run("sendCodeString",COM1,"hello COM1"); //让COM1发送数据 hello COM1
- dev2.run("setRelayOn",1); //让继电器 1 闭合
- dev2.run("delayRelayOn",0.1,1); //延时0.1s让继电器 1 闭合
- function a(){
- sendLog("a","hello");
- b();
- }
- function b(data){
- sendLog("a",data)
- }
- //传入本地代码执行 此方法效率低不建议传入代码量很多的方法
- //方法的依赖不会传过去(传入了a方法,方法里调用了b)会导致运行报错
- dev2.userRun(a)
- dev2.userRun(b,"Iam b function")
复制代码
基本原理
发送端发送字符串,接收端将字符串转变为function对象传入参数并运行。
注释源码
- /**
- * deviceLinkServer 构造函数有两个主要功能
- * 1.通过接受到的函数名,调用执行自身系统的方法,允许传入参数。
- * 2.允许传入JavaScript代码解析并执行
- *
- * 数据通信使用GBK编码
- * 收到数据默认使用gbk格式,会将收到的数据转换为utf16
- *
- * @param {Number}port TCP端口
- */
- function deviceLinkServer(port) {
- this.port =port || 23760;
- this.clientID;
- this.receive = function(self){
- return function(receiveData,handle){
- try {
- //gbk to utf16
- self.clientID =handle;
- var receiveData = getTcpString(receiveData);
- var data = JSON.parse(utf8to16(setG2U(receiveData)));
- self.linkSend(self.route(data));
- } catch (error) {
- self.linkSend({type:"response",status:"fail",value:error.message});
- sendLog("error ","deviceLinkServer"+error);
- }
- }
- }
- this.route = function(data){
- //运行系统方法传过来是方法名
- if(data.type == "systemFunction"){ //cmd 函数名
- var remote = new Function(data.cmd + "("+ this.getParamsFormat(data.params)+");");
- remote();
- return {type:"response",status:"success",value:"systemFunction"};
- }
- //运行用户函数
- if(data.type == "userFunction"){
- // 远端整个方法 和 参数数组 无依赖函数
- var remote = new Function("("+data.cmd + ")("+this.getParamsFormat(data.params)+")");
- remote();
- return {type:"response",status:"success",value:"userFunction"};
- }
- }
- this.getParamsFormat = function(params){
- //将参数数组转换为参数字符串
- varparamsFormat = "";
- for(vari = 0; i < params.length; i++) {
- if (typeof params<i> == "string") {
- paramsFormat += '"' +params<i> + '",';
- } else {
- paramsFormat += params<i> + ","
- }
- }
- returnparamsFormat.substr(0,paramsFormat.length - 1);
- }
- this.linkSend = function(data){
- data.time = new Date().getTime();
- sendTcpServer(this.clientID,setGBK(JSON.stringify(data)));
- }
- setTcpServer(this.port,this.receive(this));
- }
- //在拓展中控中实例化级联服务器并自定义TCP端口号,不需要再调用其他方法
- //允许被多台中控级联,也可实例化多个供不同中控连接。
- //只允许客户端调用服务端方法执行,双向另添加客户端(通常用不上只有一个主中控)
- vardev1 = new deviceLinkServer(12780);
- // 不传入端口号时 默认端口号 23760
- // var dev1 = new deviceLinkServer();
- /**
- * 级联客户端,用于调用 deviceLinkServer 所在中控的方法,两个方法
- * run方法用于调用 deviceLinkServer 所在中控中的已有的方法
- * userRun 方法用于传入JavaScript 代码注入解析执行
- *
- * @param {String}ip IP地址
- * @param {Number}port 端口
- * @param {Function}backCall 回调
- * 配置远端的ip和端口 也可添加回调 收到数据为gbk
- */
- function DeviceLinkClient(ip,port,backCall) {
- this.ip =ip;
- this.port =port || 23760;
- this.backCall =backCall;
- this.receive = function(self){
- return function(data){
- if (typeof self.backCall == "function"){
- self.backCall(utf8to16(setG2U(data)));
- }
- }
- }
- this.dev = new _TCP(this.ip + ":" + this.port ,this.receive(this));
- this.run = function(functionName){
- if(typeoffunctionName == "string") {
- var data = {};
- data.cmd =functionName;
- data.type = "systemFunction";
- data.params = Array.prototype.slice.call(arguments,1);
- this.linkSend(data);
- }
- }
- this.userRun = function(functionName){
- if(typeoffunctionName == "function") {
- var data = {};
- data.cmd = ""+functionName;
- data.type = "userFunction";
- data.params = Array.prototype.slice.call(arguments,1);
- this.linkSend(data);
- }
- }
- this.linkSend = function(data){
- data.time = new Date().getTime();
- this.dev.sendCodeString(setGBK(JSON.stringify(data)));
- }
- }
- //实例化连接对象
- vardev2 = new DeviceLinkClient("192.168.10.207",12381,function(data){
- setButtonText("AAA",data);
- });
- //实例化连接对象2
- // var dev2 = newDeviceLinkClient("192.168.10.207",12381);
- //实例化连接对象3 使用功默认端口 23760
- // var dev2 = newDeviceLinkClient("192.168.10.207");
- //调用中控存在的方法函数
- dev2.run("sendCodeString",COM1,"hello COM1"); //让COM1发送数据 hello COM1
- dev2.run("setRelayOn",1); //让继电器 1 闭合
- dev2.run("delayRelayOn",0.1,1); //延时0.1s让继电器 1 闭合
- function a(){
- sendLog("a","hello")
- }
- function funB(data){
- sendLog("a",data)
- }
- //传入本地代码执行 此方法效率低不建议传入代码量很多的方法
- //方法的依赖不会传过去(传入了a方法,方法里调用了b)会导致运行报错
- //总之不建议使用
- dev2.userRun(a)
- dev2.userRun(funB,"Iam B function")
复制代码
|