C# OMRON PLC FINS TCP协议简单测试

news2024/11/26 14:46:45

       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数据将在UDP/IP协议层自动组合。通常不须要关注运用 层的数据分段,但是在一个多层 IP网络中1427字节的UDP包可能无法 发送。在这种系统中就须要运用 FINS/TCP方式。

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;
            List<byte[]> cmdIndex = new List<byte[]>();
            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[headDm+offset*2+1]: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 * 2+2]: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[headDm+offset*2+k]}");
            }
            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博客
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1433346.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端框架学习 Vue(3)vue生命周期,钩子函数,工程化开发脚手架CLI,组件化开发,组件分类

Vue 生命周期 和生命周期的四个阶段 Vue生命周期:一个Vue实例从创建 到 销毁 的整个过程 生命周期四个阶段 :(1)创建 (2)挂载 (3)更新 (4)销毁 Vue生命周期函数(钩子函数) Vue生命周期过程中,会自动运行一些函数,被称为[生命周期钩子] ->让开发者可以在[特定阶段] 运行自…

[MFC] MFC消息机制的补充

之前写了[MFC] 消息映射机制的使用和原理浅析&#xff0c;还有些需要补充的&#xff0c;都记在这里。 MFC 消息的分类 MFC消息分为系统消息和自定义消息。 图片来源&#xff1a;C语言/C教程 大型源码案例分析&#xff1a;MFC消息系统的代码解析 易道云编程 系统消息分为窗口…

【SpringBoot】SpringBoot的web开发

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;SpringBoot ⛺️稳重求进&#xff0c;晒太阳 Wbe开发 使用Springboot 1&#xff09;、创建SpringBoot应用&#xff0c;选中我们需要的模块&#xff1b; 2&#xff09;、SpringBoot已经默…

用友GRP-U8 listSelectDialogServlet SQL注入漏洞复现

0x01 产品简介 用友GRP-U8R10行政事业内控管理软件是用友公司专注于国家电子政务事业,基于云计算技术所推出的新一代产品,是我国行政事业财务领域最专业的政府财务管理软件。 0x02 漏洞概述 用友GRP-U8R10行政事业内控管理软件 listSelectDialogServlet 接口处存在SQL注入…

chisel RegInit/UInt/U

val reg RegInit(0.U(8.W)) //ok val reg RegInit(0.UInt(8.W)) //errU 使用在数字 . 后边50.U UInt 使用在IO(new Bundle val a Input(UInt(8.W)) 或者 def counter(max:UInt, a1:UInt) package emptyimport chisel3._ import chisel3.util._class MyCounter extends …

操作系统-【预备学习-1】(Linux 文件目录)

文章目录 相关知识目录结构进入目录补充查看目录创建文件删除文件创建文件夹删除文件夹文件和文件夹拷贝文件和文件夹移动/重命名 任务要求 相关知识 目录结构 Linux 文件系统是树形层次结构&#xff0c;具体如下图所示&#xff0c;最重要的是根目录&#xff08;/&#xff09…

06 - python操作xml

认识XML 与HTML很像&#xff0c;是一种将数据存储在标记之间的标记语言&#xff0c;用户可以自定义自己的标记。 XML文件可以表示称为&#xff1a;XML树。这个XML树从根元素开始&#xff0c;根元素进一步分支到子元素。XML文件的每个元素都是XML树的一个节点&#xff0c;没有…

服务器和云服务器哪个更安全?

随着云计算技术的不断发展&#xff0c;越来越多的企业开始选择使用云服务器来存储和处理数据。然而&#xff0c;对于一些企业来说&#xff0c;他们可能更倾向于使用传统的服务器。在这种情况下&#xff0c;安全性成为了一个重要的考虑因素。那么&#xff0c;服务器和云服务器哪…

arm 汇编积累

C语言函数与汇编对应关系 一、MOV 系列指令 1、指令格式 MOV{条件}{S} 目的寄存器&#xff0c;源操作数 2、含义解析&#xff1a; &#xff08;1&#xff09;&#xff1a;mov 指令传送数据 案例&#xff1a; MOV R0,R1 ; R0 R1; MOV PC,R14 ;PC R14; MOV R0,R…

[Angular 基础] - 数据绑定(databinding)

[Angular 基础] - 数据绑定(databinding) 上篇笔记&#xff0c;关于 Angular 的渲染过程及组件的创建&简单学习&#xff1a;[Angular 基础] - Angular 渲染过程 & 组件的创建 Angular 之中的 databinding 是一个相对而言更加复杂&#xff0c;以及我个人觉得相对而言比…

Java on Azure Tooling 2024年1月更新|Azure Key Vault 支持、示例项目创建支持及更多

作者&#xff1a;Jialuo Gan - Program Manager, Developer Division At Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到 2024 年 Java on Azure 工具的首次更新。在本次更新中&#xff0c;我们将介绍对于 Azure Key Vault 支持、基于 Azure 示例项目的创建支…

C++ 调用lua 脚本

需求&#xff1a; 使用Qt/C 调用 lua 脚本 扩展原有功能。 步骤&#xff1a; 1&#xff0c;工程中引入 头文件&#xff0c;库文件。lua二进制下载地址&#xff08;Lua Binaries&#xff09; 2&#xff0c; 调用脚本内函数。 这里调用lua 脚本中的process函数&#xff0c;并…

如何让虚拟机拥有愉快网络环境,vmware,ubuntu,centos

博客原文 文章目录 前言拥有愉快网络环境步骤:测试网关连接 Ubuntu修改 http 与 sock 代理地址修改 /etc/resolv.conf配置 apt 使用代理测试连接 Centos设置代理地址修改 NetworkManager最后重启网卡&#xff1a;测试代理 前言 相信计算机专业的同学在学习 linux 时, 一定会被无…

Element UI+Spring Boot进行CRUD的实例

ElementUI安装与使用指南 前端代码&#xff1a;点击查看learnelementuispringboot项目源码 后端代码&#xff1a;点击查看 LearnElementUiAndSpringBoot 一、前端配置 安装axios Gitee的axios介绍与使用 GitHub的axios介绍与使用 方式一&#xff1a;使用npm安装 $ npm in…

2024年最新幻兽帕鲁服务器搭建教程

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

回归预测 | Matlab实现WOA-CNN-LSTM-Attention鲸鱼算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制)

回归预测 | Matlab实现WOA-CNN-LSTM-Attention鲸鱼算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现WOA-CNN-LSTM-Attention鲸鱼算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&…

谷歌产品大更新:Bard可生成图像;文生音乐平台等5大免费功能

2月2日&#xff0c;谷歌在官网对生成式AI产品进行了大更新&#xff0c;包括类ChatGPT聊天助手Bard可以通过文本提示生成图像&#xff1b; 全新的文生音乐平台MusicFX&#xff1b;新的文生图像平台ImageFX&#xff1b;新的文本扩写平台TextFX&#xff1b;在谷歌地图中增加生成式…

docker搭建Mysql集群准备(一)

docker搭建Mysql集群准备 Linux基本知识&#xff1a; 修改机器 IP&#xff0c;变成静态 IP vim /etc/sysconfig/network-scripts/ifcfg-ens33 文件 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic IPADDR192.168.190.67 NETMASK255.255.255.0 GAT…

第六讲:文件操作

第六讲:文件操作 文件夹创建文件夹移动文件夹复制文件夹删除文件夹文件操作文件读取文件写入文件文件夹 创建文件夹 定义创建文件夹函数:chmk_path()定义一个函数 chmk_path(),这个函数的功能是创建文件夹。 首先需要导入操作系统接口模块——os 模块,这个模块中包含某些函…

802.11n 802.11ac (WiFi 4/5 )的核心要点

802.11n 802.11ac &#xff08;WiFi 4/5 &#xff09;是什么&#xff1f; WiFi 4&#xff1a; Ieee 802.11n Enhancements for High Throughput &#xff08;HT&#xff09; WiFi 5&#xff1a; Ieee 802.11ac Enhancements for Very High Throughput &#xff08;VHT&#x…