C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)
1、介绍
对于上位机开发来说,欧姆龙PLC支持的主要的协议有Hostlink协议,FinsTcp/Udp协议,EtherNetIP协议,本项目使用Hostlink协议。
Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令,可以对PLC进行I/O读写、可以对PLC进行I/O读写、改变操作模式、强制置位/复位等操作。由于是公开协议,即便是非欧姆龙的上位设备(软件),都可以通过该协议和欧姆龙PLC实现通信链接,
Hostlink通讯协议有两种模式:C-mode和FINS
上个文章采用C-mode方式,本篇采用FINS方式
1.Cmode:专用于hostlink通讯指令,采用的是ASCII码,适用于所有OMRON的PLC通讯。只能上位机发出指令给CPU,CPU无法主动发数据给上位机,只可以通过串口进行通讯。
2.FINS:采用的二进制码,适用于新开发的PLC,可用在多种网络设备(Controller Link, Ethernet, etc),可被 CPU、IO模块、上位机主动发出,不同的指令分别适用于不同的信息接受单元。有两种链接协议:CMND和hostlink,当上位机是做发送源时,必须采用hostlink协议,既可以通过串口通讯也可以通过各类网络通讯(适应性较强)。
欧姆龙PLC与上位机连接时一般采用的是Hoslink协议,它是一种简易经济的通讯方式,比较适合一台上位机和一台PLC或者多台PLC进行通讯。上位机可对PLC进行程序传送和读写等操作。HOSTLINK系统允许一台上位机通过上位机链接命令向HOSTLINK系统的PLC发送命令,PLC处理来自上位机的每条指令,并把结果传回上位机。
2、欧姆龙Fins协议
欧姆龙FINS协议:现代工业自动化领域的通信利器,欧姆龙FINS协议,全称为”Factory Interface Network Service",是日本欧姆龙(Omron)公司开发的一套高效的工业自动化通信协议。它作为欧姆龙工业设备之间的通信接日,被广泛应用于全球范围内的工业自动化领域。
支持以太网的欧姆龙PLC CPU、以太网通信模块根据型号的不同,一般都会支持FINS(Factory Interface Network Service)协议,一些模块也会支持EtherNet/IP协议。Fins协议封装在TCP/UDP之上。
下图表述了FINS会话开始几个数据帧的作用。FINS协议的会话有一次请求帧,请求帧中附带着发起方的节点参数。PLC端(Server端)会确认并将自己的节点参数放回给请求方。

1、Fins协议和我们常用的ModbusTCP协议不同,首先体现在有握手验证命令,就是说在正常的三次握手的基础上,还需要进行一次握手,如果能正常握手,方可通信,这样做的目的也是为了PLC通信安全。
命令均为十六进制 字节数据流
2、Fins握手命令
发送报文格式:
返回报文格式:

3、Fins通信命令
Fins通信协议的整体命令如下所示

4、Fins读取数据
Fins读取数据的报文会在通用命令的基础上,将 Parameter替换为 Area+Address+Length,因此读取数据命令如下所示:
读取数据返回命令如下所示:

5、Fins写入数据
Fins写入数据会在通用命令的基础上,将 Parameter替换为 Area+Address+Length+Value,因此写入数据命令如下所示:

写入数据返回命令如下所示:
3、开搞Fins协议
1、确认环境
确认你的cx-simulator的串口设置已经设置好虚拟串口工具软件的com2

2、运行网络调试工具NetAssist.exe


3、发送握手报文
首先要有握手验证命令,就是说在正常的三次握手的基础上,还需要进行一次握手,如果能正常握手,方可通信,这样做的目的也是为了PLC通信安全。

发送:46 49 4E 53 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 04
接收:46 49 4E 53 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 04 00 00 00 0A
发送报文格式:

1)发送报文分析:
发送:46 49 4E 53 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 04
接收:46 49 4E 53 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 04 00 00 00 0A
46494E53:转换成ASCII编码就是FINS;
0000000C:指后面跟的字节长度;12个字节,不包括自己的字节长度,也就是后面的 000000000000000000000004,它有12个字节
00000000:固定命令;
00000000:错误代码;
00000004:PC节点IP,当设置为0时,会自动获取节点IP
当前我的机器IP地址是:

格式中的clientnode就是指PC的IP地址中的最后一位,也就是4,转换成16进制就是4,它占4个字节,所以是 00000004,每2位算一个字节。其实这个地址写什么都可以,PLC并不会去校验客户端的IP地址,只是一个表示占位格式而已。
发送报文共占20个字节。
2)接收报文分析如下:
报文格式:

发送:46 49 4E 53 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 04
接收:46 49 4E 53 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 04 00 00 00 0A
46494E53:ASCII编码就是FINS;
00000010:指后面跟的字节长度;16个字节,不包括自己的长度
00000001:固定命令;
00000000:错误代码;
00000004:本机电脑节点IP;
0000000A:PLC节点IP
servernode就是PLC的IP地址的最后一位,其实clientnode和servernode写什么都可以,也只是表示一个占位。
接收报文共24个字节,比发送多了servernode(4个字节)

4、读取和写入CIO区的2进制数据

设置CIO0.0到0.5的6个数据,分别是111101
1、读取指令,读取CIO区0.0开始的6个bool数据

发送:46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 30 00 00 00 00 06
接收:46 49 4E 53 00 00 00 1C 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 01 01 01 01 00 01
1)发送:46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 30 00 00 00 00 06
发送报文格式

发送报文分析如下:
46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 30 00 00 00 00 06
46494E53:ASCII编码:FINS;
0000001A:指后面跟的字节长度;26个字节,不包括自己的长度
00000002:固定命令;
00000000:错误代码,00000000表示没有错误
80:ICF,固定值0x80;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址,PLC的IP地址最后一位
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址,客户端IP地址最后一位
00:PC单元地址;
00:SID,固定值;
0101:读指令;
30:读地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0);
000000:起始地址,占3个字节
0006:读个数,占2个字节
--------------------------------------------------------------------------------------------------------------------------------
2)接收:46 49 4E 53 00 00 00 1C 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 01 01 01 01 00 01
接收报文格式

接收报文分析如下:
46 49 4E 53 00 00 00 1C 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 01 01 01 01 00 01
46494E53:ASCII编码:FINS;
0000001C:指后面跟的字节长度,1C转换成10进制就是28,就是指后面的字节长度是28个(56个字符,每2个字符为1个字节)
00000002:固定命令;
00000000:错误代码,全是0表示没有错误;
C0:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0101:读指令;
0000:读取成功标识;
01 01 01 01 00 01:读到的数据,这是2进制的数据,也就是C#中的bool类型,表示true,true,true,true,false,true,与内存存储区的数据一致。

2、写入指令,写入CIO区0.0开始的6个bool数据true, true, false, false, true,true

发送:46 49 4E 53 00 00 00 20 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 30 00 00 00 00 06 01 01 00 00 01 01
接收:46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00

1)发送报文分析如下:
写入命令格式:

发送:46 49 4E 53 00 00 00 20 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 30 00 00 00 00 06 01 01 00 00 01 01
46494E53:ASCII编码:FINS;
00000020:指后面跟的字节长度,20转换成10进制就是32,表示后面有32个字节(64个字符);
00000002:固定命令;
00000000:错误代码,全0表示没有错误;
80:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
30:写地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0);
000000:起始地址,即0.0开始的地址
0006:写入个数,占2个字节
01 01 00 00 01 01 :写入数据,具体来说表示true, true, false, false, true,true
2)接收报文分析如下:
返回命令如下

46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
46494E53:ASCII编码:FINS;
00000016:指后面跟的字节长度,表示后面的字节有22个字节
00000002:固定命令;
00000000:错误代码;
C0:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
0000:写入成功标识,也就是说写入成功啦,没有错误。
以上读取或写入命令之前都必须先发送握手报文命令,而且只能发一次握手报文
5、读取和写入D区的ushort类型数据
首先设置内存区D区100的4个数据,注意D区设置的数据类型为10进制无符号的整数,对应C#中就是ushort类型。

1、读取D区100开始的4个数据

发送:46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 82 00 64 00 00 04
接收:46 49 4E 53 00 00 00 1E 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 00 7B 00 87 00 92 03 84
1)发送报文分析如下:
46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 82 00 64 00 00 04
46494E53:ASCII编码:FINS;
0000001A:指后面跟的字节长度;26个字节,不包括自己的长度
00000002:固定命令;
00000000:错误代码,表示没有错误
80:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址,PLC的IP地址最后一位
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址,客户端IP地址最后一位
00:PC单元地址;
00:SID;
0101:读指令;
82:读地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0);
006400:起始地址,100转成16进制就是64,占3个字节,就是006400
0004:读取的个数,占2个字节
2)接收报文分析如下:
46 49 4E 53 00 00 00 1E 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 00 7B 00 87 00 92 03 84
46494E53:ASCII编码:FINS;
0000001E:指后面跟的字节度
00000002:固定命令;
00000000:错误代码,全是0表示没有错误;
C0:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0101:读指令;
0000:读取成功标识;
00 7B 00 87 00 92 03 84:读到的数据,00 7B转换成10进制就是123。00 87就是135,00 92就是146,03 84就是900
这里注意10进制的无符号整数占2个字节,也就是PLC中的一个寄存器,PLC中的word,占一个寄存器,在C#占2个字节,用来表示整数(不用带小数)

2、向D区30的地址写入4个ushort数据110, 120, 130, 140

发送:46 49 4E 53 00 00 00 22 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 82 00 1E 00 00 04 00 6E 00 78 00 82 00 8C
接收:46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
写入成功

发送报文分析
46 49 4E 53 00 00 00 22 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 82 00 1E 00 00 04 00 6E 00 78 00 82 00 8C
46494E53:ASCII编码:FINS;
00000022:指后面跟的字节长度 ;
00000002:固定命令;
00000000:错误代码,全0表示没有错误;
80:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
82:写地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0);
001E00:起始地址,即0.0开始的地址
0004:写入个数,占2个字节
00 6E 00 78 00 82 00 8C:写入数据,具体来说表示00 6E=》110, 00 78=》120, 00 82=》130, 00 8C=》140
接收报文分析
46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
46494E53:ASCII编码:FINS;
00000016:指后面跟的字节长度,表示后面的字节有22个字节
00000002:固定命令;
00000000:错误代码;
C0:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
0000:写入成功标识,也就是说写入成功啦,没有错误。
以上读取或写入命令之前都必须先发送握手报文命令,而且只能发一次握手报文
6、读取和写入H区的short类型数据
设置H区的4个数据

1、读取H区100开始的4个short类型的数据

发送:46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 B2 00 64 00 00 04
接收:46 49 4E 53 00 00 00 1E 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 00 6E 00 6F FF 90 FF 8F
1)发送报文分析如下:
46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 B2 00 64 00 00 04
46494E53:ASCII编码:FINS;
0000001A:指后面跟的字节长度;26个字节,不包括自己的长度
00000002:固定命令;
00000000:错误代码,表示没有错误
80:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址,PLC的IP地址最后一位
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址,客户端IP地址最后一位
00:PC单元地址;
00:SID;
0101:读指令;
B2:读地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0,H字:B2);
006400:起始地址,100转成16进制就是64,占3个字节,就是006400
0004:读取的个数,占2个字节
2)接收报文分析如下:
46 49 4E 53 00 00 00 1E 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 00 6E 00 6F FF 90 FF 8F
46494E53:ASCII编码:FINS;
0000001E:指后面跟的字节度
00000002:固定命令;
00000000:错误代码,全是0表示没有错误;
C0:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0101:读指令;
0000:读取成功标识;
00 6E 00 6F FF 90 FF 8F:读到的数据,00 6E转换成10进制就是110。00 6F就是111,FF 90就是65424,FF 8F就是65423,这里要将65424-65536=-112,同样65423-65536=-113,short的表示范围是 -32,768 到 32,767,表示有符号 16 位整数,ushort:代表有符号的16位整数,范围从0 到 65,535,这就也说明了,我们需要对返回的数据进行有效的处理,这个留着在后面的通讯库封装代码中去处理。

2、向H区30的地址写入4个short数据,分别是-98,654,-800,327


发送:46 49 4E 53 00 00 00 22 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 B2 00 1E 00 00 04 FF 9E 02 8E FC E0 01 47
接收:46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
发送报文分析
46 49 4E 53 00 00 00 22 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 B2 00 1E 00 00 04 FF 9E 02 8E FC E0 01 47
46494E53:ASCII编码:FINS;
00000022:指后面跟的字节长度 ;
00000002:固定命令;
00000000:错误代码,全0表示没有错误;
80:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
82:写地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0);
001E00:起始地址,即0.0开始的地址
0004:写入个数,占2个字节
FF 9E 02 8E FC E0 01 47:写入数据,具体来说表示FF 9E=》-98, 02 8E=》654,FC E0 =》-800,01 47=》327
同样的可以看到写入的负数,前面的第一个是F,这是因为负数用补码表示。
接收报文分析
46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
46494E53:ASCII编码:FINS;
00000016:指后面跟的字节长度,表示后面的字节有22个字节
00000002:固定命令;
00000000:错误代码;
C0:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
0000:写入成功标识,也就是说写入成功啦,没有错误。
以上读取或写入命令之前都必须先发送握手报文命令,而且只能发一次握手报文
7、读取和写入W区的float类型数据
float类型的数据是包括正负整数和正负小数,如40,-98,2.34,-89.53
设置W区的5个数据

1、读取W区100开始的5个float数据

发送:46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 B1 00 64 00 00 0A
接收:46 49 4E 53 00 00 00 2A 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 47 AE 3F 81 8F 5C BF 82 00 00 42 F6 00 00 C4 75 C0 00 44 02
1)发送报文分析如下:
46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 01 B1 00 64 00 00 0A
46494E53:ASCII编码:FINS;
0000001A:指后面跟的字节长度;26个字节,不包括自己的长度
00000002:固定命令;
00000000:错误代码,表示没有错误
80:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址,PLC的IP地址最后一位
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址,客户端IP地址最后一位
00:PC单元地址;
00:SID;
0101:读指令;
B1:读地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0,H字:B2);
006400:起始地址,100转成16进制就是64,占3个字节,就是006400
000A:读取的个数,占2个字节,这里为什么是A,即10个,因为一个float类型是占2个寄存器的,那么5个float就是10个寄存器,所以是A,需要注意这个不同的地方。
2)接收报文分析如下:
46 49 4E 53 00 00 00 2A 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 01 00 00 47 AE 3F 81 8F 5C BF 82 00 00 42 F6 00 00 C4 75 C0 00 44 02
46494E53:ASCII编码:FINS;
0000001E:指后面跟的字节度
00000002:固定命令;
00000000:错误代码,全是0表示没有错误;
C0:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0101:读指令;
0000:读取成功标识;
47 AE 3F 81 8F 5C BF 82 00 00 42 F6 00 00 C4 75 C0 00 44 02:读到的数据,47 AE 3F 81是第一个数据, 8F 5C BF 82是第二个数据,00 00 42 F6是第三个数据,00 00 C4 75是第四个数据,C0 00 44 02是第五个数据,这里为什么是这样的数据,如果转成10进制都不是当初设置的1.01,-1.02,123,-980,523,这是因为存在大小端,字节序的问题,这需要在程序后端进行处理,这里返回的数据是没有问题的,等后面在我的封装通讯库的进行处理,保证响当当的没有问题。
2、向W区30的地址写入5个数据120, -130, -140,15.6f,-89.4f


发送:46 49 4E 53 00 00 00 2E 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 B1 00 1E 00 00 0A 00 00 42 F0 00 00 C3 02 00 00 C3 0C 99 9A 41 79 CC CD C2 B2
接收:46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
发送报文分析
46 49 4E 53 00 00 00 2E 00 00 00 02 00 00 00 00 80 00 02 00 0A 00 00 04 00 00 01 02 B1 00 1E 00 00 0A 00 00 42 F0 00 00 C3 02 00 00 C3 0C 99 9A 41 79 CC CD C2 B2
46494E53:ASCII编码:FINS;
0000002E:指后面跟的字节长度 ;
00000002:固定命令;
00000000:错误代码,全0表示没有错误;
80:ICF,固定值;
00:RSV,固定值;
02:GCT,固定值;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
B1:写地址区(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0);
001E00:起始地址,
000A:写入个数,占2个字节,要写入5个float的,每个float占2个寄存器,5个就是10个寄存器,所以是A;
00 00 42 F0 00 00 C3 02 00 00 C3 0C 99 9A 41 79 CC CD C2 B2:写入数据,具体来说00 00 42 F0是第一个, 00 00 C3 02是第二个, 00 00 C3 0C是第三个, 99 9A 41 79是第四个, CC CD C2 B2是第五个。但这5个数据都不是120, -130, -140,15.6f,-89.4f转换成16进制的表示,这也是因为存在大小端,字节序的问题,在后面的通讯库封装中来处理,发送的报文指令绝对没有错。
接收报文分析
46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 0A 00 00 04 00 00 01 02 00 00
ASCII编码:FINS;
00000016:指后面跟的字节长度,表示后面的字节有22个字节
00000002:固定命令;
00000000:错误代码;
C0:ICF;
00:RSV;
02:GCT;
00:PLC网络地址;
0A:PLC节点地址;
00:PLC单元地址;
00:PC网络地址;
04:PC节点地址;
00:PC单元地址;
00:SID;
0102:写指令;
0000:写入成功标识,也就是说写入成功啦,没有错误。
以上读取或写入命令之前都必须先发送握手报文命令,而且只能发一次握手报文
4、小结
这节,我们读取和写入了CIO区,D区,H区,W区的二进制数据,ushort数据,short数据,float数据,充分熟悉了fins指令的格式要求,都有固定的格式。
FINS协议是一种基于TCP/IP网络通信的实时通信协议.能够实现欧姆龙工业设备之间的数据交换和控制指令传输。它采用了客户端/服务器模型,客户端发送请求,服务器返回响应,通过建立连接、传输数据包来实现设备之间的通信。
FINS协议的核心是消息和指令的传输。消息格式包括消息头、命令和数据区。消息头定义了消息的类型、长度和地址等信息。命令部分包含了具体的指令内容,如读取数据、写入数据或执行其他操作。数据区则是存储实际的数据信息。
注意几点:
1)读取命令0101,写入命令0102,固定的,不能改
2)存储区代号:(D位:02,D字:82,W位:31,C位:30,W字:B1,C字:B0,H字:B2),固定的不能改
3)协议头部:46 49 4E 53,转换成ascii码就是fins
码字不易,截图不易,写作不易,共享不易,请多多点赞支持。

相关文章:
C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)
1、介绍 对于上位机开发来说,欧姆龙PLC支持的主要的协议有Hostlink协议,FinsTcp/Udp协议,EtherNetIP协议,本项目使用Hostlink协议。 Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令,可…...
认识SpringBoot项目中的Starter
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏&…...
ChatGPT 4.0真的值得花钱买入吗?
性能提升: ChatGPT 4.0的推出不仅意味着更先进的技术,还代表着更强大的性能。相较于3.5,4.0在处理任务时更为高效,响应更迅速。 更智能的理解: 随着版本的升级,ChatGPT 4.0对语境的理解能力得到了进一步的…...
vue3对比vue2是怎样的
一、前言 Vue 3通过引入Composition API、升级响应式系统、优化性能等一系列的改进和升级,提供了更好的开发体验和更好的性能,使得开发者能够更方便地开发出高质量的Web应用。它在Vue.js 2的基础上进行了一系列的改进和升级,以提供更好的性能、更好的开发体验和更好的扩展性…...
openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证
文章目录 openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证184.1 验证项目的检查表184.2 升级版本查询184.2.1 验证步骤 184.3 检查升级数据库状态184.3.1 验证步骤 openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证 本章介绍升级完成后的验证操作。给出验…...
[Verilog语言入门教程] Verilog 减法器 (半减器, 全减器, 加减共用)
依公知及经验整理,原创保护,禁止转载。 专栏 《元带你学Verilog》 <<<< 返回总目录 <<<< “逻辑设计是一门艺术,它需要创造力和想象力。” - 马克张伯伦(Mark Zwolinski) 减法器是数字电路中常见的组件,用于减去两个二进制数的和。 在Verilog中…...
预编译仓库中的 Helm Chart
背景 内网部署项目, 没法直接hlem install , 需要提前看看有哪些镜像, 拉到本地看看 要使用预编译仓库中的 Helm Chart,你可以使用 helm fetch 命令来将 Chart 下载到本地,并使用 helm template 命令来预编译该 Chart。 首先,你可以使用以…...
Python requests get和post方法发送HTTP请求
requests.get() requests.get() 方法用于发送 HTTP GET 请求。下面介绍 requests.get() 方法的常用参数: url: 发送请求的 URL 地址。params: URL 中的查询参数,可以是字典或字符串。headers: 请求头信息。可以是字典类型,也可以是自定义的…...
在Cadence中单独添加或删除器件与修改网络的方法
首先需要在设置中使能 ,添加或修改逻辑选项。 添加或删除器件,点击logic-part,选择需要添加或删除的器件,这里的器件必须是PCB中已经有的器件,Refdes中输入添加或删除的器件标号,点击Add添加。 添加完成后就会显示在R1…...
轻松调整视频时长,创意与技术的新篇章
传统的视频剪辑工具往往难以精确控制时间,而【媒体梦工厂】凭借其先进的算法和界面设计,让视频时长的调整变得简单而精确,助你释放无限的创意,用技术为你的创意插上翅膀,让每一秒都有意义。 所需工具: 一…...
树与二叉树笔记整理
摘自小红书 ## 树与二叉树 ## 排序总结...
如何自动生成 API 接口文档 - 一份详细指南
本篇文章详细教你如何使用 Apifox 的 IDEA 插件实现自动生成接口代码。好处简单总结有以下几点: 自动生成接口文档: 不用手写,一键点击就可以自动生成文档,当有更新时,点击一下就可以自动同步接口文档;代码…...
【CF比赛记录】—— Good Bye 2023(A、B、C)
🌏博客主页:PH_modest的博客主页 🚩当前专栏:CF比赛记录 💌其他专栏: 🔴每日一题 🟡 cf闯关练习 🟢 C语言跬步积累 🌈座右铭:广积粮,缓…...
MySQL:索引
MySQL官方对索引的定义为: 索引 (Index) 是帮助MySQL高效获取数据的数据结构。 提取句子主干,就可以得到索引的本质:索引是数据结构。 1. 什么是索引,索引的作用 索引是一种用于快速查询和检索数据的数据结构,帮助mysql提高查询效率的数据…...
CUMT--Java复习--核心类
目录 一、装箱与拆箱 二、“”与equals 三、字符串类 1、String、StringBuffer、StringBuilder的区别 2、String类 3、StringBuffer类 4、StringBuilder类 四、类与类之间关系 一、装箱与拆箱 基本类型与对应封装类之间能够自动进行转换,本质就是Java的自…...
Redis:原理速成+项目实战——Redis实战4(解决Redis缓存穿透、雪崩、击穿)
👨🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:Redis:原理项目实战——Redis实战3(Redis缓存最佳实践(问题解析高级实现)&#x…...
后端开发——jdbc的学习(一)
上篇结束了Mysql数据库的基本使用,本篇开始对JDBC进行学习总结,开始先简单介绍jdbc的基本使用,以及简单的练习;后续会继续更新!以下代码可以直接复制到idea中运行,便于理解和练习。 JDBC的概念 JDBC&#…...
阿里云免费SSL证书时长只有3个月,应对方法来了
阿里云免费SSL证书签发有效期从12个月缩短至3个月:尊敬的用户,根据供应商变更要求,免费证书(默认证书)的签发有效期将由12个月缩短至3个月。 免费证书(升级证书)的有效期不会改变。 没错&#…...
Flutter 中使用 ICON
Flutter Icon URL : https://fonts.google.com/icons: 在Flutter中使用 Icon 步骤如下: 导入图标库 在Dart 文件中导入 material.dart 包,该包包含了 Flutter 的图标库。 import package:flutter/material.dart;使用图标组件 …...
百度编辑器常用设置
1、创建编辑器 UE.getEditor(editor, { initialFrameWidth:"100%" //初始化选项 }) 精简版 UE.getEditor(editor) 2、删除编辑器 UE.getEditor(editor).destroy(); 3、使编辑器获得焦点 UE.getEditor(editor).focus(); 4、获取编辑器内容 UE.getEditor(editor).getCo…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
