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…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
