『7x24小时有问必答』
这篇文章适合对IEC104协议有所了解的开发者。文中概念性的介绍比较少,以干货为主;对IEC104协议了解较少,可以先参考这篇文章  IEC608705104协议解读(含解析源码)
其实网络通信协议很简单,就是两部分组成(网络通信+协议),IEC104也不例外,简单来说就是IEC104=Socket+协议,搞懂这个,你就能搞懂大部分的网络通信协议。
为了方便理解,这里把协议分成两部分:协议组成和工作流程。
那么,我们只需要搞懂以下三点就能轻松掌握(程序解析在最后)。

1.  Socket

2.  协议组成
3. 流程

Socket
网络通信底层采用TCP/IP通信方式,不管你用什么编程语言,对于网络通信编程,Socket是绕不开的。
【IO密集型用异步,计算密集型用并行】

对于Socket通信编程,如果是一对多IO密集型,最好使用异步而不是多线程;如果是一对一,那就无所谓了。
private void ContinueReadBuffer(){    try    {        _stream.BeginRead(            _receiveBuffer,            _receiveBufferOffset,            _receiveBuffer.Length - _receiveBufferOffset,            HandleDataReceived,            _stream);    }    catch (Exception ex)     {        HandleReceiveOperationException(ex);        throw;    }}
源码中采用了异步回调的方式BeginRead和EndRead相结合,异步可以更高效的利用CPU系统资源,软件不卡顿,用户体验也更好。

在一对多的情况下,怎么能最大化利用系统资源做到高并发通信,答案肯定不是多线程,看了源码你就知道了。

协议组成
需要搞懂这几个名词:APCI、ASDU、APDU、U帧、S帧、I帧。
APCI:控制信息部分,类似于帧头,由1个byte 的起始字节0x68 + 1 byte的长度 + 4个byte控制位域(CF)组成。
ASDU:存储数据部分。

APDU:等于APCI+ASDU,长度等于APCI+ASDU-2,减去起始字节和APDU长度字节。

U帧:控制报文帧(只包括APCI部分),主要有启动帧、停止帧、测试帧。

S帧:监视帧只包括APCI部分),接收序号。

I帧:信息传输帧包括APCI+ASDU部分)

发送序号和接收序列号是保证数据完整性的条件。
类型标识定义发送数据的格式。
遥测常用类型标识
09—带品质描述的测量值,每个遥测值占3个字节
0d—带品质描述的浮点值,每个遥测值占5个字节
遥信常用类型标识

01—不带时标的单点遥信,每个遥信占1个字节

可变结构体定义发送数据信息是有序还是无序,有序即一个信息体地址,元素的对应地址会在此信息地址基础上依次加1。无序即1个地址对应一个元素。
传输原因定义记录传送的原因以及上行下行方向等。

更详细的解释请参考  IEC608705104协议解读(含解析源码)

流程
1. 由客户端(主站)向服务端(从站)发起TCP连接。
2. 连接成功后,向服务端发送链路启动帧。
3. 服务端收到启动帧以后,向客户端回应启动确认帧。
4. 客户端收到启动确认帧,向服务端发送总召唤命令帧。
5. 服务端收到总召唤命令数据请求后,发送总召唤命令数据响应帧,然后继续发送总召唤命令数据。总召唤命令数据发送完成后,发送总召唤命令数据结束帧。
6. 客户端在收到总召唤命令数据结束帧后,发送对时请求帧。
7. 服务器收到对时请求帧后,发送对时响应帧。
8. 由服务器主动向客户端发送变化数据帧。同时,收到客户端发送的控制类命令,回复相应的操作结果。
9. 客户端等到下一个数据总召唤命令数据周期,重复第5步之后的流程。
源码中是针对逆变器进行数据采集,每5分钟发送一次总召,没有涉及对时操作;总召时间间隔可根据需要调整。

程序解析
采用的C#编程语言,Windows Form窗体程序
启动后的窗体

Visual studio开发工具,Common文件夹存放公用类,IEC104文件夹存放104协议逻辑实体,Tools为工具,ZZH是Socket相关类,Form1是主窗体。
主程序流程图

loopTimer定时任务

controlTimer定时任务

void HandleData(int typeId, InformationObject[] infoObjs){    StringBuilder sb = new StringBuilder();    for (int i = 0; i < infoObjs.Length; i++)    {        //浮点遥测值        if (typeId == 13)        {            int firstAddress = infoObjs.GetInformationObjectAddress();  // 信息地址起始位            int len = infoObjs.GetInformationElements().Length / 2;            for (int j = 0; j < len; j++)            {                int address = firstAddress + j;                if (address < 16385 || address > 17000)                {                    Console.WriteLine("遥测信息地址超出定义范围");                }                var value = infoObjs.GetInformationElements()[j, 0];

                var kv = "k:" + address + ",v:" + value + "\r\n";                sb.Append(kv);                Config104.InsertHash(address, ((EShortFloat)value).GetValue());

            }        }        //单点遥信        else if (typeId == 1)        {            int firstAddress = infoObjs.GetInformationObjectAddress();  // 信息地址起始位            int len = infoObjs.GetInformationElements().Length;   // 所有信息值长度            for (int j = 0; j < len; j++)            {                int address = firstAddress + j;                if (address > 500 || address < 0)                {                    Console.WriteLine("遥信信息超出定义范围:" + address);                }                var value = infoObjs.GetInformationElements()[j, 0];

                var kv = "k:" + address + ",v:" + value + "\r\n";                sb.Append(kv);                Config104.InsertHash(address, ((ESinglePointWithQuality)value).IsOn());            }        }    }    if(checkBox1.Checked)        WriteLog.WriteLogs(sb.ToString());}
上面代码是数据解析方法,由于没有数据库操作,解析之后的数据存放在Hashtable中,如果想扩展数据库操作,可以在这里修改或直接从Hashtable中获取。

Gitee源码地址:https://gitee.com/zhangzhenhuaO/iec104.git
也可以加作者微信(xiaoyiyz)获取源码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码