跳到主要内容

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 代码块捕获,则控制系统将停止程序运行。

  • 指令将阻塞当前任务,因此正确的使用方法是在多任务中使用用,单独作为一个低优先级任务持续不断接收并产生通信连接对象 SocketConn。

  • 指令返回一个连接操作对象,并且拥有客户端连接 ip 和 port 信息,可供程序其他部分使用。返回的连接对象是一个 SocketConn 结构体,名称由系统随机分配。拿到连接对象以后,请更改连接对象的名称,避免连接丢失。

  • 支持服务器一对多连接。

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。

  • 注意外部设备的 byte 数据之间需要使用逗号隔开,比如发送”1,2,3,4,5,6”。

  • 通过 TCP 发送数据,需要在数据后面加上预设好的结束符。

  • 通过串口发送数据,不需要结束符。

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

运行程序,通过网络助手,以十六进制发送数据:
15.4.10.8.1
HMI输出结果
15.4.10.8.2
十六进制 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);