目 錄
6. 并發(fā)通訊模式開發(fā)及注意事項... 2
6.1 概述... 2
6.2 通訊機(jī)制說明... 2
6.3 設(shè)備驅(qū)動開發(fā)注意事項... 3
6.3.1 實時發(fā)送數(shù)據(jù)... 3
6.3.2 優(yōu)先發(fā)送其他數(shù)據(jù)... 3
6.3.3 如何選擇IO通道發(fā)送數(shù)據(jù)... 4
6.3.4 如何以DeviceCode分配數(shù)據(jù)... 4
6.4 宿主程序服務(wù)實例配置注意事項... 5
6.5 并發(fā)模式運行效果... 6
6. 并發(fā)通訊模式開發(fā)及注意事項
6.1 概述
并發(fā)通訊模式只能用于網(wǎng)絡(luò)通訊設(shè)備,主要是加強(qiáng)通訊的并發(fā)能力,集中發(fā)送請求數(shù)據(jù),異步接收返回數(shù)據(jù)。集中發(fā)送請求數(shù)據(jù)的間隔時間可以設(shè)置;異步接收返回數(shù)據(jù)涉及到如何分配數(shù)據(jù)到相應(yīng)的設(shè)備驅(qū)動的問題,主要是通過兩種方式:IP地址的方式和設(shè)備Code的方式,前者適用于設(shè)備終端是固定IP地址的情況,后者適用于設(shè)備終端是動態(tài)IP的情況,例如:DTU、GPRS、3G/4G等無線通訊方式。
并發(fā)通訊模式本質(zhì)上還是呼叫應(yīng)答的通訊方式,與輪詢通訊模式類似,但是比輪詢通訊模式的采集數(shù)據(jù)更高效。
6.2 通訊機(jī)制說明
網(wǎng)絡(luò)通訊的情況下,輪詢模式顯然效率比較低,那么可以采用并發(fā)模式。并發(fā)通訊模式是集中發(fā)送給所有設(shè)備請求指令,框架是采用循環(huán)同步方式發(fā)送請求命令給每個IO通道對應(yīng)的設(shè)備,當(dāng)然也可以采用并行異步方式集中發(fā)送請求命令。硬件設(shè)備接收到指令后進(jìn)行校驗,校驗成功后返回對應(yīng)指令的數(shù)據(jù),通訊平臺異步監(jiān)聽到數(shù)據(jù)信息后,進(jìn)行接收操作,然后再進(jìn)行數(shù)據(jù)的分發(fā)、處理等。
那么這里就涉及到IO通道接收到的數(shù)據(jù)是異步接收的,如何才能和設(shè)備驅(qū)動匹配上(把數(shù)據(jù)分發(fā)到設(shè)備驅(qū)動上),這是能過DeviceCode和DeviceIP兩種方式來實現(xiàn)的。DeviceCode可以是設(shè)備地址或是設(shè)備編碼,DeviceIP是預(yù)先設(shè)置好的參數(shù),要求終端設(shè)備的IP地址是固定的。
通訊結(jié)構(gòu)如下圖:
`
6.3 設(shè)備驅(qū)動開發(fā)注意事項
6.3.1 實時發(fā)送數(shù)據(jù)
ServerSuperIO框架會輪詢調(diào)度所有設(shè)備,以呼叫應(yīng)答的方式向設(shè)備發(fā)送請求實時數(shù)據(jù)命令,對于同一個設(shè)備的請求實時數(shù)據(jù)命令一般相對固定。在調(diào)度某一具體設(shè)備驅(qū)動的時候,會調(diào)用固定的調(diào)用IRunDevice驅(qū)動接口的GetConstantCommand函數(shù),以獲得請求實時數(shù)據(jù)的命令。代碼如下:
1 2 3 4 5 6 7 | public override byte [] GetConstantCommand() { byte [] data = this .Protocol.DriverPackage<String>( "0" , "61" , null ); string hexs = BinaryUtil.ByteToHex(data); OnDeviceRuningLog( "發(fā)送>>" +hexs); return data; } |
this.Protocol.DriverPackage驅(qū)動調(diào)用61命令獲得要發(fā)送的命令,并返回byte[]數(shù)組,ServerSuperIO獲得數(shù)據(jù)后會自動通過IO接口下發(fā)命令數(shù)據(jù)。如果返回null類型,系統(tǒng)不進(jìn)行下發(fā)操作。
6.3.2 優(yōu)先發(fā)送其他數(shù)據(jù)
對于一個設(shè)備不可能只有一個讀實時數(shù)據(jù)的命令,可能還存在其他命令進(jìn)行交互,例如:讀參數(shù)、實時校準(zhǔn)等,這時就需要進(jìn)行優(yōu)先級調(diào)度發(fā)送數(shù)據(jù)信息??梢酝ㄟ^兩種方式讓ServerSuperIO框架優(yōu)先調(diào)度該設(shè)備驅(qū)動。
把命令增加發(fā)送數(shù)據(jù)緩存中,框架從緩存中獲得數(shù)據(jù)后會自動刪除,代碼如下:
1 | this .Protocol.SendCache.Add( "讀參數(shù)" ,readParaBytes); |
2.設(shè)置設(shè)備的優(yōu)先級別屬性,代碼如下:
1 | this .DevicePriority=DevicePriority.Priority; |
6.3.3 如何選擇IO通道發(fā)送數(shù)據(jù)
集中發(fā)送數(shù)據(jù)時,涉及到如何關(guān)聯(lián)設(shè)備驅(qū)動與IO通道,框架會以DeviceParameter.NET.RemoteIP設(shè)置的終端IP參數(shù)進(jìn)行選擇IO通道發(fā)送數(shù)據(jù)。但是如果終端設(shè)備是動態(tài)IP地址的話,那么RemoteIP參數(shù)也應(yīng)該是變動的。這時就需要設(shè)置服務(wù)實例是以DeviceCode的方式分布數(shù)據(jù)到設(shè)備驅(qū)動,終端設(shè)備先發(fā)送簡單的驗證數(shù)據(jù),保證發(fā)送的DeviceCode與設(shè)備驅(qū)動的相對應(yīng),設(shè)備驅(qū)動接收到驗證數(shù)據(jù)后需要保存臨時的RemoteIP信息,這樣保證在發(fā)送數(shù)據(jù)的時候參數(shù)準(zhǔn)確找到要請求數(shù)據(jù)的IO通道到終端設(shè)備。
例如下面代碼:
1 2 3 4 5 6 | public override void Communicate(ServerSuperIO.Communicate.IRequestInfo info) { this .DeviceParameter.NET.RemoteIP = info.Channel.Key; this .DeviceParameter.Save( this .DeviceParameter); …… } |
6.3.4 如何以DeviceCode分配數(shù)據(jù)
如果服務(wù)實例設(shè)置以DeliveryMode.DeviceCode模式分配數(shù)據(jù),那么就需要在通訊協(xié)議接口里實現(xiàn)過濾DeviceCode編碼的接口。
例如下面的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | internal class DeviceProtocol:ProtocolDriver { public override string GetCode( byte [] data) { byte [] head = new byte [] {0x55, 0xaa}; int codeIndex = data.Mark(0, data.Length, head); if (codeIndex == -1) { return String.Empty; } else { return data[codeIndex + head.Length].ToString(); } } } |
6.4 宿主程序服務(wù)實例配置注意事項
在宿主程序中創(chuàng)建服務(wù)實例的時候,需要把服務(wù)實例的配置參數(shù)設(shè)置為并發(fā)通訊模式,并啟動服務(wù)實例,把實例化的設(shè)備驅(qū)動增加到該服務(wù)實例中。代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | static void Main( string [] args) { IServer server = new ServerFactory().CreateServer( new ServerConfig() { ServerName = "服務(wù)1" , ComReadTimeout = 1000, ComWriteTimeout = 1000, NetReceiveTimeout = 1000, NetSendTimeout = 1000, ControlMode = ControlMode.Parallel, SocketMode = SocketMode.Tcp, StartReceiveDataFliter = false , ClearSocketSession = false , StartCheckPackageLength = false , CheckSameSocketSession = false , DeliveryMode = DeliveryMode.DeviceCode, ParallelInterval = 1000 }); server.AddDeviceCompleted += server_AddDeviceCompleted; server.DeleteDeviceCompleted += server_DeleteDeviceCompleted; server.Start(); string devCode = "0" ; DeviceDriver dev1 = new DeviceDriver(); dev1.DeviceParameter.DeviceName = "設(shè)備驅(qū)動" + devCode.ToString(); dev1.DeviceParameter.DeviceAddr = int .Parse(devCode); dev1.DeviceParameter.DeviceCode = devCode.ToString(); dev1.DeviceParameter.DeviceID = devCode.ToString(); dev1.DeviceDynamic.DeviceID = devCode.ToString(); dev1.DeviceParameter.NET.RemoteIP = "127.0.0.1" ; dev1.DeviceParameter.NET.RemotePort = 9600; dev1.CommunicateType = CommunicateType.NET; dev1.Initialize(devCode.ToString()); server.AddDevice(dev1); devCode = "1" ; DeviceDriver dev2 = new DeviceDriver(); dev2.DeviceParameter.DeviceName = "設(shè)備驅(qū)動" + devCode.ToString(); dev2.DeviceParameter.DeviceAddr = int .Parse(devCode); dev2.DeviceParameter.DeviceCode = devCode.ToString(); dev2.DeviceParameter.DeviceID = devCode.ToString(); dev2.DeviceDynamic.DeviceID = devCode.ToString(); dev2.DeviceParameter.NET.RemoteIP = "192.168.1.102" ; dev2.DeviceParameter.NET.RemotePort = 9600; dev2.CommunicateType = CommunicateType.NET; dev2.Initialize(devCode.ToString()); server.AddDevice(dev2); while ( "exit" == Console.ReadLine()) { server.Stop(); } } |
ControlMode = ControlMode. Parallel代碼是設(shè)置服務(wù)實例調(diào)度設(shè)備為并發(fā)控制模式;以DeliveryMode = DeliveryMode.DeviceCode方式進(jìn)行數(shù)據(jù)分發(fā),當(dāng)然我現(xiàn)在模擬的是因定的終端IP。
6.5 并發(fā)模式運行效果
1.圖片
2.視頻
1.[連載]《C#通訊(串口和網(wǎng)絡(luò))框架的設(shè)計與實現(xiàn)》
2.[開源]C#跨平臺物聯(lián)網(wǎng)通訊框架ServerSuperIO(SSIO)介紹
2.應(yīng)用SuperIO(SIO)和開源跨平臺物聯(lián)網(wǎng)框架ServerSuperIO(SSIO)構(gòu)建系統(tǒng)的整體方案