手把手教你学习IEC104协议和编程实现 (可删除,见文章 一 起章)

本章可以不看了,已经对格式内容进行了修正和优化.见文章手把手教你学习IEC104协议和编程实现 一 起章-CSDN博客

从今天开始,我准备将我编程实现IEC104协议软件的思路给各位分享下,里面会穿插IEC104协议的具体分析,对于学习IEC104协议和编程实现都有一定的意义,也算科普一下吧.

本章仅对网络部分做一个讲解.

众所周知,我们的IEC104是国家电网公司经常使用的一个协议,在电力系统工作的人,通讯这一块有接触的话,都要对这个协议有一个深入的理解.

IEC104协议有三个版本,分别是1997版,2002版,2009版,其中我们使用最多的可能是200版.

各版本在规约处理流程上没有什么变化,不同之处在于:

(1)2002版在1997版的基础上,扩展了遥测、遥信、遥控等信息体基体址。

(2)2009版在2002版的基础上,增加了协议的传输序列和互操作性的改进,以及对冗余连接处理方面的新功能。

具体的内容我们后面会讲到.

我们首先要知道的是IEC104协议传输的介质是网络.所以她是通过socket方式进行传输的,这个就涉及到网络ip和网络端口,这2个参数才能构成一个网络接口参数,缺一不可.

所以今天这一章我们先学习实现网络部分,仅和编程有关系,具体IE104的具体内容见下一章,毕竟我们要循序渐进的,嘿嘿.

clientsocket类

我们先建立一个类,名称为clientsocket,代表客户端的模式下的socket类,毕竟,万一我们以后使用server模式呢.

这个类主要的函数为connect,close,receivestr,sendstr,分别用于链接,断开(关闭),接收数据,发送数据函数.

函数1:connect

/// <summary>
        /// connect  网络连接
        /// </summary>
        /// <param name="ipadd"></param>
        /// <param name="port"></param>
        #region 连接ip,端口号
        public bool Connect(string ipadd, int port)
        {
            byte[] data = new byte[4096];
            newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                   
            IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ipadd), port);
            try
            {
                newclient.Connect(ie);                
                Connected = true;
                
                ThreadStart myThreaddelegate = new ThreadStart(Receivestr);
                myThread = new Thread(myThreaddelegate);
                myThread.Start();                
            }
            catch (Exception ex)
            {
                string str = "对象名称: " + ex.Source + "\n" +
               "函数: " + ex.TargetSite + "\n" +
               "故障点: " + ex.StackTrace.Substring(ex.StackTrace.IndexOf("\\")) + "\n" +
               "错误提示 : " + ex.Message + "\r\n";
                writeLogFile(str);
                Connected = false;
                if(myThread!=null)
                    myThread.Join(0);
                
            }
            
            return Connected;

        }
        #endregion

我们需要先定义变量:

        public Socket newclient;
        public bool Connected;
        public Thread myThread;

我了能够正常使用,我们还需要先包含相关的工作空间

using System.Net;
using System.Net.Sockets;
using System.Threading;

对于catch()部分,这样我们可以在发生异常的时候,将异常文件的函数和行数等信息输出出来,更加精确的分析故障发生的原因.如果不是很理解,这一部分可以删除掉,直接catch()也OK.

这个函数的功能就是使用线程的方式对定义好的网络参数连接成功,开始准备发送和接收数据.

函数2: close

        /// <summary>
        /// close  关闭网络
        /// </summary>
        #region 关闭
        public void close()
        {
            if (Connected)
            {
                newclient.Close();
            }
        }
        #endregion

用于程序结束的时候,需要先关闭网络.

函数3: receivestr

#endregion

        /// <summary>
        /// receivestr   接收字符串
        /// </summary>
        #region 接收数据
        public void Receivestr()
        {
            receive_str = "";
            stringdata = "";
            int recv=0;
            //修正当手动断开连接时,cpu占用过高的问题,原来是while(true)
            while (Connected) 
            {
                Console.WriteLine("receivestr is runing \r\n");
                if (!Connected) return;
                
                try
                {
                    recv = newclient.Receive(buffer);
                    
                    for (int i = 0; i < recv; i++)
                    {
                        data.Add(buffer[i]);
                        
                        receive_str += data[i].ToString("X2");
                        
                    }
                    
                    stringdata = Encoding.UTF8.GetString(buffer, 0, recv);
                }
                catch 
                {
                    Connected = false;  
                     //return;
                }
            }
        }
        #endregion

这个函数需要先定义几个变量

        public string send_str,receive_str;//发送字符串,接收字符串
        public string stringdata="";
        public byte[] buffer = new byte[4096];  //数据存放的缓冲区
        public List<byte> data = new List<byte>();//链表,用于循环组合报文.

函数4:sendstr

/// <summary>
        /// sendstr  发送字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="hexflag"></param>
        #region 发送数据
        public void sendstr(string str,int hexflag)
        {
            if (!Connected) return;
            //str = str.Replace(" ", "");
            int m_length = str.Length/2;
            
            byte[] data = new byte[m_length];
            if (hexflag == 0)
            {
                data = Encoding.UTF8.GetBytes(str);
            }
            else
            {
                //在这里我们引入了一个新的函数,作用是将字符串转换成16进制的数组,函数实现在后面
                int len = strtohexarray(str, data);
            }
            //data[4] = Convert.ToByte(sendindex);
            try
            {
                sendlen = newclient.Send(data);
                
            }
            catch 
            {
                //Console.Write(ex.Message);
                Connected = false;
                return;
            }
            
        }
        #endregion

附加函数strtohexarray

/// <summary>
        /// strtohexarray  将字符串转换成byte[]
        /// </summary>
        /// <param name="str"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        #region
        public int strtohexarray(string str,byte[] b)
        {
            int i = 0;
            try
            {
                str = str.Replace(" ", "");
                str = str.Replace("-", "");
                str = str.Replace(">", "");
                if (str.Length % 2 != 0)
                    str = str.Substring(0, str.Length - 1);
                for (i = 0; i < str.Length / 2; i++)
                {
                    b[i] = System.Convert.ToByte(str.Substring(i * 2, 2), 16);
                }
                return i;
            }
            catch
            {
                return i;
            }
        }
        #endregion

这个函数的作用就是将我们的组织好的报文发送出去,具体我们使用的,将报文组织好,直接调用这个函数就OK.其中,可以选择是按照16进制的模式,还是使用字符模式,这2中模式下的发送内容处理方式是不一样.有兴趣的朋友可以深入研究下.

另外为 更好的调试或者运行log,我们设计了辅助函数writelogfile,将收到的报文以数组或者字符串的格式保存到文件中.

函数5:writelog

函数用途将报文写入到log文件,由于我们在处理的过程可能会涉及到不同的模式,所以我们使用2个函数writeLogFile(string str),writeLogFile(byte[] bb,int len)来实现.具体如下:

#region 写log文件
        //将字符串类型的数据存储到log文件中
        public int writeLogFile(string str)
        {            
            if(str=="") return 1;
            string filePath = Form1.form.datadir + @"run.log";//这里文件的路径是当前执行程序下的run.log文件
            //当文件存在,并且大小大于20M的时候,删除文件,这样是为了保证存储log的速度
            if (File.Exists(filePath))
            {
                FileInfo fileInfo = new FileInfo(filePath);
                if (fileInfo.Length > 2000000)
                    File.Delete(filePath);
            }

            try
            {
                FileStream aFile = new FileStream(filePath, FileMode.OpenOrCreate | FileMode.Append);
                StreamWriter sw = new StreamWriter(aFile);
                DateTime tt = DateTime.Now;
                str = "[" + tt.ToString() + "]  " + str;
                sw.WriteLine(str);
                sw.Close();
                aFile.Close();
            }
            catch { }
            return 1;
        }
        #endregion

        #region 写log文件
        //将数组数据保存到log文件中,使用len的目的是我们可以选择从开始的一部分数据来储存
        public int writeLogFile(byte[] bb,int len)
        {
            if (len > bb.Length) len = bb.Length;
            string str = "";
            for (int i = 0; i < len; i++)
            {
                str += bb[i].ToString("X2");
                str += " ";
            }
            string filePath = Form1.form.datadir + @"run.log";
            if (File.Exists(filePath))
            {
                FileInfo fileInfo = new FileInfo(filePath);
                if (fileInfo.Length > 2000000)
                    File.Delete(filePath);
            }
            try
            {
                FileStream aFile = new FileStream(filePath, FileMode.OpenOrCreate | FileMode.Append);
                StreamWriter sw = new StreamWriter(aFile);
                DateTime tt = DateTime.Now;
                str = "[" + tt.ToString() + "]   " + str+"\r\n";
                sw.WriteLine(str);
                sw.Close();
                aFile.Close();
                return 1;
            }
            catch
            {
                return 1;
            }            
        }
        #endregion

这样我们的网络类就完成了,在真正使用这个类的时候,可以直接调用这个类就OK了.

下一章我们就直接讲解IEC104的规约结构了,最好能提前看看,呵呵.

好了就写到这里.

创作不易,点赞不止,您的关注与点赞是我创作的最大动力!

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
学习IEC 104协议编程实现九号遥控功能是一项复杂的任务。本文将以300字的篇幅简要概述该过程。 首先,IEC 104协议是电力自动化系统中常用的通信协议之一,用于远程监控和控制通信。要学习IEC 104协议,首先需要了解其基本原理和通信过程。可以通过阅读相关技术文档、书籍和在线程来深入了解IEC 104协议的细节。 接下来,学习IEC 104协议编程实现需要有一定的编程基础。主要编程语言可以选择C、C++或者其他常用的编程语言。编程实现九号遥控功能需要掌握IEC 104协议的编码和解码过程,了解相应的数据结构和流程控制。 在编程实现九号遥控功能时,首先需要建立与远程终端设备的连接。可以利用TCP/IP协议进行通信,建立socket连接。在连接建立后,必须正确配置IEC 104协议的站地址、传输参数等信息。 然后,需要编写代码实现IEC 104协议的编码和解码过程。首先,将要发送的遥控命令封装成IEC 104规定的格式,即APCI(Application Protocol Control Information)+ASDU(Application Service Data Unit)+ASDU信息组。然后,根据协议规定的解码方式,对接收到的遥控命令进行解码。 最后,根据九号遥控功能的具体需求,编写代码处理相应的逻辑。例如,判断遥控命令的类型,执行相应的操作,并及时响应给远程终端设备。 总结而言,学习IEC 104协议编程实现九号遥控功能是一项需要专业知识和编程技能的任务。需要深入学习和实践才能掌握。希望以上简要概述可以为您提供一些指导。如需深入了解,请参考相关专业文献和指导资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

six2me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值