开源纯C#工控网关+组态软件(四)上下位机通讯原理
一、???網(wǎng)關(guān)的功能:承上啟下
最近有點忙,更新慢了。感謝園友們給予的支持,現(xiàn)在github上已經(jīng)有。目標(biāo)是最好的開源組態(tài),看來又近一步^^
之前有提到網(wǎng)關(guān)是物聯(lián)網(wǎng)的關(guān)鍵環(huán)節(jié),它的作用就是承上啟下。
下位機有下位機的語言,上位機有上位機的思路。網(wǎng)關(guān)就是一個翻譯,把下位機的語言轉(zhuǎn)成通用語,再告訴上位機該怎么做。
這個翻譯的過程,應(yīng)該保證:
實時性。如果太慢,上下位機明顯不合拍,就會出問題。
精確性。信號不能頻繁丟失、丟步、跳步;不能有太大誤差;也不會帶入太多干擾和噪音。
穩(wěn)定性。如發(fā)生故障,如通訊斷開,要能自動重連;要足夠強壯,不會動輒崩潰;發(fā)生意外崩潰,要能自動重啟;要有容錯機制和錯誤日志;等等。
要實現(xiàn)這些指標(biāo),還是很有挑戰(zhàn)的。
二、???通訊藍(lán)本:OPC規(guī)范
網(wǎng)關(guān)看上去是個很玄奧的東西,網(wǎng)上找來很多相關(guān)資料,發(fā)現(xiàn)都集中到一個點:OPC規(guī)范。
OPC就如一盞明燈,照亮了前進(jìn)的方向。OPC規(guī)范是大家手筆,集中了業(yè)界專家的智慧,站在巨人的肩膀上,可以少走很多彎路。
OPC規(guī)范定義了數(shù)據(jù)的采集、歸檔、報警以及完整的接口示范。
OPC數(shù)據(jù)采集規(guī)范,包含了這樣一些重要概念:
數(shù)據(jù)讀寫方式包括下位機批量推送數(shù)據(jù)、上位機單獨讀寫數(shù)據(jù)。
可以異步讀寫,也可以同步讀寫。
數(shù)據(jù)包括元數(shù)據(jù)(數(shù)據(jù)的屬性,如數(shù)據(jù)類型、長度、名稱等)和過程數(shù)據(jù)(ID、數(shù)值、時間戳、質(zhì)量戳)。
包含驅(qū)動(Driver)-組(Group)-變量(Item)的三級架構(gòu)。可以對變量按需分組,有的組只讀,有的組需要1秒刷新一次,有的只要5分鐘就可以,要加以區(qū)別以提高效率。
要能判斷驅(qū)動程序是否斷線、要提供斷線或關(guān)閉的事件供應(yīng)用程序處理。
??
總之,信息量很豐富,啟發(fā)很大,我的網(wǎng)關(guān)程序很大程度上都參考了OPC規(guī)范。
但是OPC有它固有的缺陷:依賴微軟的COM組件技術(shù),不能跨平臺,同時COM是一種過時的技術(shù),在不同主機上通訊的配置十分繁瑣且不安全。
因此,我改造實現(xiàn)了自己的類OPC服務(wù)器。基本通訊過程是:批量輪詢下位機-與上個周期的數(shù)據(jù)比對-提取變化的數(shù)據(jù)-批量推送給上位機。
三、???與下位機通訊:批量輪詢
-
下位機的特點-為什么要采用輪詢
下位機通訊的特點:
下位機很多采用主-從模式。即主機發(fā)送的信息可以傳送到各個從機或指定的從機,而各個從機的信息只能發(fā)送給主機。主機采用查詢方式接收發(fā)送數(shù)據(jù),從機采用中斷方式接收發(fā)送數(shù)據(jù)。這種模式天然適合輪詢。
下位機多數(shù)只有一個通信口,有些還是串口,天然不適合推送模式。
下位機很多是單片機,訂閱-發(fā)布模式往往邏輯較為復(fù)雜,程序編寫難度大,對芯片及存儲要求也必然提高。因此采用這種模式的下位機目前極少。
輪詢就是網(wǎng)關(guān)作為主機,定期請求下位機的數(shù)據(jù)。如果實現(xiàn)批量請求,減少往返,輪詢的效率并不低。幾千個變量輪詢周期500毫秒(西門子),無壓力。
輪詢是以組(Group)為單位的。Group都繼承自IGroup?接口:??
public interface IGroup : IDisposable { ? ? ?bool IsActive { get; set; } ?
?
? ?short ID { get; } ? ?
? ?
? ?int UpdateRate { get; set; } ? ?
? ?
? ?float DeadBand { get; set; } ?
? ? ?
? ?string Name { get; set; }IDriver Parent { get; }IDataServer Server { get; }IEnumerable<ITag> Items { get; } ?
? ?bool AddItems(IList<TagMetaData> items); ? ?
? ?
? ?bool AddTags(IEnumerable<ITag> tags); ? ?
? ?
? ?bool RemoveItems(params ITag[] items); ? ? ?
? ?
? ?bool SetActiveState(bool active, params short[] items);ITag FindItemByAddress(DeviceAddress addr);HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray); ? ? ? ?int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true);ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache);ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache);ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache);ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache);ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache);ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache); ? ? ? ?int WriteInt32(DeviceAddress address, int value); ? ? ? ?int WriteInt16(DeviceAddress address, short value); ? ? ? ?int WriteFloat(DeviceAddress address, float value); ? ? ? ?int WriteString(DeviceAddress address, string value); ? ? ? ?int WriteBit(DeviceAddress address, bool value); ? ? ? ?int WriteBits(DeviceAddress address, byte value); ? ? ? ?event DataChangeEventHandler DataChange; }
?其中,UpdateRate就是輪詢周期。DeadBand是死區(qū)。死區(qū)代表敏感度,設(shè)的小敏感度高,但也帶來更多的噪聲。
每個Group的變量可支持單獨讀寫(如各ReadXXX,WriteXXX方法),也支持批量推送(DataChange事件)。對下位機的輪詢,都是以組為單位,每個組在激活狀態(tài)下按照自己的輪詢周期,采集、推送數(shù)據(jù),互不干擾。
每個Group包含特性相似的一組變量:有相同的輪詢周期、激活屬性(需要輪詢或無需輪詢)、讀寫屬性(均為只讀、讀/寫或只寫),需要的話可以同時使能或同時屏蔽。
因為部分變量無需隨時監(jiān)控,可以將其劃入一組,不刷新(輪詢);有些變量變化很快,需要高頻掃描;有些變化很慢也不需要時時查看,可以幾分鐘輪詢一次。將變量有效分組可以提高對重點監(jiān)控變量的讀寫效率,減少對下位機資源的占用。
網(wǎng)關(guān)如果有多個客戶端相連,各自需要的數(shù)據(jù)又不盡相同,由網(wǎng)關(guān)統(tǒng)一定期輪詢,再批量推送給客戶端是很高效的。
就比如開超市,南來北往的客人(客戶端)需求各異,但超市(網(wǎng)關(guān))來統(tǒng)一采購(輪詢),不用客戶跟各個批發(fā)市場(下位機)直接訂貨,集中來我這購物(訂閱Tag)就行。
-
未來的擴展
雖然當(dāng)前主流PLC不都支持訂閱-推送模式,但這是歷史潮流。AB Controllogix、新的西門子S71200-1500,都支持標(biāo)簽地址,也就是直接推送變化的標(biāo)簽(Tag)數(shù)據(jù)。
未來考慮制定一個新的接口支持這一模式。
四、???與上位機通訊:訂閱-推送
-
?上位機的特點-為什么要采用訂閱-推送
上位機通信的特點主要為:
要及時、準(zhǔn)確了解下位機的消息。無論是監(jiān)控畫面、還是報警、提示人工操作這些,越實時越好,越準(zhǔn)確越好。如果采用請求-響應(yīng)模式,請求的周期決定實時性不會太好。請求頻繁網(wǎng)關(guān)壓力大,反之實時性差。
一個網(wǎng)關(guān)可能要拖好幾個上位機,工段多的,可能要開十幾個顯示屏同時監(jiān)控。因此,每個上位機都向網(wǎng)關(guān)請求數(shù)據(jù),流量會陡然上升,網(wǎng)絡(luò)會阻塞,網(wǎng)關(guān)壓力會很大。
大部分上位機需要的數(shù)據(jù)不會經(jīng)常變化,尤其是一些開關(guān)量。如果每次反復(fù)請求,浪費資源,浪費時間。
如果采用對各變量單獨請求數(shù)據(jù),勢必造成大量不同時段請求-響應(yīng)過程交錯發(fā)生,難以整合,也難以批量讀寫,效率極低。
因此,向訂閱客戶只推送變化的數(shù)據(jù),無疑是一種高效的辦法。只要數(shù)據(jù)發(fā)生變化,馬上推送給客戶端,既保證了實時性,又保證了推送數(shù)據(jù)的最小化。
就像打報修電話,送修單位(下位機)不一定時時刻刻有人上門;電話打過去,總臺(網(wǎng)關(guān))記下號碼,有人上門(下位機有數(shù)據(jù)變化)了通知(推送)您。沒人你也不用幾秒鐘打一個催(輪詢),你煩大家煩。
-
如何實現(xiàn)訂閱-推送
推送是只推送變化的變量。要知道哪些變量變化了,必須要緩存上一次變量的數(shù)據(jù)加以比對。因此,需要緩存每次輪詢的數(shù)據(jù)。
網(wǎng)關(guān)在對下位機的輪詢過程中,將訂閱的變量都掃描了一遍;這些數(shù)據(jù)都存在內(nèi)存的變量緩存:ICache:
public interface ICache : IReaderWriter { ? ? ?? int Size { get; set; } ? ? ?
? ? ? ? ?
? int ByteCount { get; }Array Cache { get; } ? ? ?
? ? ? ? ?
? int GetOffset(DeviceAddress start, DeviceAddress end); }
?首先,ICache?繼承了IReaderWriter,也就是緩存類也具有可讀寫性,以方便比對。同時還有一個Cache屬性,這就是內(nèi)存區(qū)域:映射、儲存下位機的變量。
因為下位機可能有很多個,存儲地址也是不連續(xù)的;但通過對下位機地址DeviceAddress的排序,最終下位機地址映射到一塊連續(xù)的內(nèi)存地址,通過DeviceAddress的CacheIndex(緩存索引)相關(guān)聯(lián)。
每次輪詢,即調(diào)用IReaderWriter的ReadBytes方法依次讀入下位機變量區(qū)域;讀入的值與Cache中緩存的數(shù)據(jù)比對; 所有變化的部分加入一個ChangedList表,存儲變化的CacheIndex。
這些功能在PLCGroup定時器內(nèi)的Poll函數(shù)實現(xiàn)。? ? ??
protected void timer_Timer(object sender, EventArgs e) { ? ? ? ?? ?if (_isActive){ ?
? ??lock (sync){ ? ? ? ? ? ?
? ?? ? ?Poll(); ? ? ? ? ? ?
? ?? ? ?if (_changedList.Count > 0)Update();}}
? ? else
? ? return;}
比較之后,如發(fā)現(xiàn)ChangedList的數(shù)量大于0,說明有變量數(shù)值更新,執(zhí)行Update方法,根據(jù)CacheIndex找到所有變化的變量,通過IGroup?接口的DataChange事件打包推送出去。
具體的訂閱-推送過程,是利用套接字(Socket)在DAService類實現(xiàn)的。套接字顧名思義,就是一條電話線:各客戶端向網(wǎng)關(guān)服務(wù)器發(fā)送請求,建立一個長連接:只要客戶不掛電話,就一直連著。客戶始終監(jiān)聽電話;只要服務(wù)器數(shù)據(jù)有變化,立馬有話務(wù)員及時告知,客戶響應(yīng)。在這里我實現(xiàn)了一個自定義的TLV(Type-Length-Value)協(xié)議,將變化的數(shù)據(jù)打包發(fā)送,客戶端拆包,分解出變量的ID、實時值、時間戳等信息,并轉(zhuǎn)換為圖元的動畫,后文再詳細(xì)闡述。
-
上下位機通訊流程圖:
?五、???下面的計劃
?
寫一系列帖子,把架構(gòu)、原理講清楚。大致如下:
-
網(wǎng)關(guān)層接口概述
-
上下位機通訊原理
-
如何實現(xiàn)一個設(shè)備驅(qū)動
-
如何設(shè)計圖元
-
VS插件模塊及原理
-
歸檔模塊及文件格式
-
如何進(jìn)行功能擴展
-
組態(tài)變量表達(dá)式實現(xiàn)
github地址:https://github.com/GavinYellow/SharpSCADA。QQ群:102486275
相關(guān)文章:?
-
.NET十年回顧
-
開源純C#工控網(wǎng)關(guān)+組態(tài)軟件
-
開源純C#工控網(wǎng)關(guān)+組態(tài)軟件(三)加入一個新驅(qū)動:西門子S7
原文地址:http://www.cnblogs.com/evilcat/p/7743970.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的开源纯C#工控网关+组态软件(四)上下位机通讯原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core 2.0 + E
- 下一篇: 浅析C#中单点登录的原理和使用