C#实现上位机与欧姆龙PLC通讯(FINS)

这篇文章主要介绍了C#实现上位机与欧姆龙PLC通讯(FINS)方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

先介绍下一些基本定义

串行通信:通过的是PLC上的串行口RS232/RS422/485口,上位机链接系统 Hostlink系统是对于FA系统一种及优化有经济的通信方式。

适用于一台上位机与一台或者多台的PLC进行数据通信。

通讯协议分两种

1:C-mode commands

只可以通过串口进行通讯

2:Fins commands

既可以通过串口通讯也可以通过各类网络通讯(适应性较强)

本文只介绍fins通讯

fins (factory in terfave network service) 通讯协议是欧姆龙公司开发的一款自动化控制网络指令/响应系统的通讯协议;运用FINS指令可实现各种网络间的无缝通信

FINS帧结构

  • 发送命令结构 命令码2个字节
  • 响应命令结构 命令码2个字节
  • 命令码 01 01读数据
  • 命令码 01 02写数据
  • 结束码 00 00无错误

1、获取PLC节点地址

上位机和PLC建立TCP通讯之后,可以发送以下命令来获得PLC的节点地址;

帧结构见下表:

列表Content(十六进制)Description(说明)
46 49 4E 53ASCII分别代表的是 F I N S
长度00 00 00 0C从Command之后的数据包的长度
错误代码00 00 00 00目前暂时没用,因为在服务器不需要检测内部错误
连接的节点地址00000000~000000FE分别是0~254,当设置为0时表示自动获取客户端的Fins节点地址

2、命令码介绍

详细内容说明介绍见下表:

命令内容命令代码(MR SR)说明功能
访问I/O存储区01 01读内存区读连续I/O存储区字的内容
访问I/O存储区01 02写内存区写连续I/O存储区字的内容
访问I/O存储区01 03填充内存区将相同的数据写入指定范围的I/O存储器区
访问I/O存储区01 04多个存储区读取读取指定的非连续I/O存储区字
访问I/O存储区01 05存储区传输将连续存储I/O存储区字内容复制到另外的I/O存储区
访问参数区02 01读取参数区读取连续参数区字内容
访问参数区02 02写入参数区写入连续参数区字内容
访问参数区02 03填充参数区将相同数据写入到指定范围参数区域字
改变操作模式04 01设置CPU操作模式为RUN将CPU单元的操作模式更改为RUN或MONITOR
改变操作模式04 02设置CPU操作模式为STOP将CPU单元的操作模式更改为编程
改变操作模式06 01读取CPU单元状态读取CPU单元状态
错误日志21 01错误清除清除错误或错误信息
错误日志21 02读取错误日志读取错误日志
错误日志21 03清除错误日志清除错误日志指针

3、I / O存储器地址标识

区域数据类型存储区代码存储区地址范围存储地址字节长度
DMBit02D0000000到D3276715000000到7FFF0F1
DMWord82D00000到D32767000000到7FFF002

鉴于我们在和PLC通讯时,一般只需要进行读取DM区寄存器操作,因为本文只介绍读取和写入DM区寄存器。其他的有需要的童鞋进行自己功能拓展。

举例:

读取DM区地址100,连续10个地址的数据

发送命令:010182006400000A

返回命令:010100000102030405060708090A

发送的命令进行说明:

  • 1、01 01代表功能码,读连续I/O存储区字的内容
  • 2、82代表进行字操作
  • 3、00 64转成十进制就是100代表的是我们要读取的起始地址
  • 4、00000A转成十进制就是10代表的是我们要读取得长度

响应的命令说明

  • 1、01 01代表功能码,读连续I/O存储区字的内容
  • 2、00 00代表结束码,当不是00 00的时候表明数据帧不对
  • 3、01 02 03 04 05 06 07 08 09 0A分别代表要读取的十个寄存器的数据值

具体协议可以查看百度文库:欧姆龙fins通讯协议

综上,结合之前的博客,C#Socket客户端:C#Socket客户端我们可以整合得到一个欧姆龙fins的类,如下所示:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace OmrEntFINS
{
    public class OmronFINS
    {
        /// <summary>
        /// 客户端连接Socket
        /// </summary>
        private Socket clientSocket;
        /// <summary>
        /// 连接状态
        /// </summary>
        public Boolean connected = false;
        /// <summary>
        /// 发送数据
        /// </summary>
        private Byte[] SendMess;
        /// <summary>
        /// 连接点
        /// </summary>
        private IPEndPoint hostEndPoint;
        /// <summary>
        /// 连接信号量
        /// </summary>
        private static AutoResetEvent autoConnectEvent = new AutoResetEvent(false);
        /// <summary>
        /// 接受到数据时的委托
        /// </summary>
        /// <param name="info"></param>
        public delegate void ReceiveMsgHandler(Byte[] info);
        /// <summary>
        /// 接收到数据时调用的事件
        /// </summary>
        public event ReceiveMsgHandler OnMsgReceived;
        /// <summary>
        /// 开始监听数据的委托
        /// </summary>
        public delegate void StartListenHandler();
        /// <summary>
        /// 开始监听数据的事件
        /// </summary>
        public event StartListenHandler StartListenThread;
        /// <summary>
        /// 发送信息完成的委托
        /// </summary>
        /// <param name="successorfalse"></param>
        public delegate void SendCompleted(bool successorfalse);
        /// <summary>
        /// 发送信息完成的事件
        /// </summary>
        public event SendCompleted OnSended;
        /// <summary>
        /// 监听接收的SocketAsyncEventArgs
        /// </summary>
        private SocketAsyncEventArgs listenerSocketAsyncEventArgs;
        int Plcport;
        public OmronFINS(String hostName, Int32 port, Int32 PLCStaion)
        {
            Plcport = PLCStaion;
            IPAddress[] addressList = Dns.GetHostAddresses(hostName);
            this.hostEndPoint = new IPEndPoint(addressList[addressList.Length - 1], port);
            this.clientSocket = new Socket(this.hostEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        }
        /// <summary>
        /// 连接服务端
        /// </summary>
        private bool Connect()
        {
            using (SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs())
            {
                connectArgs.UserToken = this.clientSocket;
                connectArgs.RemoteEndPoint = this.hostEndPoint;
                connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);
                clientSocket.ConnectAsync(connectArgs);
                //等待连接结果
                bool autores = autoConnectEvent.WaitOne(1000);
                if (this.connected)
                {
                    listenerSocketAsyncEventArgs = new SocketAsyncEventArgs();
                    byte[] receiveBuffer = new byte[1024];//设置接收buffer区大小
                    listenerSocketAsyncEventArgs.UserToken = clientSocket;
                    listenerSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
                    listenerSocketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
                    StartListenThread();
                    // SocketExtensions.SetKeepAlive(clientSocket, 3000, 1000);
                    return true;
                }
                else
                    return false;
            }
        }
        /// <summary>
        /// 开始监听线程的入口函数
        /// </summary>
        private void Listen()
        {
            (listenerSocketAsyncEventArgs.UserToken as Socket).ReceiveAsync(listenerSocketAsyncEventArgs);
        }
        public static List<SocketAsyncEventArgs> s_lst = new List<SocketAsyncEventArgs>();
        /// <summary>
        /// 发送信息
        /// </summary>
        /// <param name="message"></param>
        private void Send(Byte[] message)
        {
            if (this.connected)
            {
                Byte[] sendBuffer = message;
                SocketAsyncEventArgs senderSocketAsyncEventArgs = null;// new SocketAsyncEventArgs();
                lock (s_lst)
                {
                    if (s_lst.Count > 0)
                    {
                        senderSocketAsyncEventArgs = s_lst[s_lst.Count - 1];
                        s_lst.RemoveAt(s_lst.Count - 1);
                    }
                }
                if (senderSocketAsyncEventArgs == null)
                {
                    senderSocketAsyncEventArgs = new SocketAsyncEventArgs();
                    senderSocketAsyncEventArgs.UserToken = this.clientSocket;
                    senderSocketAsyncEventArgs.RemoteEndPoint = this.hostEndPoint;
                    senderSocketAsyncEventArgs.Completed += (object sender, SocketAsyncEventArgs _e) =>
                        {
                            lock (s_lst)
                            {
                                s_lst.Add(senderSocketAsyncEventArgs);
                            }
                        };
                }
                senderSocketAsyncEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
                clientSocket.SendAsync(senderSocketAsyncEventArgs);
            }
            else
            {
                this.connected = false;
            }
            SendMess = message;
        }
        /// <summary>
        /// 断开连接
        /// </summary>
        private bool Disconnect()
        {
            bool returnDis = true;
            try
            {
                this.clientSocket.Shutdown(SocketShutdown.Both);
                this.clientSocket.Close();
                //this.clientSocket.Dispose();
                //clientSocket.Disconnect(true);
                //clientSocket.Disconnect(false);
            }
            catch (Exception)
            {
                returnDis = false;
            }
            finally
            {
            }
            this.connected = false;
            return returnDis;
        }
        /// <summary>
        /// 连接的完成方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnConnect(object sender, SocketAsyncEventArgs e)
        {
            this.connected = (e.SocketError == SocketError.Success);
            autoConnectEvent.Set();
        }
        /// <summary>
        /// 接收的完成方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnReceive(object sender, SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred == 0)
            {
                //Console.WriteLine("Socket is closed", Socket.Handle);
                if (clientSocket.Connected)
                {
                    try
                    {
                        clientSocket.Shutdown(SocketShutdown.Both);
                    }
                    catch (Exception)
                    {
                        //client already closed
                    }
                    finally
                    {
                        if (clientSocket.Connected)
                        {
                            clientSocket.Close();
                        }
                    }
                }
                Byte[] rs = new Byte[0];
                OnMsgReceived(rs);
                this.connected = false;
            }
            else
            {
                byte[] buffer = new byte[e.BytesTransferred];
                for (int i = 0; i < e.BytesTransferred; i++)
                {
                    buffer[i] = e.Buffer[i];
                }
                this.OnMsgReceived(buffer);
                Listen();
            }
        }
        /// <summary>
        /// 发送的完成方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSend(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                OnSended(true);
            }
            else
            {
                OnSended(false);
                this.ProcessError(e);
            }
        }
        /// <summary>
        /// 处理错误
        /// </summary>
        /// <param name="e"></param>
        private void ProcessError(SocketAsyncEventArgs e)
        {
            Socket s = e.UserToken as Socket;
            if (s.Connected)
            {
                try
                {
                    s.Shutdown(SocketShutdown.Both);
                }
                catch (Exception)
                {
                    //client already closed
                }
                finally
                {
                    if (s.Connected)
                    {
                        s.Close();
                    }
                }
                this.connected = false;
            }
            //throw new SocketException((Int32)e.SocketError);
        }
        #region IDisposable Members
        public void Dispose()
        {
            autoConnectEvent.Close();
            if (this.clientSocket.Connected)
            {
                this.clientSocket.Close();
            }
        }
        #endregion
        #region  欧姆龙通讯协议
        /// <summary>
        /// 发送命令反馈
        /// </summary>
        /// <param name="successorfalse"></param>
        void OmronFINS_OnSended(bool successorfalse)
        {
            if (!successorfalse)
            {
            }
            else
            {
            }
        }
        Byte PCS;
        Byte PLCS;
        int SendOrRev;
        int SendOrRev2;
        Byte[] SendBack;
        Byte[] RecvBack;
        /// <summary>
        /// 接受命令反馈
        /// </summary>
        /// <param name="info"></param>
        void OmronFINS_OnMsgReceived(byte[] info)
        {
            if (SendmessHas)
            {
                if (info.Length >= 24)
                {
                    if (int.Parse(info[23].ToString("X").Trim(), System.Globalization.NumberStyles.HexNumber) == Plcport)
                    {
                        PCS = info[19];
                        PLCS = info[23];
                        SendmessHas = false;
                    }
                }
            }
            else
            {
                if (info.Length > 0)//PLC连接错误NO
                {
                    if (SendOrRev2 == 1)//发送命令
                    {
                        SendBack = info;
                        SendOrRev2 = 0;
                    }
                    else if (SendOrRev == 2)//接受命令
                    {
                        RecvBack = info;
                        SendOrRev = 0;
                    }
                }
                else
                {
                    if (SendOrRev2 == 1)//发送命令
                    {
                        SendBack = new Byte[1];
                        SendBack[0] = 0x00;
                        SendOrRev2 = 0;
                    }
                    else if (SendOrRev == 2)//接受命令
                    {
                        RecvBack = new Byte[1];
                        RecvBack[0] = 0x00;
                        SendOrRev = 0;
                    }
                }
            }
        }
        void OmronFINS_StartListenThread()
        {
            this.Listen();
        }
        bool SendmessHas = false;
        /// <summary>
        /// 打开连接
        /// </summary>
        /// <returns></returns>
        public bool OpenLinkPLC()
        {
            bool Ret = false;
            this.StartListenThread += new StartListenHandler(OmronFINS_StartListenThread);
            this.OnMsgReceived += new ReceiveMsgHandler(OmronFINS_OnMsgReceived);
            this.OnSended += new SendCompleted(OmronFINS_OnSended);
            Ret = this.Connect();
            if (Ret)
            {
                SendmessHas = true;
                this.Send(GetPCAdress());
                int addNs = 0;
                while (SendmessHas && addNs < 500)
                {
                    Thread.Sleep(2);
                    addNs++;
                }
                if (addNs < 500)
                {
                    Ret = true;
                }
                else
                {
                    Ret = false;
                }
            }
            return Ret;
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public bool CloseLinkPLC()
        {
            return this.Disconnect();
        }
        /// <summary>
        /// 写入值(Word)
        /// </summary>
        /// <param name="TypeInt">起始寄存器地址</param>
        /// <param name="NumData">个数</param>
        /// <param name="IntValue">值</param>
        /// <returns>true,成功;false,失败</returns>
        public bool WritePlcData(string TypeInt, int NumData, ref int[] IntValue)
        {
            bool sendRerun = false;
            if (NumData > 0)
            {
                Byte[] numData = BitConverter.GetBytes(NumData);//Use numData[0] and numData[1]
                Byte[] SendmessAge = new Byte[34 + NumData * 2];
                if (GetType(TypeInt, 0) != 0x00 && Address != null && Address.Length > 1)
                {
                    Byte[] getSendFunc = this.SetPLCvalue(PLCS, PCS, GetType(TypeInt, 0), Address[1], Address[0], 0x00, numData[1], numData[0], NumData);
                    for (int i = 0; i < 34 + NumData * 2; i++)
                    {
                        if (i < 34)
                        {
                            SendmessAge[i] = getSendFunc[i];
                        }
                        else
                        {
                            if ((i - 33) % 2 != 0)
                            {
                                Byte[] BuffInvalue = BitConverter.GetBytes(IntValue[(i - 33) / 2]);
                                SendmessAge[i] = BuffInvalue[1];
                            }
                            else
                            {
                                Byte[] BuffInvalue = BitConverter.GetBytes(IntValue[(i - 33) / 2 - 1]);
                                SendmessAge[i] = BuffInvalue[0];
                            }
                            //SendmessAge[i]
                        }
                    }
                    SendOrRev2 = 1;
                    this.Send(SendmessAge);
                    int Outtime = 0;
                    while (SendOrRev2 != 0 && this.connected && Outtime < 600)
                    {
                        Thread.Sleep(2);
                        Outtime++;
                    }
                    if (Outtime < 600 && SendBack != null)
                    {
                        try
                        {
                            //if (SendBack.Length > 1 && SendBack[26] == 0x01 && SendBack[27] == 0x02 && SendBack[28] == 0x00 && SendBack[29] == 0x00)
                            //{
                            if (SendBack.Length > 1 && SendBack[26] == 0x01 && SendBack[27] == 0x02 && SendBack[28] == 0x00)
                            {
                                sendRerun = true;
                            }
                            else
                            {
                                sendRerun = false;
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }
                    else
                    {
                        SendOrRev2 = 0;
                    }
                }
            }
            return sendRerun;
        }
        /// <summary>
        /// 读取值(Word)
        /// </summary>
        /// <param name="TypeInt">起始寄存器地址</param>
        /// <param name="NumData">个数</param>
        /// <param name="IntValue">值</param>
        /// <returns></returns>
        public bool ReadPlcData(string TypeInt, int NumData, out int[] IntValue)
        {
            bool GetPlcRet = false;
            IntValue = new int[1];
            if (NumData > 0)
            {
                IntValue = new int[NumData];
                Byte[] numData = BitConverter.GetBytes(NumData);//Use numData[0] and numData[1]
                Byte[] RecivemessAge = new Byte[34];
                if (GetType(TypeInt, 0) != 0x00 && Address != null && Address.Length > 1)
                {
                    Byte[] getReciveFunc = this.GetPLCvalue(PLCS, PCS, GetType(TypeInt, 0), Address[1], Address[0], 0x00, numData[1], numData[0]);
                    this.Send(getReciveFunc);
                    SendOrRev = 2;
                    int Outtime = 0;
                    while (SendOrRev != 0 && this.connected && Outtime < 600)
                    {
                        Thread.Sleep(2);
                        Outtime++;
                    }
                    if (Outtime < 600 && RecvBack != null)
                    {
                        try
                        {
                            //if (RecvBack.Length > 1 && RecvBack[26] == 0x01 && RecvBack[27] == 0x01 && RecvBack[28] == 0x00 && RecvBack[29] == 0x00)
                            if (RecvBack.Length == 30 + NumData * 2 && RecvBack[26] == 0x01 && RecvBack[27] == 0x01 && RecvBack[28] == 0x00)
                            {
                                GetPlcRet = true;
                                for (int i = 0; i < NumData; i++)
                                {
                                    IntValue[i] = int.Parse(RecvBack[30 + i * 2].ToString("X").PadLeft(2, '0') + RecvBack[30 + i * 2 + 1].ToString("X").PadLeft(2, '0'), System.Globalization.NumberStyles.HexNumber);
                                }
                            }
                            else
                            {
                                GetPlcRet = false;
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }
                    else
                    {
                        SendOrRev = 0;
                    }
                }
            }
            return GetPlcRet;
        }
        /// <summary>
        /// 读取值(Word)
        /// </summary>
        /// <param name="TypeInt">起始寄存器地址</param>
        /// <param name="NumData">个数</param>
        /// <param name="IntValue">值</param>
        /// <returns></returns>
        public bool ReadPlcDataCIO(string TypeInt, int NumData, out int[] IntValue)
        {
            bool GetPlcRet = false;
            IntValue = new int[1];
            if (NumData > 0)
            {
                IntValue = new int[NumData];
                Byte[] numData = BitConverter.GetBytes(NumData);//Use numData[0] and numData[1]
                Byte[] RecivemessAge = new Byte[34];
                if (GetType(TypeInt, 0) != 0x00 && Address != null && Address.Length > 1)
                {
                    Byte[] getReciveFunc = this.GetPLCvalue(PLCS, PCS, GetType(TypeInt, 0), Address[1], Address[0], 0x00, numData[1], numData[0]);
                    this.Send(getReciveFunc);
                    SendOrRev = 2;
                    int Outtime = 0;
                    while (SendOrRev != 0 & this.connected & Outtime < 800)
                    {
                        Thread.Sleep(2);
                        Outtime++;
                    }
                    if (Outtime < 800 && RecvBack != null)
                    {
                        //try
                        //{
                        //if (RecvBack.Length > 1 && RecvBack[26] == 0x01 && RecvBack[27] == 0x01 && RecvBack[28] == 0x00 && RecvBack[29] == 0x00)
                        if (RecvBack.Length > 30 && RecvBack[26] == 0x01 && RecvBack[27] == 0x01 && RecvBack[28] == 0x00)
                        {
                            GetPlcRet = true;
                            for (int i = 0; i < NumData; i++)
                            {
                                IntValue[i] = int.Parse(RecvBack[30 + i * 2].ToString("X").PadLeft(2, '0') + RecvBack[30 + i * 2 + 1].ToString("X").PadLeft(2, '0'), System.Globalization.NumberStyles.HexNumber);
                            }
                        }
                        else
                        {
                            GetPlcRet = false;
                        }
                        /

本文标题为:C#实现上位机与欧姆龙PLC通讯(FINS)

基础教程推荐