1. ServerSuperIO(SSIO)說明
SSIO是基於早期工業現場300波特率通訊傳輸應用場景發展、演化而來。為了適應互聯網、物聯網的發展趨勢,以及不同應用場景的需求,SSIO也在不斷的進行更新。
SSIO是一個跨平台的物聯網通訊框架,但是其本質不僅僅是通訊框架,而是設備驅動、串口和網絡IO管理器、場景控制器三者之間的協調與調度機制。
物聯網是一個發展趨勢,如果各種傳感器、硬件設備的協議驅動無法統一,那麼使用SSIO框架來開發設備驅動,隨意掛載到框架運行並且進行通訊和交互,你擁有的是集成能力,以及對用戶的承諾;如果是公司內部使用,協議是“標准”的,那麼使用SSIO更簡單、方便,降低人員成本、提高生產效率。
2. 升級概要
1.在串口和網絡IO中增加接收數據緩存功能,充分利用緩存空間,保證數據的完整性。
2.修改數據分發策略,現在以遠程IP或設備編號兩種方式分發數據。
3.增加接收數據過濾器,保證數據按一定的規則進行提取,以及保證數據的連續性。
4.增加定時、超時清理網絡連接資源,如果網絡連接在一定時間范圍內沒有接到數據,則進行清理。
5.其他代碼優化。
1.修復:退出軟件可能造成的異常。
2.修復:分發數據的邏輯問題。
1.在協議驅動IProtocolDriver增加GetCode和GetPackageLength接口。GetCode一般為設備的唯一編號;GetPackageLength一般獲得本次數據包應該接收的數據長度,在框架中並未實際使用。
2.把CommandCache命令空間改為DataCache,下面增加ISendCache發送緩存接口和IReceiveCache接收緩存接口。
3.把現在設備驅動中的CommandCache改為SendCache發送數據緩存,移植到ProtocolDriver協議驅動中。
4.在ComSession和TcpSocketSession中增加ReceiveCache接收數據緩存。
5.增加IReceiveFilter接收數據過濾器接口。
7.去掉按設備地址分發數據,增加按設備編碼分發數據。
8.網絡偵聽連接的時候,退出軟件有可能造成異常。
9.網絡異步接收數據時,在邏輯上有可能造成分發錯誤。
10.對配置文件進行修改,增加StartReceiveDataFliter、ClearSocketSession、ClearSocketSessionInterval和ClearSocketSessionTimeOut。
11.網絡通訊時,去掉多少次沒有接收到數據進行清理連接的功能。
3. 升級考慮
SSIO原來是用設備地址(DeviceAddress)來識別設備驅動,而DeviceAddress是int類型,不能滿足業務場景的需求了,因為設備編碼不僅僅是一個數字類型的數值,有可能是一串字符串,包括數字和字母。設備編碼包括設備地址,是識別設備的一個規則編碼。有時候設備編碼與設備地址是等同的。
所以在設備驅動中增加了GetCode接口,這個接口也作為網絡異步接收數據使用過濾器查找設備的接口,一般需要在配置文件設備StartReceiveDataFliter屬性。
GetCode可以進行模糊查找,並且返回設備編碼,如下代碼:
public override string GetCode(byte[] data) { int codeIndex = -1; byte[] head=new byte[] {0x55,0xaa}; for (int i = 0; i < data.Length; i++) { if (data.Mark(0, data.Length, i, head)) { codeIndex = i; break; } } if (codeIndex == -1) { return String.Empty; } else { return data[codeIndex + head.Length].ToString("00#"); } }
3.2 接收數據緩存
SSIO以前接收完數據,直接從緩存中提取數據後返回給了設備驅動;也有另外一種方案,就是創建一個更大的緩存對象保存byte數據,但是這種方案有些浪費內存空間,以及效率。
SSIO現在采用了折中方案,利用現有的緩存空間(byte[]),配合接收數據過濾器,對已經接收到的數據進行管理和過濾提取,並且對未提取的數據有持久存儲的能力。代碼如下:
/// <summary> /// 獲得數據 /// </summary> /// <param name="filter"></param> public IList<byte[]> Get(IReceiveFilter filter) { if (filter == null) { throw new NullReferenceException("filter引用為空"); } if (DataLength <= 0) { return new List<byte[]>(); } lock (_SyncLock) { int lastByteOffset = InitOffset; IList<byte[]> listBytes = filter.Filter(ReceiveBuffer, InitOffset, DataLength, ref lastByteOffset); if (listBytes != null && listBytes.Count > 0 && lastByteOffset>InitOffset) { CurrentOffset = lastByteOffset + 1; int gets = CurrentOffset - InitOffset ; DataLength -= gets; MoveLeft(gets); } return listBytes; } }
接收數據過濾器是按一定的原則在數據緩存中查找、提取數據信息,過濾器接口定義如下代碼:
/// <summary> /// 過濾數據信息 /// </summary> /// <param name="receiveBuffer">緩沖區</param> /// <param name="offset">偏移量</param> /// <param name="length">有效數據長度</param> /// <param name="lastByteOffset">最後一個字節的偏移量</param> /// <returns>沒有數據返回null</returns> IList<byte[]> Filter(byte[] receiveBuffer, int offset, int length, ref int lastByteOffset);
SSIO在此接口的基礎上,實現了5種數據過濾方式,固定結尾的方式(FixedEndReceiveFliter)、固定開頭和結尾的方式(FixedHeadAndEndReceiveFliter)、因定開頭的方式(FixedHeadReceiveFliter)、固定開頭和長度的方式(FixedHeadAndLengthReceiveFliter)、因定長度的方式(FixedLengthReceiveFliter),這幾種方式各有利弊,請根據不同設備的協議使用不同的過濾器。
SSIO以前是設置一個發送和接收次數值,如果超過這個值,還沒有接收到數據信息,那麼就認為是失效的IO通道,就會關閉、釋放掉資源。
SSIO現在增加了定時檢測功能,如果在一定時間范圍內(可設備)還沒有接收到數據,那麼就認為是失效的IO通道,就會關閉、釋放掉資源。代碼如下:
private void ClearSocketSession(object state) { if (Monitor.TryEnter(state)) { try { ICollection<IChannel> socketChannels = this.ChannelManager.GetChannels(CommunicateType.NET); if (socketChannels == null || socketChannels.Count<=0) return; DateTime now = DateTime.Now; IEnumerable<IChannel> timeoutSessions = socketChannels.Where(c => (now-((ISocketSession)c).LastActiveTime).Seconds>Config.ClearSocketSessionTimeOut); System.Threading.Tasks.Parallel.ForEach(timeoutSessions, c => { ISocketSession s = ((ISocketSession) c); Logger.Info(true,String.Format("網絡連接超時:{0}, 開始時間: {1}, 最後激活時間:{2}!", now.Subtract(s.LastActiveTime).TotalSeconds, s.StartTime, s.LastActiveTime)); RemoveTcpSocketSession(s); }); } catch (Exception ex) { this.Logger.Error(true,ex.Message); } finally { Monitor.Exit(state); } } }
對於輪詢模式通訊,不存在數據分發,因為每次高度設備驅動,都是順序執行發送和接收操作,接收的數據肯定是這個設備驅動的,就會立即返回。
但是對於並發模式、自主模式、單例模式的通訊方式(應用場景),是異步接收數據信息,我怎麼知道接收過來的數據應該分配哪個設備驅動呢?有兩種方式:按IP和按設備編碼(原來是按數字類型的設備地址)。接收到的數據,會通過設備協議驅動與設備參數進行比對,並且進行數據分發。如下代碼:
/// <summary> /// 分發數據模式 /// </summary> public enum DeliveryMode { [EnumDescription("設備IP分發數據")] DeviceIP, [EnumDescription("設備編碼分發數據")] DeviceCode }