1、 網關的功能:承上啓下git
最近有點忙,更新慢了。感謝園友們給予的支持,如今github上已經有。目標是最好的開源組態,看來又近一步^^github
以前有提到網關是物聯網的關鍵環節,它的做用就是承上啓下。數組
下位機有下位機的語言,上位機有上位機的思路。網關就是一個翻譯,把下位機的語言轉成通用語,再告訴上位機該怎麼作。緩存
這個翻譯的過程,應該保證:安全
要實現這些指標,仍是頗有挑戰的。服務器
2、 通信藍本:OPC規範網絡
網關看上去是個很玄奧的東西,網上找來不少相關資料,發現都集中到一個點:OPC規範。架構
OPC就如一盞明燈,照亮了前進的方向。OPC規範是你們手筆,集中了業界專家的智慧,站在巨人的肩膀上,能夠少走不少彎路。異步
OPC規範定義了數據的採集、歸檔、報警以及完整的接口示範。函數
OPC數據採集規範,包含了這樣一些重要概念:
總之,信息量很豐富,啓發很大,個人網關程序很大程度上都參考了OPC規範。
可是OPC有它固有的缺陷:依賴微軟的COM組件技術,不能跨平臺,同時COM是一種過期的技術,在不一樣主機上通信的配置十分繁瑣且不安全。
所以,我改造實現了本身的類OPC服務器。基本通信過程是:批量輪詢下位機-與上個週期的數據比對-提取變化的數據-批量推送給上位機。
3、 與下位機通信:批量輪詢
下位機通信的特色:
輪詢就是網關做爲主機,按期請求下位機的數據。若是實現批量請求,減小往返,輪詢的效率並不低。幾千個變量輪詢週期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是死區。死區表明敏感度,設的小敏感度高,但也帶來更多的噪聲。
每一個Group的變量可支持單獨讀寫(如各ReadXXX,WriteXXX方法),也支持批量推送(DataChange事件)。對下位機的輪詢,都是以組爲單位,每一個組在激活狀態下按照本身的輪詢週期,採集、推送數據,互不干擾。
每一個Group包含特性類似的一組變量:有相同的輪詢週期、激活屬性(須要輪詢或無需輪詢)、讀寫屬性(均爲只讀、讀/寫或只寫),須要的話能夠同時使能或同時屏蔽。
由於部分變量無需隨時監控,能夠將其劃入一組,不刷新(輪詢);有些變量變化很快,須要高頻掃描;有些變化很慢也不須要時時查看,能夠幾分鐘輪詢一次。將變量有效分組能夠提升對重點監控變量的讀寫效率,減小對下位機資源的佔用。
網關若是有多個客戶端相連,各自須要的數據又不盡相同,由網關統必定期輪詢,再批量推送給客戶端是很高效的。
就好比開超市,南來北往的客人(客戶端)需求各異,但超市(網關)來統一採購(輪詢),不用客戶跟各個批發市場(下位機)直接定貨,集中來我這購物(訂閱Tag)就行。
雖然當前主流PLC不都支持訂閱-推送模式,但這是歷史潮流。AB Controllogix、新的西門子S71200-1500,都支持標籤地址,也就是直接推送變化的標籤(Tag)數據。
將來考慮制定一個新的接口支持這一模式。
4、 與上位機通信:訂閱-推送
上位機通訊的特色主要爲:
所以,向訂閱客戶只推送變化的數據,無疑是一種高效的辦法。只要數據發生變化,立刻推送給客戶端,既保證了實時性,又保證了推送數據的最小化。
就像打報修電話,送修單位(下位機)不必定時時刻刻有人上門;電話打過去,總檯(網關)記下號碼,有人上門(下位機有數據變化)了通知(推送)您。沒人你也不用幾秒鐘打一個催(輪詢),你煩你們煩。
推送是隻推送變化的變量。要知道哪些變量變化了,必需要緩存上一次變量的數據加以比對。所以,須要緩存每次輪詢的數據。
網關在對下位機的輪詢過程當中,將訂閱的變量都掃描了一遍;這些數據都存在內存的變量緩存:ICache:
public interface ICache : IReaderWriter { int Size { get; set; } int ByteCount { get; } Array Cache { get; } int GetOffset(DeviceAddress start, DeviceAddress end); }
首先,ICache 繼承了IReaderWriter,也就是緩存類也具備可讀寫性,以方便比對。同時還有一個Cache屬性,這就是內存區域:映射、儲存下位機的變量。
根據下位機的地址最小單元、字節序的不一樣,對應的ICache也相應不一樣。例如ModbusTcp是16位寄存器,網絡字節序,相應的Cache也是16位數組,按照網絡字節序讀寫;AB PLC對應的Cache則是32位數組。
由於下位機可能有不少個,存儲地址也是不連續的;但經過對下位機地址DeviceAddress的排序,最終下位機地址映射到一塊連續的內存地址,經過DeviceAddress的CacheIndex(緩存索引)相關聯。
每次輪詢,即調用IReaderWriter的ReadBytes方法依次讀入下位機變量區域;讀入的值與Cache中緩存的數據比對; 全部變化的部分加入一個ChangedList表,存儲變化的CacheIndex。
這些功能在PLCGroup定時器內的Poll函數實現。
protected void timer_Timer(object sender, EventArgs e) { if (_isActive) { lock (sync) { Poll(); if (_changedList.Count > 0) Update(); } } else return; }
比較以後,如發現ChangedList的數量大於0,說明有變量數值更新,執行Update方法,根據CacheIndex找到全部變化的變量,經過IGroup 接口的DataChange事件打包推送出去。
具體的訂閱-推送過程,是利用套接字(Socket)在DAService類實現的。套接字顧名思義,就是一條電話線:各客戶端向網關服務器發送請求,創建一個長鏈接:只要客戶不掛電話,就一直連着。客戶始終監聽電話;只要服務器數據有變化,立馬有話務員及時告知,客戶響應。在這裏我實現了一個自定義的TLV(Type-Length-Value)協議,將變化的數據打包發送,客戶端拆包,分解出變量的ID、實時值、時間戳等信息,並轉換爲圖元的動畫,後文再詳細闡述。
5、 下面的計劃
寫一系列帖子,把架構、原理講清楚。大體以下:
github地址:https://github.com/GavinYellow/SharpSCADA。QQ羣:102486275