欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#實現(xiàn)上位機與歐姆龍PLC通訊(FINS)

 更新時間:2023年05月08日 14:27:23   作者:工控程序狗  
這篇文章主要介紹了C#實現(xiàn)上位機與歐姆龍PLC通訊(FINS)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

先介紹下一些基本定義

串行通信:通過的是PLC上的串行口RS232/RS422/485口,上位機鏈接系統(tǒng) Hostlink系統(tǒng)是對于FA系統(tǒng)一種及優(yōu)化有經(jīng)濟的通信方式。

適用于一臺上位機與一臺或者多臺的PLC進行數(shù)據(jù)通信。

通訊協(xié)議分兩種

1:C-mode commands

只可以通過串口進行通訊

2:Fins commands

既可以通過串口通訊也可以通過各類網(wǎng)絡通訊(適應性較強)

本文只介紹fins通訊

fins (factory in terfave network service) 通訊協(xié)議是歐姆龍公司開發(fā)的一款自動化控制網(wǎng)絡指令/響應系統(tǒng)的通訊協(xié)議;運用FINS指令可實現(xiàn)各種網(wǎng)絡間的無縫通信

FINS幀結構

  • 發(fā)送命令結構 命令碼2個字節(jié)
  • 響應命令結構 命令碼2個字節(jié)
  • 命令碼 01 01讀數(shù)據(jù)
  • 命令碼 01 02寫數(shù)據(jù)
  • 結束碼 00 00無錯誤

1、獲取PLC節(jié)點地址

上位機和PLC建立TCP通訊之后,可以發(fā)送以下命令來獲得PLC的節(jié)點地址;

幀結構見下表:

列表Content(十六進制)Description(說明)
46 49 4E 53ASCII分別代表的是 F I N S
長度00 00 00 0C從Command之后的數(shù)據(jù)包的長度
錯誤代碼00 00 00 00目前暫時沒用,因為在服務器不需要檢測內部錯誤
連接的節(jié)點地址00000000~000000FE分別是0~254,當設置為0時表示自動獲取客戶端的Fins節(jié)點地址

2、命令碼介紹

詳細內容說明介紹見下表:

命令內容命令代碼(MR SR)說明功能
訪問I/O存儲區(qū)01 01讀內存區(qū)讀連續(xù)I/O存儲區(qū)字的內容
訪問I/O存儲區(qū)01 02寫內存區(qū)寫連續(xù)I/O存儲區(qū)字的內容
訪問I/O存儲區(qū)01 03填充內存區(qū)將相同的數(shù)據(jù)寫入指定范圍的I/O存儲器區(qū)
訪問I/O存儲區(qū)01 04多個存儲區(qū)讀取讀取指定的非連續(xù)I/O存儲區(qū)字
訪問I/O存儲區(qū)01 05存儲區(qū)傳輸將連續(xù)存儲I/O存儲區(qū)字內容復制到另外的I/O存儲區(qū)
訪問參數(shù)區(qū)02 01讀取參數(shù)區(qū)讀取連續(xù)參數(shù)區(qū)字內容
訪問參數(shù)區(qū)02 02寫入?yún)?shù)區(qū)寫入連續(xù)參數(shù)區(qū)字內容
訪問參數(shù)區(qū)02 03填充參數(shù)區(qū)將相同數(shù)據(jù)寫入到指定范圍參數(shù)區(qū)域字
改變操作模式04 01設置CPU操作模式為RUN將CPU單元的操作模式更改為RUN或MONITOR
改變操作模式04 02設置CPU操作模式為STOP將CPU單元的操作模式更改為編程
改變操作模式06 01讀取CPU單元狀態(tài)讀取CPU單元狀態(tài)
錯誤日志21 01錯誤清除清除錯誤或錯誤信息
錯誤日志21 02讀取錯誤日志讀取錯誤日志
錯誤日志21 03清除錯誤日志清除錯誤日志指針

3、I / O存儲器地址標識

區(qū)域數(shù)據(jù)類型存儲區(qū)代碼存儲區(qū)地址范圍存儲地址字節(jié)長度
DMBit02D0000000到D3276715000000到7FFF0F1
DMWord82D00000到D32767000000到7FFF002

鑒于我們在和PLC通訊時,一般只需要進行讀取DM區(qū)寄存器操作,因為本文只介紹讀取和寫入DM區(qū)寄存器。其他的有需要的童鞋進行自己功能拓展。

舉例:

讀取DM區(qū)地址100,連續(xù)10個地址的數(shù)據(jù)

發(fā)送命令:010182006400000A

返回命令:010100000102030405060708090A

發(fā)送的命令進行說明:

  • 1、01 01代表功能碼,讀連續(xù)I/O存儲區(qū)字的內容
  • 2、82代表進行字操作
  • 3、00 64轉成十進制就是100代表的是我們要讀取的起始地址
  • 4、00000A轉成十進制就是10代表的是我們要讀取得長度

響應的命令說明

  • 1、01 01代表功能碼,讀連續(xù)I/O存儲區(qū)字的內容
  • 2、00 00代表結束碼,當不是00 00的時候表明數(shù)據(jù)幀不對
  • 3、01 02 03 04 05 06 07 08 09 0A分別代表要讀取的十個寄存器的數(shù)據(jù)值

具體協(xié)議可以查看百度文庫:歐姆龍fins通訊協(xié)議

綜上,結合之前的博客,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>
        /// 連接狀態(tài)
        /// </summary>
        public Boolean connected = false;
        /// <summary>
        /// 發(fā)送數(shù)據(jù)
        /// </summary>
        private Byte[] SendMess;
        /// <summary>
        /// 連接點
        /// </summary>
        private IPEndPoint hostEndPoint;
        /// <summary>
        /// 連接信號量
        /// </summary>
        private static AutoResetEvent autoConnectEvent = new AutoResetEvent(false);
        /// <summary>
        /// 接受到數(shù)據(jù)時的委托
        /// </summary>
        /// <param name="info"></param>
        public delegate void ReceiveMsgHandler(Byte[] info);
        /// <summary>
        /// 接收到數(shù)據(jù)時調用的事件
        /// </summary>
        public event ReceiveMsgHandler OnMsgReceived;
        /// <summary>
        /// 開始監(jiān)聽數(shù)據(jù)的委托
        /// </summary>
        public delegate void StartListenHandler();
        /// <summary>
        /// 開始監(jiān)聽數(shù)據(jù)的事件
        /// </summary>
        public event StartListenHandler StartListenThread;
        /// <summary>
        /// 發(fā)送信息完成的委托
        /// </summary>
        /// <param name="successorfalse"></param>
        public delegate void SendCompleted(bool successorfalse);
        /// <summary>
        /// 發(fā)送信息完成的事件
        /// </summary>
        public event SendCompleted OnSended;
        /// <summary>
        /// 監(jiān)聽接收的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區(qū)大小
                    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>
        /// 開始監(jiān)聽線程的入口函數(shù)
        /// </summary>
        private void Listen()
        {
            (listenerSocketAsyncEventArgs.UserToken as Socket).ReceiveAsync(listenerSocketAsyncEventArgs);
        }
        public static List<SocketAsyncEventArgs> s_lst = new List<SocketAsyncEventArgs>();
        /// <summary>
        /// 發(fā)送信息
        /// </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>
        /// 發(fā)送的完成方法
        /// </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  歐姆龍通訊協(xié)議
        /// <summary>
        /// 發(fā)送命令反饋
        /// </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)//發(fā)送命令
                    {
                        SendBack = info;
                        SendOrRev2 = 0;
                    }
                    else if (SendOrRev == 2)//接受命令
                    {
                        RecvBack = info;
                        SendOrRev = 0;
                    }
                }
                else
                {
                    if (SendOrRev2 == 1)//發(fā)送命令
                    {
                        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">個數(shù)</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">個數(shù)</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">個數(shù)</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;
                        }
                        //}
                        //catch (Exception)
                        //{
                        //}
                    }
                    else
                    {
                        SendOrRev = 0;
                    }
                }
            }
            return GetPlcRet;
        }
        /// <summary>
        /// 讀取值(Bit)
        /// </summary>
        /// <param name="TypeInt">起始寄存器地址</param>
        /// <param name="IntValue">值</param>
        /// <returns></returns>
        public bool ReadPlcBitData(string TypeInt, out int IntValue)
        {
            bool GetPlcRet = false;
            IntValue = 0;
            Byte[] RecivemessAge = new Byte[34];
            if (GetType(TypeInt, 1) != 0x00 && Address != null && Address.Length > 1)
            {
                Byte[] getReciveFunc = this.GetPLCvalue(PLCS, PCS, GetType(TypeInt, 1), Address[1], Address[0], AddressBit[0]);
                this.Send(getReciveFunc);
                SendOrRev = 2;
                int Outtime = 0;
                while (SendOrRev != 0 & Outtime < 600)
                {
                    Thread.Sleep(2);
                    Outtime++;
                }
                if (Outtime < 600)
                {
                    if (RecvBack.Length > 1 && RecvBack[26] == 0x01 && RecvBack[27] == 0x01 && RecvBack[28] == 0x00 && RecvBack[29] == 0x00)
                    {
                        GetPlcRet = true;
                        IntValue = int.Parse(RecvBack[30].ToString("X").PadLeft(2, '0'), System.Globalization.NumberStyles.HexNumber);
                    }
                }
                else
                {
                    SendOrRev = 0;
                }
            }
            return GetPlcRet;
        }
        /// <summary>
        /// Write值(bit)
        /// </summary>
        /// <param name="TypeInt">寄存器地址(CIOX.X)</param>
        /// <returns></returns>
        public bool WriteBitPlcData(string TypeInt, int ItemValue)
        {
            bool sendRerun = false;
            Byte[] SendmessAge = new Byte[35];
            if (GetType(TypeInt, 1) != 0x00 && Address != null && Address.Length > 1)
            {
                Byte[] getSendFunc = this.WritePLCBitvalue(PLCS, PCS, GetType(TypeInt, 1), Address[1], Address[0], AddressBit[0]);
                for (int i = 0; i < 35; i++)
                {
                    if (i < 32)
                    {
                        SendmessAge[i] = getSendFunc[i];
                    }
                    else if (i == 32)
                    {
                        SendmessAge[i] = 0x00;
                    }
                    else if (i == 33)
                    {
                        SendmessAge[i] = 0x01;
                    }
                    else
                    {
                        if (ItemValue == 1)
                            SendmessAge[i] = 0x01;
                        else
                            SendmessAge[i] = 0x00;
                    }
                }
                this.Send(SendmessAge);
                SendOrRev2 = 1;
                int Outtime = 0;
                while (SendOrRev2 != 0 & Outtime < 600)
                {
                    Thread.Sleep(2);
                    Outtime++;
                }
                if (Outtime < 600)
                {
                    if (SendBack.Length > 1 && SendBack[26] == 0x01 && SendBack[27] == 0x02 && SendBack[28] == 0x00 && SendBack[29] == 0x00)
                    {
                        sendRerun = true;
                    }
                }
                else
                {
                    SendOrRev2 = 0;
                }
            }
            return sendRerun;
        }
        /// <summary>
        /// Set 值(bit)
        /// </summary>
        /// <param name="TypeInt">寄存器地址(CIOX.X)</param>
        /// <returns></returns>
        public bool SetPlcData(string TypeInt)
        {
            bool sendRerun = false;
            Byte[] SendmessAge = new Byte[35];
            if (GetType(TypeInt, 1) != 0x00 && Address != null && Address.Length > 1)
            {
                Byte[] getSendFunc = this.SetPLCBitvalue(PLCS, PCS, GetType(TypeInt, 1), Address[1], Address[0], AddressBit[0]);
                for (int i = 0; i < 35; i++)
                {
                    if (i < 34)
                    {
                        SendmessAge[i] = getSendFunc[i];
                    }
                    else
                    {
                        SendmessAge[i] = 0x00;
                    }
                }
                this.Send(SendmessAge);
                SendOrRev2 = 1;
                int Outtime = 0;
                while (SendOrRev2 != 0 & Outtime < 600)
                {
                    Thread.Sleep(2);
                    Outtime++;
                }
                if (Outtime < 600)
                {
                    if (SendBack.Length > 1 && SendBack[26] == 0x01 && SendBack[27] == 0x02 && SendBack[28] == 0x00 && SendBack[29] == 0x00)
                    {
                        sendRerun = true;
                    }
                }
            }
            return sendRerun;
        }
        /// <summary>
        /// Rest 值(bit)
        /// </summary>
        /// <param name="TypeInt">寄存器地址(CIOX.X)</param>
        /// <returns></returns>
        public bool RestPlcData(string TypeInt)
        {
            bool sendRerun = false;
            Byte[] SendmessAge = new Byte[35];
            if (GetType(TypeInt, 1) != 0x00 && Address != null && Address.Length > 1)
            {
                Byte[] getSendFunc = this.RstPLCBitvalue(PLCS, PCS, GetType(TypeInt, 1), Address[1], Address[0], AddressBit[0]);
                for (int i = 0; i < 35; i++)
                {
                    if (i < 34)
                    {
                        SendmessAge[i] = getSendFunc[i];
                    }
                    else
                    {
                        SendmessAge[i] = 0x00;
                    }
                }
                this.Send(SendmessAge);
                SendOrRev2 = 1;
                int Outtime = 0;
                while (SendOrRev2 != 0 & Outtime < 600)
                {
                    Thread.Sleep(2);
                    Outtime++;
                }
                if (Outtime < 600)
                {
                    if (SendBack.Length > 1 && SendBack[26] == 0x01 && SendBack[27] == 0x02 && SendBack[28] == 0x00 && SendBack[29] == 0x00)
                    {
                        sendRerun = true;
                    }
                }
            }
            return sendRerun;
        }
        Byte[] Address;
        Byte[] AddressBit;
        private Byte GetType(string TypeInt, int SizeofInt)
        {
            Byte ms = 0x00;
            if (TypeInt.IndexOf("D") == 0 | TypeInt.IndexOf("d") == 0)
            {
                if (SizeofInt == 0)
                {
                    ms = 0x82;
                    if (TypeInt.IndexOf(".") == -1)
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.Length - 1)));
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
                else
                {
                    ms = 0x02;
                    if (TypeInt.IndexOf(".") > 1)
                    {
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.IndexOf(".") - 1)));
                        AddressBit = BitConverter.GetBytes(int.Parse(TypeInt.Substring(TypeInt.IndexOf(".") + 1, TypeInt.Length - TypeInt.IndexOf(".") - 1)));
                    }
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
            }
            else if (TypeInt.IndexOf("CIO") == 0 | TypeInt.IndexOf("cio") == 0)
            {
                if (SizeofInt == 0)
                {
                    ms = 0xB0;
                    if (TypeInt.IndexOf(".") == -1)
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(3, TypeInt.Length - 3)));
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
                else
                {
                    ms = 0x30;
                    if (TypeInt.IndexOf(".") > 3)
                    {
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(3, TypeInt.IndexOf(".") - 3)));
                        AddressBit = BitConverter.GetBytes(int.Parse(TypeInt.Substring(TypeInt.IndexOf(".") + 1, TypeInt.Length - TypeInt.IndexOf(".") - 1)));
                    }
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
            }
            else if (TypeInt.IndexOf("W") == 0 | TypeInt.IndexOf("w") == 0)
            {
                if (SizeofInt == 0)
                {
                    ms = 0xB1;
                    if (TypeInt.IndexOf(".") == -1)
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.Length - 1)));
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
                else
                {
                    ms = 0x31;
                    if (TypeInt.IndexOf(".") > 1)
                    {
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.IndexOf(".") - 1)));
                        AddressBit = BitConverter.GetBytes(int.Parse(TypeInt.Substring(TypeInt.IndexOf(".") + 1, TypeInt.Length - TypeInt.IndexOf(".") - 1)));
                    }
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
            }
            else if (TypeInt.IndexOf("H") == 0 | TypeInt.IndexOf("h") == 0)
            {
                if (SizeofInt == 0)
                {
                    ms = 0xB2;
                    if (TypeInt.IndexOf(".") == -1)
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.Length - 1)));
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
                else
                {
                    ms = 0x32;
                    if (TypeInt.IndexOf(".") > 1)
                    {
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.IndexOf(".") - 1)));
                        AddressBit = BitConverter.GetBytes(int.Parse(TypeInt.Substring(TypeInt.IndexOf(".") + 1, TypeInt.Length - TypeInt.IndexOf(".") - 1)));
                    }
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
            }
            else if (TypeInt.IndexOf("A") == 0 | TypeInt.IndexOf("a") == 0)
            {
                if (SizeofInt == 0)
                {
                    ms = 0xB3;
                    if (TypeInt.IndexOf(".") == -1)
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.Length - 1)));
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
                else
                {
                    ms = 0x33;
                    if (TypeInt.IndexOf(".") > 1)
                    {
                        Address = BitConverter.GetBytes(int.Parse(TypeInt.Substring(1, TypeInt.IndexOf(".") - 1)));
                        AddressBit = BitConverter.GetBytes(int.Parse(TypeInt.Substring(TypeInt.IndexOf(".") + 1, TypeInt.Length - TypeInt.IndexOf(".") - 1)));
                    }
                    else
                    {
                        Address = new Byte[1];
                        AddressBit = new Byte[1];
                    }
                }
            }
            return ms;
        }
        /// <summary>
        /// Get PLC DA1 SNA1
        /// </summary>
        /// <returns></returns>
        private Byte[] GetPCAdress()
        {
            Byte[] ReturnGetPC = new Byte[20];
            ReturnGetPC[0] = 0x46;//f
            ReturnGetPC[1] = 0x49;//i
            ReturnGetPC[2] = 0x4E;//n
            ReturnGetPC[3] = 0x53;//s
            ReturnGetPC[4] = 0x00;
            ReturnGetPC[5] = 0x00;
            ReturnGetPC[6] = 0x00;
            ReturnGetPC[7] = 0x0C;
            ReturnGetPC[8] = 0x00;
            ReturnGetPC[9] = 0x00;
            ReturnGetPC[10] = 0x00;
            ReturnGetPC[11] = 0x00;
            ReturnGetPC[12] = 0x00;
            ReturnGetPC[13] = 0x00;
            ReturnGetPC[14] = 0x00;
            ReturnGetPC[15] = 0x00;
            ReturnGetPC[16] = 0x00;
            ReturnGetPC[17] = 0x00;
            ReturnGetPC[18] = 0x00;
            ReturnGetPC[19] = 0x00;
            return ReturnGetPC;
        }
        /// <summary>
        /// Read Word
        /// </summary>
        /// <param name="DA1"></param>
        /// <param name="SA1"></param>
        /// <param name="DataValue"></param>
        /// <param name="Address1"></param>
        /// <param name="Address2"></param>
        /// <param name="Address3"></param>
        /// <param name="DataNum1"></param>
        /// <param name="DataNum2"></param>
        /// <returns></returns>
        private Byte[] GetPLCvalue(Byte DA1, Byte SA1, Byte DataValue, Byte Address1, Byte Address2, Byte Address3, Byte DataNum1, Byte DataNum2)
        {
            Byte[] SendFun = new Byte[34];
            SendFun[0] = 0x46;
            SendFun[1] = 0x49;
            SendFun[2] = 0x4E;
            SendFun[3] = 0x53;
            SendFun[4] = 0x00;
            SendFun[5] = 0x00;
            SendFun[6] = 0x00;
            SendFun[7] = 0x1A;
            SendFun[8] = 0x00;
            SendFun[9] = 0x00;
            SendFun[10] = 0x00;
            SendFun[11] = 0x02;
            SendFun[12] = 0x00;
            SendFun[13] = 0x00;
            SendFun[14] = 0x00;
            SendFun[15] = 0x00;
            SendFun[16] = 0x80;
            SendFun[17] = 0x00;
            SendFun[18] = 0x02;
            SendFun[19] = 0x00;
            SendFun[20] = DA1;
            SendFun[21] = 0x00;
            SendFun[22] = 0x00;
            SendFun[23] = SA1;
            SendFun[24] = 0x00;
            SendFun[25] = 0x01;
            SendFun[26] = 0x01;
            SendFun[27] = 0x01;
            SendFun[28] = DataValue;
            SendFun[29] = Address1;
            SendFun[30] = Address2;
            SendFun[31] = Address3;
            SendFun[32] = DataNum1;
            SendFun[33] = DataNum2;
            return SendFun;
        }
        /// <summary>
        /// Write Word
        /// </summary>
        /// <param name="DA1"></param>
        /// <param name="SA1"></param>
        /// <param name="DataValue"></param>
        /// <param name="Address1"></param>
        /// <param name="Address2"></param>
        /// <param name="Address3"></param>
        /// <param name="DataNum1"></param>
        /// <param name="DataNum2"></param>
        /// <returns></returns>
        private Byte[] SetPLCvalue(Byte DA1, Byte SA1, Byte DataValue, Byte Address1, Byte Address2, Byte Address3, Byte DataNum1, Byte DataNum2, int num)
        {
            Byte[] SendFun = new Byte[34];
            SendFun[0] = 0x46;
            SendFun[1] = 0x49;
            SendFun[2] = 0x4E;
            SendFun[3] = 0x53;
            SendFun[4] = 0x00;
            SendFun[5] = 0x00;
            SendFun[6] = 0x00;
            SendFun[7] = (byte)(26 + 2 * num);
            SendFun[8] = 0x00;
            SendFun[9] = 0x00;
            SendFun[10] = 0x00;
            SendFun[11] = 0x02;
            SendFun[12] = 0x00;
            SendFun[13] = 0x00;
            SendFun[14] = 0x00;
            SendFun[15] = 0x00;
            SendFun[16] = 0x80;
            SendFun[17] = 0x00;
            SendFun[18] = 0x02;
            SendFun[19] = 0x00;
            SendFun[20] = DA1;
            SendFun[21] = 0x00;
            SendFun[22] = 0x00;
            SendFun[23] = SA1;
            SendFun[24] = 0x00;
            SendFun[25] = 0x01;
            SendFun[26] = 0x01;
            SendFun[27] = 0x02;
            SendFun[28] = DataValue;
            SendFun[29] = Address1;
            SendFun[30] = Address2;
            SendFun[31] = Address3;
            SendFun[32] = DataNum1;
            SendFun[33] = DataNum2;
            return SendFun;
        }
        /// <summary>
        /// Read Bit
        /// /// </summary>
        /// <param name="DA1"></param>
        /// <param name="SA1"></param>
        /// <param name="DataValue"></param>
        /// <param name="Address1"></param>
        /// <param name="Address2"></param>
        /// <param name="Address3"></param>
        /// <param name="DataNum1"></param>
        /// <param name="DataNum2"></param>
        /// <returns></returns>
        private Byte[] GetPLCvalue(Byte DA1, Byte SA1, Byte DataValue, Byte Address1, Byte Address2, Byte Address3)
        {
            Byte[] SendFun = new Byte[34];
            SendFun[0] = 0x46;
            SendFun[1] = 0x49;
            SendFun[2] = 0x4E;
            SendFun[3] = 0x53;
            SendFun[4] = 0x00;
            SendFun[5] = 0x00;
            SendFun[6] = 0x00;
            SendFun[7] = 0x1A;
            SendFun[8] = 0x00;
            SendFun[9] = 0x00;
            SendFun[10] = 0x00;
            SendFun[11] = 0x02;
            SendFun[12] = 0x00;
            SendFun[13] = 0x00;
            SendFun[14] = 0x00;
            SendFun[15] = 0x00;
            SendFun[16] = 0x80;
            SendFun[17] = 0x00;
            SendFun[18] = 0x02;
            SendFun[19] = 0x00;
            SendFun[20] = DA1;
            SendFun[21] = 0x00;
            SendFun[22] = 0x00;
            SendFun[23] = SA1;
            SendFun[24] = 0x00;
            SendFun[25] = 0x01;
            SendFun[26] = 0x01;
            SendFun[27] = 0x01;
            SendFun[28] = DataValue;
            SendFun[29] = Address1;
            SendFun[30] = Address2;
            SendFun[31] = Address3;
            SendFun[32] = 0x00;
            SendFun[33] = 0x01;
            return SendFun;
        }
        /// <summary>
        /// Write Bit
        /// </summary>
        /// <param name="DA1"></param>
        /// <param name="SA1"></param>
        /// <param name="DataValue"></param>
        /// <param name="Address1"></param>
        /// <param name="Address2"></param>
        /// <param name="Address3"></param>
        /// <returns></returns>
        private Byte[] WritePLCBitvalue(Byte DA1, Byte SA1, Byte DataValue, Byte Address1, Byte Address2, Byte Address3)
        {
            Byte[] SendFun = new Byte[32];
            SendFun[0] = 0x46;
            SendFun[1] = 0x49;
            SendFun[2] = 0x4E;
            SendFun[3] = 0x53;
            SendFun[4] = 0x00;
            SendFun[5] = 0x00;
            SendFun[6] = 0x00;
            SendFun[7] = 0x1B;
            SendFun[8] = 0x00;
            SendFun[9] = 0x00;
            SendFun[10] = 0x00;
            SendFun[11] = 0x02;
            SendFun[12] = 0x00;
            SendFun[13] = 0x00;
            SendFun[14] = 0x00;
            SendFun[15] = 0x00;
            SendFun[16] = 0x80;
            SendFun[17] = 0x00;
            SendFun[18] = 0x02;
            SendFun[19] = 0x00;
            SendFun[20] = DA1;
            SendFun[21] = 0x00;
            SendFun[22] = 0x00;
            SendFun[23] = SA1;
            SendFun[24] = 0x00;
            SendFun[25] = 0x01;
            SendFun[26] = 0x01;
            SendFun[27] = 0x02;
            SendFun[28] = DataValue;
            SendFun[29] = Address1;
            SendFun[30] = Address2;
            SendFun[31] = Address3;
            return SendFun;
        }
        /// <summary>
        /// Set Bit
        /// </summary>
        /// <param name="DA1"></param>
        /// <param name="SA1"></param>
        /// <param name="DataValue"></param>
        /// <param name="Address1"></param>
        /// <param name="Address2"></param>
        /// <param name="Address3"></param>
        /// <returns></returns>
        private Byte[] SetPLCBitvalue(Byte DA1, Byte SA1, Byte DataValue, Byte Address1, Byte Address2, Byte Address3)
        {
            Byte[] SendFun = new Byte[35];
            SendFun[0] = 0x46;
            SendFun[1] = 0x49;
            SendFun[2] = 0x4E;
            SendFun[3] = 0x53;
            SendFun[4] = 0x00;
            SendFun[5] = 0x00;
            SendFun[6] = 0x00;
            SendFun[7] = 0x1B;
            SendFun[8] = 0x00;
            SendFun[9] = 0x00;
            SendFun[10] = 0x00;
            SendFun[11] = 0x02;
            SendFun[12] = 0x00;
            SendFun[13] = 0x00;
            SendFun[14] = 0x00;
            SendFun[15] = 0x00;
            SendFun[16] = 0x80;
            SendFun[17] = 0x00;
            SendFun[18] = 0x02;
            SendFun[19] = 0x00;
            SendFun[20] = DA1;
            SendFun[21] = 0x00;
            SendFun[22] = 0x00;
            SendFun[23] = SA1;
            SendFun[24] = 0x00;
            SendFun[25] = 0x01;
            SendFun[26] = 0x23;
            SendFun[27] = 0x01;
            SendFun[28] = 0x01;
            SendFun[29] = 0x00;
            SendFun[30] = 0x01;
            SendFun[31] = DataValue;
            SendFun[32] = Address1;
            SendFun[33] = Address2;
            SendFun[34] = Address3;
            return SendFun;
        }
        /// <summary>
        /// RST Bit
        /// </summary>
        /// <param name="DA1"></param>
        /// <param name="SA1"></param>
        /// <param name="DataValue"></param>
        /// <param name="Address1"></param>
        /// <param name="Address2"></param>
        /// <param name="Address3"></param>
        /// <returns></returns>
        private Byte[] RstPLCBitvalue(Byte DA1, Byte SA1, Byte DataValue, Byte Address1, Byte Address2, Byte Address3)
        {
            Byte[] SendFun = new Byte[35];
            SendFun[0] = 0x46;
            SendFun[1] = 0x49;
            SendFun[2] = 0x4E;
            SendFun[3] = 0x53;
            SendFun[4] = 0x00;
            SendFun[5] = 0x00;
            SendFun[6] = 0x00;
            SendFun[7] = 0x1B;
            SendFun[8] = 0x00;
            SendFun[9] = 0x00;
            SendFun[10] = 0x00;
            SendFun[11] = 0x02;
            SendFun[12] = 0x00;
            SendFun[13] = 0x00;
            SendFun[14] = 0x00;
            SendFun[15] = 0x00;
            SendFun[16] = 0x80;
            SendFun[17] = 0x00;
            SendFun[18] = 0x02;
            SendFun[19] = 0x00;
            SendFun[20] = DA1;
            SendFun[21] = 0x00;
            SendFun[22] = 0x00;
            SendFun[23] = SA1;
            SendFun[24] = 0x00;
            SendFun[25] = 0x01;
            SendFun[26] = 0x23;
            SendFun[27] = 0x01;
            SendFun[28] = 0x01;
            SendFun[29] = 0x00;
            SendFun[30] = 0x00;
            SendFun[31] = DataValue;
            SendFun[32] = Address1;
            SendFun[33] = Address2;
            SendFun[34] = Address3;
            return SendFun;
        }
        #endregion
    }
}

至此,我們就可以用上述的類和歐姆龍PLC進行FINS通訊。

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • C#?執(zhí)行Javascript腳本的方法步驟

    C#?執(zhí)行Javascript腳本的方法步驟

    本文主要介紹了C#?執(zhí)行Javascript腳本的方法步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C# 觀察者模式實例介紹

    C# 觀察者模式實例介紹

    一個簡單的例子,比如說貓叫,老鼠跑,主人被驚醒,在不知道觀察者模式之前,我們的代碼可能是這樣的
    2012-10-10
  • FileStream常用的屬性與方法總結

    FileStream常用的屬性與方法總結

    本篇文章主要是對FileStream常用的屬性與方法進行了詳細的總結介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-01-01
  • C#中Lambda表達式的用法

    C#中Lambda表達式的用法

    這篇文章介紹了C#中Lambda表達式的用法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • 基于WPF實現(xiàn)裁剪圖像功能

    基于WPF實現(xiàn)裁剪圖像功能

    這篇文章主要為大家詳細介紹了如何基于WPF實現(xiàn)裁剪圖像功能,文中的示例代碼講解詳細,對我們學習或工作有一定幫助,感興趣的小伙伴可以了解一下
    2023-06-06
  • C#之Windows自帶打印功能的實現(xiàn)

    C#之Windows自帶打印功能的實現(xiàn)

    這篇文章主要介紹了C#之Windows自帶打印功能的實現(xiàn)方式,具有很好的價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • C#串口編程實例代碼

    C#串口編程實例代碼

    這篇文章主要為大家詳細介紹了C#串口編程實例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • C#實現(xiàn)手機拍照并且保存水印照片

    C#實現(xiàn)手機拍照并且保存水印照片

    這篇文章主要介紹了C#實現(xiàn)手機拍照并且保存水印照片的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-11-11
  • C#排序算法的比較分析

    C#排序算法的比較分析

    這篇文章主要介紹了C#排序算法的比較,實例分析幾種比較常見的算法,并對其時間復雜度與穩(wěn)定性進行了詳細的分析,需要的朋友可以參考下
    2014-11-11
  • C#中Timer實現(xiàn)Tick使用精度的問題

    C#中Timer實現(xiàn)Tick使用精度的問題

    這篇文章主要介紹了C#中Timer實現(xiàn)Tick使用精度的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評論