15.4.10 通信指令
RL 程序中机器人与外部设备通信,支持以太网和串口两种方式。在指令集上,设计了一套统一的指令进行资源管理和数据收发,以提供一致的使用体验。
指令集 |
TCP 客户端 |
TCP 服务器 |
串口 |
OpenDev |
Y |
Y |
Y |
SocketAccept |
N/A |
Y |
N/A |
CloseDev |
Y |
Y |
Y |
SendString |
Y |
Y |
Y |
SendByte |
Y |
Y |
Y |
ReadBit |
Y |
Y |
Y |
ReadByte |
Y |
Y |
Y |
ReadDouble |
Y |
Y |
N/A |
ReadInt |
Y |
Y |
N/A |
ReadString |
Y |
Y |
Y |
GetSocketConn |
Y |
N/A |
N/A |
GetSocketServer |
N/A |
Y |
N/A |
GetBufSize |
N/A |
N/A |
Y |
ClearBuffer |
Y |
Y |
Y |
15.4.10.1 OpenDev
说明: 用于打开监听服务器、作为客户端发起连接、打开串口资源,取决于参数所表示的对象。
打开 SocketServer 对象时,将初始化资源,并且完成端口绑定和端口监听。
打开 SocketConn 对象时,机器人将作为 TCP 客户端,按照预设的 ip 和端口尝试连接外部服务器。
打开串口资源,将按照窗口参数初始化串口,准备好通信条件。
定义:
OpenDev(name);
-
name:数据类型 string,客户端对象或者服务端对象或者串口资源的名称。
示例:
例1
SocketConn scnn3={"192.168.0.200", 8090, "clt1", 2, "\n"};
Try
OpenDev("clt1") //尝试连接远程服务器,连接成功则 clt1 的 attr 自动修改为 outgoing。
string readstr = ReadString(30, "clt1");
..... //对 readstr 的逻辑处理
string sendstr = "helloserver!";
SendString(sendstr, "clt1"); //使用 clt1 的客户端连接来发送数据
... //一系列代码
catch(ERROR e); // ERROR 错误类型,包括产生错误的文件、行号、错误码、错误内容等
... //一系列异常处理
Endtry
例2
SocketServer listener1 = {"192.168.0.200", 8090, "svr1"};
global pers bool exit = false;
try
OpenDev("svr1"); //绑定端口、监听端口
while(exit! = true)
SocketConn conn = SocketAccept("svr1"); //阻塞式接收连入的客户端
Endwhile
catch(ERROR e);
... //一系列异常处理
Endtry
若发生报错,控制系统将抛出异常,并上报错误原因。如果异常没有被try代码块捕获,则控制系统将停止程序运行。
15.4.10.2 SocketAccept
说明: 阻塞等待客户端连接到来,并完成客户端连接处理。该指令仅在机器人作为 TCP 服务器时使用。
定义:
SocketConn conn = SocketAccept(name);
-
name:数据类型 string,已经准备好的 SocketServer 对象的名称,并且已经使用 OpenDev 打开成功。 -
返回值:数据类型:SocketConn,外部设备以 TCP 客户端形式连接上机器人后,控制系统产生一个通信对象,用于 RL 程序来控制通信读写。
示例:
SocketServer listener1 = {"192.168.0.200", 8090, "svr1"};
global pers bool exit = false;
try
OpenDev("svr1") //绑定端口、监听端口
while(exit! = true)
SocketConn conn = SocketAccept("svr1"); //阻塞式接收连入的客户端
conn.name = "client1"; //重要!给通信连接起个名,否则后续难以按名称进行数据读写
conn.suffix="\n";//可选,设置数据包结束符
Endwhile
catch(ERROR e)
... //一系列异常处理
Endtry
若发生报错,控制系统将抛出异常,并上报错误原因。如果异常没有被 try 代码块捕获,则控制系统将停止程序运行。
|
15.4.10.3 CloseDev
说明: 关闭资源,可用于关闭 TCP 通信链接、TCP 监听服务器或者串口资源。
定义:
CloseDev(name);
-
name:数据类型 string,用于通信的 SocketConn 连接、监听服务器 SocketServer 对象或者串口资源。
示例:
例1
SocketConn scnn3={"192.168.0.200", 8090, "clt1", 2, "\n"};
Try
OpenDev("clt1");
string readstr = ReadString(30, "clt1");
..... //对readstr的逻辑处理
string sendstr = "helloserver!";
SendString (sendstr, "clt1"); //使用 clt1 的客户端连接来发送数据
... //一系列代码
catch(ERROR e);
... //一系列异常处理
endtry
CloseDev("clt"); //无论是否发生错误,最后关闭 socket 客户端。
例2
SocketServer listener1 ={"192.168.0.200", 8090, "svr1"};
global pers bool exit = false;
try
OpenDev("svr1"); //绑定端口、监听端口
while(exit! = true)
SocketConn conn = SocketAccept("svr1"); //阻塞式接收连入的客户端
conn.name = "client1"; //重要!给通信连接起个名,否则后续难以按名称进行数据读写
conn.suffix = "\n"; //可选,设置数据包结束符
Endwhile
catch(ERROR e)
... //一系列异常处理
Endtry;
CloseDev("client1"); //关闭与外部 TCP 客户端通信。重要!
CloseDev("svr1"); //关闭监听服务器
示例 2 中,有两个网络对象存在,必须按照顺序,先关闭通信连接,再关闭服务器对象,否则将产生资源释放不彻底的状态(TCP TIME_WAIT状态)。
如果机器人做服务器时,已经与外部建立了多路通信连接,则需要在关闭服务器之前,先将这些通信连接依次关闭掉。
如果发生了资源释放不彻底,需要重启控制系统才能恢复。不过不必过于担心,控制系统中允许的资源数量有冗余,可以保证少量的资源被占用后,依然可以正常工作。但还是需要避免大量资源因为使用方式不正确而处于占用状态。
15.4.10.4 SendString
说明: 对外发送一个字符串。可以是通过网络或串口,取决于参数中的标识符所表示的硬件资源。
定义:
SendString(StringData, name);
-
StringData:数据类型 string,待发送的字符串数据。 -
name:数据类型 string,用于发送数据的硬件资源名称,可以是建立好的 TCP 通信连接 SocketConn 对象,也可以是已经成功打开的串口资源。
示例:
例1
SendString(“HelloWorld”, “Socket0”);
通过 Socket0 对外发送 Hello World 字符串。其中 Socket0 是已经定义并连接成功的 SocketConn 类型。
例2
VAR String str1 = “Hello World”;
SocketSendString(str1, “Serial1”);
通过 Serial1 对外发送 str1 存储的字符串 Hello World。其中 Serial1 是已经定义并成功打开的串口。
15.4.10.5 SendByte
说明: 对外发送一个字节 byte,在需要发送 ASCII 字符时非常有用。
定义:
SendByte(ByteData, name, [length]);
-
ByteData:数据类型 int 或 byte 或 byte 数组,发送一个 0~255 的无符号字节或数组,主要用于发送 ASCII 码。 -
name:数据类型 string,用于发送数据的 Socket 或者串口名称。 -
length:数据类型 int,用于指定发送 byte 数组的前若干字节,可选参数,如省略,则默认发送数据的全部字节。
示例:
例1
SendByte(13, “socket0“);
通过 socket0 对外发送一个回车符。
例2
VAR byte data1 = 13;
SendByte(data1, “serial0“);
首先定义一个 byte 类型变量 data1,它其实是一个回车符。然后通过 serial0 对外发送。
例3
VAR byte data2[2] = {13, 17};
SendByte(data2, “socket0”);
通过 socket0 发送一个 byte 类型数组变量 data2。会将数组中的内容全部发出去。
例4
VAR byte data2[2] = {13,17,20};
SendByte(data2[2], “socket0”);
通过 socket0 发送一个 byte 类型变量,该变量为 data2[2],仅表示数组中的第 2 个元素。此时会将 data2[2] 的值 17 发出去,而不会多发任何其他元素。
例5
SendByte(byte_array, "client", 5); //发送 byte_array 的前 5 个字节
15.4.10.6 ReadBit
说明: 控制系统按 bit 接收数据。
1)通过网络通信以 TCP 形式接收,外部发送的数据需以 SocketConn 配置好的结束符结尾。
2)通过串口通信方式接收,外部设备只需要发送数据部分即可,无需考虑结束符。
定义:
Ret = ReadBit(BitNum, TimeOut, name);
-
BitNum:数据类型 int,需要读取的 bit 数量,大小应该为 8 的整数倍。 -
TimeOut:数据类型 int,超时时间。单位 s,范围 0~86400,默认 60s。 -
name:数据类型 string,通信连接 SocketConn 的名称或者串口的名称。 -
Ret:数据类型 bool 数组,接收到的数据,数组第一个元素表示最低位。 -
返回值,数据类型: bool 数组,使用 bool 型数组存储接收到的 bit 数据,每一个 bit 对应一个 bool 成员。
示例:
bool groupio[16];
groupio = ReadBit(16, 60, “Socket0”);
通过 ReadBit 指令读取 16 个 bit 的数据存储到名为 groupio 的布尔型数组中,超时时间 60s。
假设外部设备发送 ASCII 字符:95+结束符,则机器人接收到“95”。其中“9”的十六进制为 0x39,“5”的十六进制为 0x35,因此用户接收到的数据为 0x3935。此时 groupio 数组从[1]~[16]表示为:1001 1100 1010 1100。[1]是数据低位,与 0x3935 符合。
15.4.10.7 ReadByte
说明: 接收一定字节的数据。注意数据之间需要使用逗号隔开。
定义:
Ret = ReadByte(ByteNum, TimeOut, name);
-
ByteNum:数据类型 int,需要读取的 Byte 数量。 -
TimeOut:数据类型 int,超时时间。单位 s,范围 0~86400,默认 60s。 -
name:数据类型 string,通信连接 SocketConn 的名称或者串口的名称。 -
Ret:数据类型 byte 数组,接收到的数据。 -
返回值,数据类型:byte 数组,使用 byte 型数组存储接收到的数据。
示例:
byte rets[6]={0,0,0,0,0,0};
rets = ReadByte(6,60,"clt1");
读取 6 个 byte 的数据存储到名为 rets 的 byte 类型数组中,超时时间 60s。
|
15.4.10.8 ReadRawByte
说明: 当 socket 不使用结束符时(结束符设置为空),通过该指令直接从数据缓冲区读取二进制数据。
与 ReadByte 指令区别:不需要设置结束符、字节之间不需要使用逗号进行分割。
定义:
bytes = ReadRawByte(length,timeout,socket_name)
-
bytes:用于存放Read RawByte 读取的数据。数据类型,byte 数组。 -
length:读取字节长度。数据类型,int。 -
timeout:超时时间。数据类型,int。 -
socket_name:socket 名称。数据类型,string。
示例:
// 前提:变量列表中新建socketserver变量。
GLOBAL PROC main()
CloseDev("server");
Wait(0.50);
OpenDev("server");
SocketConn conn = SocketAccept("server");
conn.name = "client";
conn.suffix = ""; //结束符为空
byte byte_array[10] = {0,0,0,0,0,0,0,0,0,0};
while(true)
byte_array = ReadRawByte(4,20,"client");//读取 byte 数据,存到 byte_array 中
print(byte_array);
intres = 0;
BytesToData(byte_array,1,res,"int32");
print("byte转int的结果",res);
wait(1);
endwhile
ENDPROC
运行程序,通过网络助手,以十六进制发送数据:
HMI输出结果
十六进制 22,表示十进制的 34。
15.4.10.9 ReadDouble
说明: 通过 Socket 接收 double 型数据,外部发送的数据需以配置好的结束符结尾。
注意,该指令仅对 TCP 网络通信生效,适用于机器人做客户端和服务端,但不适用于串口。
定义:
Ret = ReadDouble(DoubleNum,TimeOut,name);
-
DoubleNum:数据类型:double,需要读取的 double 数个数,最大为 4096 个。 -
TimeOut:数据类型:int,超时时间。单位 s,范围 0 ~86400,默认 60s。 -
name:数据类型:string,用于接收数据的 Socket 名称。 -
返回值,数据类型:double 数组,使用 double 型数组存储接收到的数据。
示例:
double dd[10];
dd = ReadDouble(10,60, “Socket0”) ;
读取 10 个 double 型的数据存储到名为 dd 的 double 型数组中,超时时间 60s。
15.4.10.10 ReadInt
说明: 通过 Socket 接收 int 型数据,外部发送的数据需以配置好的结束符结尾。
注意,该指令仅对 TCP 网络通信生效,适用于机器人做客户端和服务端,但不适用于串口。
定义:
Ret = ReadInt(IntNum,TimeOut,name);
-
IntNum:数据类型:int,需要读取的 int 数个数,最大为 4096 个。 -
TimeOut:数据类型:int,超时时间。单位 s,范围 0 ~86400,默认 60s。 -
name:数据类型:string,用于接收数据的 Socket 名称。 -
返回值,数据类型:int,使用 int 型数组存储接收到的数据。
示例:
int ii[10];
ii = ReadInt(10,60,“Socket0”);
读取 10 个 int 型的数据存储到名为 ii 的 int 型数组中,超时时间 60s。
15.4.10.11 ReadString
说明: 读取字符串并返回,外部发送的数据需以配置好的结束符结尾。
定义:
Ret = ReadString(TimeOut,name, [len]);
-
TimeOut:数据类型: int,超时时间。 单位 s, 范围 0~86400,默认 60s。 -
name:数据类型:string,用于接收数据的 Socket 或者串口名称。 -
len:数据类型 int,可选参数,使用串口读取时才使用。由于串口中没有加入结束符的概念,因此需要指定长度后才能正确读取和解析。 -
返回值,数据类型:string,存储接收到的字符串。
示例:
例1
VAR String str1;
str1 = ReadString(60, “Socket1”);
从 Socket1 中接收一个字符串,并存储到 str1 中,超时时间 60s。网络通信方式。
例2
VAR String str1;
str1 = ReadString(60, “serial0”,5);
从 serial0 中接收一个长度为 5 的字符串,并存储到 str1 中,超时时间 60s。串口通信方式。
15.4.10.12 GetSocketConn
说明: 从 socket 连接名称查找对应的 socket 属性集对象。
该指令获取的结果可以用来进行判断和处理逻辑。仅应该将其当作只读对象使用。
该指令仅适用于通信连接(包括机器人作为客户端,或者作为服务端已经连接上的用于通信的通道),不适用于监听服务器和串口。
定义:
Ret = GetSocketConn(name);
-
name:数据类型 string,通信连接 SocketConn 的名称。 -
Ret:数据类型:SocketConn,通过字面名称查找到的 socket 属性对象。 -
返回值:数据类型:SocketConn,通过字面名称查找到的 socket 属性对象。
可查询属性 |
查询方法 |
含义和示例 |
ip 地址 |
ret.ip |
字符串,如 "192.168.0.161" |
端口号 |
ret.port |
整型,如 8090 |
属性 |
ret.attr |
机器人做服务器:“incoming”;机器人做客户端:“outgoing”;如果连接未建立:“”或者其他值,通常为空 |
缓存大小 |
ret.cache |
1~100 |
名称 |
ret.name |
连接名称 |
连接状态 |
ret.state |
closed、establish |
示例:
SocketConn ret = GetSocketConn(“client0”);
查找名称为“client0”的 SocketConn 对象。可以使用 ret 来获得这个连接的属性,包括 ip 地址、端口号、通信结束符、连接状态等信息。
15.4.10.13 GetSocketServer
说明: 从用户定义名称查找对应的服务器属性集对象。
该指令获取的结果可以用来进行判断和处理逻辑。仅应该将其当作只读对象使用。
该指令仅适用于监听服务器(SocketServer 对象)不适用于通信连接(包括机器人作为客户端,或者作为服务端已经连接上的用于通信的通道)和串口。
定义:
Ret = GetSocketServer(name);
-
Name:数据类型:string,通信连接 SocketServer 的名称。 -
Ret:数据类型:SocketServer,通过字面名称查找到的 socket 属性对象。 -
返回值,数据类型:SocketServer,通过字面名称查找到的服务器属性对象。
可查询属性 |
查询方法 |
含义和示例 |
ip 地址 |
ret.ip |
字符串,如 "192.168.0.161" |
端口号 |
ret.port |
整型,如 8090 |
名称 |
ret.name |
示例为“svr1” |
连接状态 |
ret.state |
closed、listening、error |
示例:
SocketServer listener1 = {"192.168.0.200",8090,"svr1"};
OpenDev("svr1"); //绑定端口、监听端口
//根据连接标识"svr1 "获取 SocketServer 对象,此时 ret 将复制任务 1 中 listener1 的全部状态
SocketServer ret = GetSocketServer("svr1");
if(ret.state == "listening") //使用 SocketServer 的 attr 属性,判断是否监听中
//逻辑处理
endif
15.4.10.14 GetBufSize
说明: 获取串口缓冲区中还剩余多少数据未读,单位为字节。指令仅适用于串口,不适用于 TCP 服务器和客户端。
定义:
Ret = GetBufSize(name);
-
name:数据类型:string,串口资源的字面名称。 -
Ret:数据类型:int,缓冲区中未处理数据量,以字节为单位。 -
返回值,数据类型:int,缓冲区中未处理数据量,以字节为单位。
示例:
OpenDev("serial0");
int a = GetBufSize("seria0");
Print(a);
15.4.10.15 ClearBuffer
说明: 清除对应连接的缓冲区,未读取完毕的数据将丢失。支持串口和 socket 数据。已经被结束符分割完成的数据和还未被分割数据都将被清除。
定义:
ClearBuffer(name);
-
name:数据类型:string,链接的字面名称。
示例:
// 假设结束符为\r,已经接收 2 份数据和一份正在接收的数据,执行该指令后,缓冲区内的所有数据都将被清空,RL 程序只能读取到清空之后重新发送的数据。
123456789\r
Abcdefg\r
15.4.10.16 ReadOpcUaVarByName
说明: 通过名称读取 OPC-UA 自定义变量的值。
定义:
ReadOpcUaVarByName(name, value);
-
name:数据类型 string,OPC-UA 自定义变量名称。 -
value:数据类型 bool/byte/int/double/string,用于存放读取到的 OPC-UA 自定义变量的值,如果 value 的类型与 opcua 变量的类型不一致,将自动转换,注意:string 转换为数值类型时,都为 0。 -
无返回值。
示例:
int value = 0;
ReadOpcUaVarByName("int_var", value);
Print(value);
15.4.10.17 WriteOpcUaVarByName
说明: 通过名称修改 OPC-UA 自定义变量的值。
定义:
WriteOpcUaVarByName(name, value);
-
name:数据类型:string, OPC-UA 自定义变量名称。 -
value:数据类型:bool/byte/int/double/string, OPC-UA 变量的修改值,如果 value 的类型与 opcua 变量的类型不一致,将自动转换,注意:string 转换为数值类型时,都为 0。
示例:
int value =0;
WriteOpcUaVarByName(“int_var”,value);
WriteOpcUaVarByName(“int_var”,123);