欧姆龙NJ/NX系列PLC 基于以太网的CIP通讯(EtherNet/IP)

这篇具有很好参考价值的文章主要介绍了欧姆龙NJ/NX系列PLC 基于以太网的CIP通讯(EtherNet/IP)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

CIP (Common Industrial Protocol, 通用工业协议) 是由 ODVA组织提出并维护的具有增强服务的自动化通讯协议。是一种使用生产者-消费者通信模型的与媒体无关的协议,并且是上层的严格面向对象的协议。每个CIP对象都有属性(数据)、服务(命令)、连接和行为(属性值和服务之间的关系)。CIP包括一个广泛的对象库,用于支持通用网络通信、文件传输等网络服务以及模拟和数字输入/输出设备、HMI、运动控制和位置反馈等典型自动化功能。
EtherNet/IP是基于以太网的通讯协议,为用户提供了为工业自动化应用部署标准以太网技术(IEEE 802.3与TCP/IP套件相结合)的网络工具,同时实现了互联网和企业连接,从而随时随地产生数据。EtherNet/IP提供各种拓扑选项,包括具有标准以太网基础设施设备的传统星形,或启用EtherNet/IP设备的设备级环(DLR)。QuickConnectTM功能允许在网络运行时通过使用简短的启动程序快速重新连接设备。

一、 EtherNet/IP协议的两种消息传递模式

1.消息种类

1)显式消息

显式消息连接是点对点的关系,旨在方便两个节点之间的请求-响应事务。这些连接是通用性质的,通常用于两个节点之间的频繁请求。它们可用于访问设备内的任何网络可访问项。显式消息连接利用TCP/IP服务在以太网上传递消息。

2)隐式消息

隐式(I/O数据)连接是为了在规律的时间间隔内移动特定应用程序的I/O数据而建立的。这些连接可以设置为一对一的关系或一对多的关系,以充分利用生产者-消费者组播模型。隐式消息使用UDP/IP资源使以太网上的组播数据传输成为现实。

2. 已连接消息传递(CMM)

用于传递EtherNet/IP上每个节点内预先专用于特定目的的资源,例如频繁的显式消息交易或实时I/O数据。连接资源是使用通过UCMM可用的通信服务保留和配置的。

3. 未连接消息传递(UCMM)

在连接建立过程中用于传递不频繁、低优先级的显式消息。设备中的未连接资源称为未连接消息管理器或UCMM。EtherNet/IP上的未连接消息利用TCP/IP资源在以太网上传递消息。

二、未连接消息模式下欧姆龙NJ/NX系列PLC通讯数据报文格式解析C#代码实现

1. 报文格式分析

1)头部数据帧解析
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace OmronCIP
{
    internal class EncapsulationHeader
    {
        /// <summary>
        /// Encapsulation Command
        /// </summary>
        public ushort Command { get; set; }
        /// <summary>
        /// Length of the data portion in bytes
        /// </summary>
        public ushort Length { get; set; }
        /// <summary>
        /// Session handle
        /// </summary>
        public uint SessionHandle { get; set; }
        /// <summary>
        /// Status Code
        /// </summary>
        public uint Status { get; set; }

        private byte[] _senderContext = new byte[8];
        /// <summary>
        /// Sender Context
        /// </summary>
        /// <remarks>Information only pertinent to the sender of the encaps command. Must be 8 bytes.</remarks>
        public byte[] SenderContext
        {
            get { return _senderContext; }
            set
            {
                if (value == null)
                    _senderContext = new byte[8];

                if (value.Length >= 8)
                {
                    _senderContext = new byte[8];
                    Array.Copy(value, _senderContext, 8);
                }

                if (value.Length < 8)
                {
                    _senderContext = new byte[8];
                    Array.Copy(value, _senderContext, value.Length);
                }
            }
        }
        /// <summary>
        /// Options
        /// </summary>
        public uint Options { get; set; }
        /// <summary>
        /// Protocol Version
        /// </summary>
        public ushort ProtocolVersion { get; set; }
        /// <summary>
        /// Options Flags
        /// </summary>
        public ushort OptionsFlag { get; set; }

        public void Expand(byte[] DataArray, int Offset)
        {
            if (DataArray.Length < Offset + 24)
                throw new IndexOutOfRangeException("Not enough data in the DataArray for the encapsulated packet");
            Command = BitConverter.ToUInt16(DataArray, Offset);
            Length = BitConverter.ToUInt16(DataArray, Offset + 2);
            SessionHandle = BitConverter.ToUInt32(DataArray, Offset + 4);
            Status = BitConverter.ToUInt32(DataArray, Offset + 8);
            _senderContext = new byte[8];
            Buffer.BlockCopy(DataArray, Offset + 12, _senderContext, 0, 8);
            Options = BitConverter.ToUInt32(DataArray, Offset + 20);
            if (Command == (ushort)EncapsCommand.RegisterSession)
            {
                ProtocolVersion = BitConverter.ToUInt16(DataArray, Offset + 24);
                OptionsFlag = BitConverter.ToUInt16(DataArray, Offset + 26);
            }
        }
        public byte[] ReplyPack()
        {
            byte[] data = null;
            if (Command == (ushort)EncapsCommand.RegisterSession)
            {
                data = new byte[28];
                Buffer.BlockCopy(BitConverter.GetBytes(Command), 0, data, 0, 2);
                Buffer.BlockCopy(BitConverter.GetBytes(Length), 0, data, 2, 2);
                Buffer.BlockCopy(BitConverter.GetBytes(SessionHandle), 0, data, 4, 4);
                Buffer.BlockCopy(BitConverter.GetBytes(Status), 0, data, 8, 4);
                Buffer.BlockCopy(_senderContext, 0, data, 12, 8);
                Buffer.BlockCopy(BitConverter.GetBytes(Options), 0, data, 20, 4);
                Buffer.BlockCopy(BitConverter.GetBytes(ProtocolVersion), 0, data, 24, 2);
                Buffer.BlockCopy(BitConverter.GetBytes(OptionsFlag), 0, data, 26, 2);
            }
            if (Command == (ushort)EncapsCommand.UnRegisterSession || Command == (ushort)EncapsCommand.SendRRData)
            {
                data = new byte[24];
                Buffer.BlockCopy(BitConverter.GetBytes(Command), 0, data, 0, 2);
                Buffer.BlockCopy(BitConverter.GetBytes(Length), 0, data, 2, 2);
                Buffer.BlockCopy(BitConverter.GetBytes(SessionHandle), 0, data, 4, 4);
                Buffer.BlockCopy(BitConverter.GetBytes(Status), 0, data, 8, 4);
                Buffer.BlockCopy(_senderContext, 0, data, 12, 8);
                Buffer.BlockCopy(BitConverter.GetBytes(Options), 0, data, 20, 4);
            }
            return data;
        }
    }
}

2)服务命令与数据内容解析(CommandSpecificData)
// 这里的解析部分代码省略(代码太长)
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OmronCIP
{
    public class CommandSpecificData
    {
        /// <summary>
        /// Interface handle
        /// </summary>
        public uint InterfaceHandle { get; set; }
        /// <summary>
        /// Connect timeout
        /// </summary>
        public ushort ConnectTimeout { get; set; }
        /// <summary>
        /// Service timeout
        /// </summary>
        public ushort ServiceTimeout { get; set; }
        /// <summary>
        /// Message request size
        /// </summary>
        public ushort MessageRequestSize { get; set; }
        /// <summary>
        /// Multiple service packet
        /// </summary>
        public byte MultipleServicePacket { get; set; }
        /// <summary>
        /// Multiple tag request path size
        /// </summary>
        public byte MultipleTagRequestPathSize { get; set; }
        /// <summary>
        /// Message router request path
        /// </summary>
        public byte[] MessageRouterRequestPath { get; set; }
        /// <summary>
        /// Number of Services
        /// </summary>
        public ushort ServiceNumber { get; set; }
        /// <summary>
        /// Offset list
        /// </summary>
        public ushort[] OffsetList { get; set; }
        /// <summary>
        /// Item count
        /// </summary>
        public ushort ItemCount { get; set; }
        /// <summary>
        /// Service type
        /// </summary>
        public byte ServiceType { get; set; }
        /// <summary>
        /// Request service code
        /// </summary>
        public byte RequestService { get; set; }
        /// <summary>
        /// Address item
        /// </summary>
        public ushort AddressItem { get; set; }
        /// <summary>
        /// Address item length
        /// </summary>
        public ushort AddressItemLen { get; set; }
        /// <summary>
        /// Unconnected data item
        /// </summary>
        public ushort DataItem { get; set; }
        /// <summary>
        /// Unconnected data item length(byte)
        /// </summary>
        public ushort DataItemLen { get; set; }
        /// <summary>
        /// Request path length
        /// </summary>
        public byte RequestPathLen { get; set; }

        /// <summary>
        /// Request path
        /// </summary>
        public byte[] RequestPath { get; set; }
        /// <summary>
        /// CIP message length (word)
        /// </summary>
        public byte[] CipMsgLens { get; set; }
        /// <summary>
        /// Tag name byte array
        /// </summary>
        public string[] TagNames { get; set; }
        /// <summary>
        /// PLC slot serial number
        /// </summary>
        public uint Slot { get; set; }
        /// <summary>
        /// Read / Write data type
        /// </summary>
        public ushort DataType { get; set; }
        /// <summary>
        /// Return error code
        /// </summary>
        public ushort StateCode { get; set; }
        /// <summary>
        /// Write data type
        /// </summary>
        public DataTypeCode WriteDataType { get; set; }
        /// <summary>
        /// Write data quantity
        /// </summary>
        public ushort WriteDataQuantity { get; set; }
        /// <summary>
        /// Write value
        /// </summary>
        public object WriteDataValue { get; set; }
        /// <summary>
        /// Array length
        /// </summary>
        public int ArrayLength { get; set; } = 0;     
    }
}
2)服务命令与数据内容封装(EncapsulationPacket)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace OmronCIP
{
    public class EncapsulationPacket
    {
        private EncapsulationHeader encapsulationHeader = new EncapsulationHeader();
        public CommandSpecificData CommandSpecificData = new CommandSpecificData();
        /// <summary>
        /// Encapsulation Command
        /// </summary>
        public ushort Command { get; set; }
        public void Expand(byte[] data)
        {
            ushort flag = BitConverter.ToUInt16(data, 0);
            Command = flag;
            if (flag == (ushort)EncapsCommand.RegisterSession)
            {
                encapsulationHeader.Expand(data, 0);
                CommandSpecificData.loadTags();//读写数组时需要加载全局变量列表
                encapsulationHeader.SessionHandle = GenerateSessionHandle();
            }
            if (flag == (ushort)EncapsCommand.UnRegisterSession)
            {
                encapsulationHeader.Expand(data, 0);
                encapsulationHeader.SessionHandle = 0;
            }
            if (flag == (ushort)EncapsCommand.SendRRData)
            {
                encapsulationHeader.Expand(data, 0);
                CommandSpecificData.Expand(data, 24);
            }
        }
        public byte[] ReplyPack()
        {
            byte[] packet = null;
            byte[] headerPacket = null;
            switch (Command)
            {
                case (ushort)EncapsCommand.RegisterSession://0x65
                    {
                        headerPacket = encapsulationHeader.ReplyPack();
                        packet = new byte[headerPacket.Length];
                        Buffer.BlockCopy(headerPacket, 0, packet, 0, headerPacket.Length);
                        break;
                    }
                case (ushort)EncapsCommand.UnRegisterSession://0x66
                    {
                        headerPacket = encapsulationHeader.ReplyPack();
                        packet = new byte[headerPacket.Length];
                        Buffer.BlockCopy(headerPacket, 0, packet, 0, headerPacket.Length);
                        break;
                    }
                case (ushort)EncapsCommand.SendRRData://0x6F
                    {
                        if (ServiceType.Read == (ServiceType)CommandSpecificData.ServiceType)
                        {
                            headerPacket = encapsulationHeader.ReplyPack();
                            byte[] tempPacket = CommandSpecificData.ReplyPack();
                            packet = new byte[headerPacket.Length + tempPacket.Length];
                            encapsulationHeader.Length = BitConverter.ToUInt16(BitConverter.GetBytes(tempPacket.Length), 0);
                            Buffer.BlockCopy(encapsulationHeader.ReplyPack(), 0, packet, 0, headerPacket.Length);
                            Buffer.BlockCopy(tempPacket, 0, packet, headerPacket.Length, tempPacket.Length);
                        }
                        else if (ServiceType.Write == (ServiceType)CommandSpecificData.ServiceType)
                        {
                            headerPacket = encapsulationHeader.ReplyPack();
                            byte[] tempPacket = CommandSpecificData.ReplyPack();
                            packet = new byte[headerPacket.Length + tempPacket.Length];
                            encapsulationHeader.Length = BitConverter.ToUInt16(BitConverter.GetBytes(tempPacket.Length), 0);
                            Buffer.BlockCopy(encapsulationHeader.ReplyPack(), 0, packet, 0, headerPacket.Length);
                            Buffer.BlockCopy(tempPacket, 0, packet, headerPacket.Length, tempPacket.Length);
                        }
                        break;
                    }
            }
            return packet;
        }
        private uint GenerateSessionHandle()
        {
            byte[] sessionHandle = new byte[4];
            MD5 md5 = MD5.Create();
            byte[] temp = md5.ComputeHash(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()));
            for (int i = 0; i < 4; i++)
                sessionHandle[i] = temp[i];
            return BitConverter.ToUInt32(sessionHandle, 0);
        }
    }
}

2. 欧姆龙EtherNet/IP协议的解析C#实现用途说明

以上代码都是小编在做项目的过程中所写,当然,有些代码是基于EEIP这个项目写的,小编在这里感谢EEIP项目的所有贡献者 ,本项目用于创建欧姆龙EtherNet/IP协议的虚拟服务端,HslCommunication这个通讯库可以直接访问,基于此可以访问欧姆龙编程软件Sysmac Studio的模拟器,并在没有实体PLC的情况下完全模拟欧姆龙PLC测试PLC程序的逻辑问题,也就是可以实现EIP协议的解析与协议报文封装。最后,已连接模式下的消息传递报文解析与封装将在下一片文章分享,届时将可以实现与威纶通HMI模拟器通讯。文章来源地址https://www.toymoban.com/news/detail-815332.html

到了这里,关于欧姆龙NJ/NX系列PLC 基于以太网的CIP通讯(EtherNet/IP)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 晨控CK-FR03-EIP读卡器与欧姆龙NX/NJ系列EtherNet/IP通讯手册

    晨控CK-FR03-EIP读卡器与欧姆龙NX/NJ系列EtherNet/IP通讯手册 CK-FR03-EIP是一款基于射频识别技术的高频RFID标签读卡器,读卡器工作频率为13.56MHZ,支持对I-CODE 2、I-CODE SLI等符合ISO15693国际标准协议格式标签的读取。 读卡器同时支持标准工业通讯协议EtherNet IP,方便用户通集成到PLC等

    2024年02月01日
    浏览(53)
  • 欧姆龙PLC仿真环境搭建及通信测试

    欧姆龙PLC是工控领域市场率较高的品牌之一,有低端CP/CH系列,中端CJ系列,高端NX/NJ系列。对于上位机开发来说,主要的协议有Hostlink协议、FinsTCP/UDP协议、EtherNetIP协议,今天主要跟大家分享一下,我们在上位机学习中如何通过软件来搭建仿真环境,在没有硬件的情况下,也

    2024年02月09日
    浏览(113)
  • 欧姆龙以太网模块如何设置ip连接 Kepware opc步骤

    在数字化和自动化的今天,PLC在工业控制领域的作用日益重要。然而,PLC通讯口的有限资源成为了困扰工程师们的问题。为了解决这一问题,捷米特推出了JM-ETH-CP转以太网模块,让即插即用的以太网通讯成为可能,不仅有效利用了PLC的通讯资源,还提高了数据传输的效率和稳

    2024年02月16日
    浏览(80)
  • 上位机与欧姆龙PLC的Fins tcp通讯

    参考1:(147条消息) 欧姆龙PLC的FinsTCP协议_weixin_37700863的博客-CSDN博客_欧姆龙fins协议tcp   参考2:(147条消息) FinsTCP协议报文详细分析_常哥说编程的博客-CSDN博客_fins协议 参考3:(147条消息) 欧姆龙FINS通讯命令实例详解_搬砖dog的博客-CSDN博客_欧姆龙fins 上述文档中有部分描述错误

    2024年02月11日
    浏览(51)
  • Labview+Hsl通讯(与欧姆龙NX1P2通讯)

    通过和欧姆龙客服沟通,NX1P2不支持fins tcp与OPC UA,但是支持FINS-UDP通讯,没办将就用吧。这里PLC IP: 192.168.250.0.1  端口:9600   下面是测试图。PC端的端口随意填就行,不要和PLC端口重复就行。  

    2024年02月13日
    浏览(46)
  • WPF真入门教程26--项目案例--欧姆龙PLC通讯工具

    前面已经完成了25篇的文章介绍,概括起来就是从0开始,一步步熟悉了wpf的概念,UI布局控件,资源样式文件的使用,MVVM模式介绍,命令Command等内容,这节来完成一个实际的项目开发,虽然小案例,但是可以把前面的知识融合起来,比如控件的布局,命令的绑定,视图模型的

    2024年02月02日
    浏览(43)
  • ETHERNET/IP 转ETHERCAT连接倍福和欧姆龙PLC的配置方法

    ETHERNET/IP和ETHERCAT是两种不同的协议,它们在工业生产中都有广泛的应用。然而,由于协议不同,这两种设备之间无法通讯,这给工业生产带来了很大的麻烦。而捷米JM-EIP-ECAT网关应运而生,它能够连接到ETHERNET/IP总线和ETHERCAT总线中,实现两种不同协议设备之间的通讯。这个网

    2024年02月14日
    浏览(53)
  • 欧姆龙plc编程软件CX-Progammer v9.8升级教程

    一、前言 1、欧姆龙CP2E需要用CX-Progammer v9.7以上版本,非v9.7以上版本的编程软件,打开时无CP2E的选择。          2、百度网盘下载链接:https://pan.baidu.com/s/1A-6VOl4oPobOeTBYfT-HWw  提取码:kb72 1、下载完成后,得到文件名为 “CXOUpgradeUtility_V4” 的压缩包: 2 、解压开双击解压

    2024年02月16日
    浏览(128)
  • EtherNet Ip工业RFID读写器与欧姆龙PLC 配置示例说明

    一、准备阶段 POE交换机 欧姆龙PLC 支持EtherNet Ip协议 CX-Programmer 9.5 配置软件 二、配置读卡器 1、打开软件 2、选择网卡,如果多网卡的电脑请注意对应所接的网卡,网卡名一般为“Network adapter \\\'Realtek PCIe GBE Family” 3、点击“选择网卡”,再点“扫描设备” 4、修改IP,选中某设

    2024年02月20日
    浏览(41)
  • C#上位机与欧姆龙PLC的通信12----【再爆肝】上位机应用开发(WPF版)

    继上节完成winform版的应用后,今天再爆肝wpf版的,看看看。 可以看到,wpf的确实还是漂亮很多,现在人都喜欢漂亮的,颜值高的,现在是看脸时代,作为软件来说,是交给用户使用的,UI自然是要讲究,可以看出,wpf比winform漂亮多了,因为wpf使用样式css来美化界面,虽然这

    2024年01月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包