用asp做网站的流程,wordpress cache插件,域名自助服务平台,wordpress ninetyFINS(factory interface network service)通信协议是欧姆龙公司开发的用于工业自动化控制网络的指令#xff0f;响应系统。运用 FINS指令可实现各种网络间的无缝通信#xff0c;包括用于信息网络的 Etherne(以太网)#xff0c;用于控制网络的Controller Link和SYSMAC LINK。… FINS(factory interface network service)通信协议是欧姆龙公司开发的用于工业自动化控制网络的指令响应系统。运用 FINS指令可实现各种网络间的无缝通信包括用于信息网络的 Etherne(以太网)用于控制网络的Controller Link和SYSMAC LINK。 ORMON PLC的FINS协议看起来非常简单但其中数据内容涉及高低位转换、16进制整数、字符串有时需要自己写代码来进行通讯处理。
1、PLC的数据类型 数据类型 说明 布尔型 单个位 短整型 有符号 16 位值 位 0 是低位 位 14 是高位 位 15 是符号位 字 无符号 16 位值 位 0 是低位 位 15 是高位 长整型 有符号 32 位值 位 0 是低位 位 30 是高位 位 31 是符号位 双字型 无符号 32 位值 位 0 是低位 位 31 是高位 浮点型 32 位实数 BCD 两个字节封装的 BCD 值的范围是 0 至 9999。对于超出此范围的值未定义行为。 LBCD 四个字节封装的 BCD 值的范围是 0 至 99999999。对于超出此范围的值未定义行为。 字符串 空终止 ASCII 字符串。 支持多达 512 个字符的字符串长度并支持择由高到低的字节排序、由低到高的字节排序、仅高位字节和仅低位字节。
短整型、长整型、双字等也可以是BCD码需要根据PLC的程序设定进行解析。 1、FINS帧定义
FINS/UDP运用的是一种嵌套格式数据包即Ethernet报头、IP报头、 UDP报头和FINS帧。一个UDP数据段(FINS 帧)超过1472字节将被分成若干个数据包来传送。分开的UDP数据将在UDPIP协议层自动组合。通常不须要关注运用 层的数据分段但是在一个多层 IP网络中1427字节的UDP包可能无法 发送。在这种系统中就须要运用 FINSTCP方式。 ICF为信息控制域用于标明指令和响应
RSV为系统保留
GCT为网关允许数目
DNA为目的网络号
DA1为目的节点号
DA2为目的单元号
SNA为源网络号
SA1为源节点号
SA2为源单元号
SID为服务和响应的标识号可任意配置指令和响应对应相同
MRC和SRC分别为 FINS指令的主指令和从指令
参数数据域用于标明所操作的数据地址、范围等在响应帧中前两个字节MRES和SRES构成响应码用来诊断不正确信息
填充示例 2、PLC连接、数据读取和解析示例
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace PascalMing
{public class Tcp_FINS_PascalMingTest{TcpClient _tcpClient new TcpClient();int _port;string _host;byte plcAddr 0;byte pcAddr 0;int headDm 30;const int readDMStart 5000;const int readDMCount 60;EventWaitHandle ewh new EventWaitHandle(false, EventResetMode.AutoReset);CancellationTokenSource cts new CancellationTokenSource();//返回46 49 4E 53 00 00 00 10 00 00 00 01 00 00 00 00 00 00 00 F0 00 00 00 9B //PC,PLC IP最后一位(4个字节一组)//不同网络配置返回值有区别需要根据实际进行替换byte[] cmdConnect { 0x46, 0x49, 0x4E, 0x53, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };//最后一位PC端IP最后一位//读D5000连续100个byte[] cmdReadD5000 { 0x46, 0x49, 0x4E, 0x53, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x00, 0x9B, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x01, 0x01, 0x82, (byte)(readDMStart / 0x100), (byte)(readDMStart % 0x100), 0x00, (byte)(readDMCount/0x100), (byte)(readDMCount%0x100) };public bool ConnectDevice(string ip, int port){_host ip;_port port;try{_tcpClient new TcpClient();_tcpClient.Connect(_host, _port);//_tcpClient.ReceiveTimeout 5_000;_tcpClient.Client.Send(cmdConnect);byte[] rx new byte[100];for (int k 0; k 100; k){if (_tcpClient.Available 24)break;Thread.Sleep(50);}int ret _tcpClient.Client.Receive(rx);Console.WriteLine($connect recv Len:{ret},{rx[0]},{rx[1]},{rx[2]},{rx[3]},{rx[7]});if (ret 24 rx[0] 0x46 rx[1] 0x49 rx[2] 0x4E rx[3] 0x53){pcAddr rx[19];plcAddr rx[23];cmdReadD5000[20] plcAddr;cmdReadD5000[23] pcAddr;Console.WriteLine(connect ok);Task.Run(() {myTaskExecute(cts.Token);}); return true;}Console.WriteLine(connect fail,check fail);return false;}catch (Exception ex){Console.WriteLine(connect fail,ex:ex.Message);return false;}}int count 0;async Task myTaskExecute(CancellationToken token){Stopwatch sw Stopwatch.StartNew();int timeOutMs 30_000;Listbyte[] cmdIndex new Listbyte[]();ushort txCount 0;try{while (!token.IsCancellationRequested){int ret -1;byte[] rx new byte[4096];Console.WriteLine(${DateTime.Now} send read);_tcpClient.Client.Send(cmdReadD5000);for(int k 0; k 200; k ){if (_tcpClient.Available headDm readDMCount*2)break;await Task.Delay(20, token);}Console.WriteLine(${DateTime.Now} send Available:{_tcpClient.Available});if (_tcpClient.Available 0){sw.Restart();ret _tcpClient.Client.Receive(rx);try{Console.WriteLine(${DateTime.Now} DataReceived len:{ret},data:{rx[7]},{rx[19]},{rx[23]});if (ret 16 rx[0] 0x46 rx[1] 0x49 rx[2] 0x4E rx[3] 0x53){//数据解析根据PLC定义进行包括数据值。此处使用比较简单的测试方案Console.WriteLine($D{5000}:{GetInt16(rx,0)});Console.WriteLine($D{5001}:{GetInt16(rx,1)});Console.WriteLine($D{5010}:{GetInt32_2(rx, 10)});Console.WriteLine($D{5012}:{GetInt32_2(rx, 12)});Console.WriteLine($D{5014}:{GetInt16(rx, 14)});Console.WriteLine($D{5015}:{GetString(rx, 15,40)});Console.WriteLine(); }}catch (Exception ex){Console.WriteLine(DataReceived parse err: ex.Message);}}await Task.Delay(2000, token);}}catch (Exception ex){Console.WriteLine(DataReceived ex: ex.Message);}Console.WriteLine(DataReceived leave);}public int GetInt16(byte[]data,int offset){string sV ${data[headDm offset*2]:X2}{data[headDmoffset*21]:X2};int iV int.Parse(sV);return iV;}public int GetInt32(byte[] data, int offset){string sV ${data[headDm offset*2]:X2}{data[headDm offset*2 1]:X2}{data[headDm offset*2 2]:X2}{data[headDm offset*2 3]:X2};int iV int.Parse(sV);return iV;}public int GetInt32_2(byte[] data, int offset){string sV ${data[headDm offset * 22]:X2}{data[headDm offset * 2 3]:X2}{data[headDm offset * 2 0]:X2}{data[headDm offset * 2 1]:X2};int iV int.Parse(sV);return iV;}public string GetString(byte[] data, int offset,int len){int headDm 30;StringBuilder sb new StringBuilder();for(int k 0; k len; k ){if (data[headDm offset*2 k] 0)break;sb.Append(${(char)data[headDmoffset*2k]});}return sb.ToString();}public void DisconnectDevice(){_tcpClient?.Close();}}
}验证
Tcp_FINS_PascalMingTest tcp_fins new Tcp_FINS_PascalMingTest();
void Do_Tcp_FINS()
{tcp_FINS_Yinlun.ConnectDevice(192.168.0.1, 9600);
} 3、参考资料
FinsTCP协议报文详细分析 - 知乎基于FINS协议的OMRON PLC与上位机以太网通信的实现_omron nx fins上位机配置-CSDN博客