国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

[連載]《C#通訊(串口和網(wǎng)絡(luò))框架的設(shè)計(jì)與實(shí)現(xiàn)》-4.設(shè)備驅(qū)動(dòng)管理器的設(shè)計(jì)

2019-11-14 13:44:18
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

目       錄

第四章           設(shè)備驅(qū)動(dòng)管理器的設(shè)計(jì)... 2

4.1           接口定義... 2

4.2           設(shè)備容器... 7

4.3           生成設(shè)備ID.. 7

4.4           對(duì)設(shè)備容器操作的互斥... 8

4.5           獲得設(shè)備列表... 8

4.6           設(shè)備計(jì)數(shù)器的特殊用處... 8

4.7           小結(jié)... 10

 

第四章     設(shè)備驅(qū)動(dòng)管理器的設(shè)計(jì)

    設(shè)備驅(qū)動(dòng)管理器是對(duì)IRunDevice設(shè)備驅(qū)動(dòng)接口的管理,是框架重要的組成部分之一。不管設(shè)備驅(qū)動(dòng)管理器怎么設(shè)計(jì)、以什么形式存在,在概念上肯定是存在的。設(shè)計(jì)好的設(shè)備驅(qū)動(dòng)管理器對(duì)于框架平臺(tái)的穩(wěn)定運(yùn)行至關(guān)重要。

   在介紹設(shè)備驅(qū)動(dòng)管理器之前,先簡(jiǎn)單介紹一下IO控制器(IIOController),它主要負(fù)責(zé)對(duì)IO和設(shè)備進(jìn)行調(diào)度,并驅(qū)動(dòng)設(shè)備運(yùn)行,在《第5章 串口和網(wǎng)絡(luò)的IO設(shè)計(jì)》進(jìn)行詳細(xì)的介紹。也就是說(shuō)一個(gè)IO控制器可能會(huì)對(duì)應(yīng)多個(gè)設(shè)備驅(qū)動(dòng)(插件)。

   早期的時(shí)候,每個(gè)IO控制器都有一個(gè)設(shè)備驅(qū)動(dòng)管理器。在框架平臺(tái)啟動(dòng)的時(shí)候,根據(jù)設(shè)備驅(qū)動(dòng)的通訊參數(shù)把相應(yīng)的設(shè)備驅(qū)動(dòng)分配到相應(yīng)的IO管理器;當(dāng)IO參數(shù)發(fā)生變化的時(shí)候,會(huì)觸發(fā)事件,把該設(shè)備驅(qū)動(dòng)從當(dāng)前IO控制器移動(dòng)到另一個(gè)IO控制器。

   從業(yè)務(wù)角度來(lái)考慮,這樣做并沒(méi)有什么問(wèn)題,并且一直運(yùn)行的很穩(wěn)定。但是,從模塊化、擴(kuò)展性角度來(lái)考慮,不是太理想,如果在其他地方調(diào)用某一個(gè)設(shè)備驅(qū)動(dòng)時(shí),不能直接、很快的找到該設(shè)備驅(qū)動(dòng),必要遍歷IO控制器再匹配相應(yīng)的設(shè)備驅(qū)動(dòng),并且操作麻煩以及效率不高。

   在對(duì)框架平臺(tái)進(jìn)行重構(gòu)的時(shí)候,把該問(wèn)題進(jìn)行了重新考慮,并把相關(guān)聯(lián)的問(wèn)題一起解決。把每個(gè)IO控制器中的設(shè)備驅(qū)動(dòng)管理器進(jìn)行了整合,用一個(gè)設(shè)備驅(qū)動(dòng)管理器來(lái)完成框架平臺(tái)的協(xié)調(diào)工作。

   這塊涉及到的技術(shù)并不難,也很容易理解,但是在設(shè)計(jì)過(guò)程中需要注意一些細(xì)節(jié)問(wèn)題,這些問(wèn)題可能影響框架平臺(tái)的穩(wěn)定性。

4.1    接口定義

    先定義一個(gè)接口(IDeviceManager<TKey, TValue>),確定設(shè)備驅(qū)動(dòng)管理器都要完成什么功能,增加設(shè)備、刪除設(shè)備、獲得設(shè)備和列表、以及其他的功能。接口代碼如下:

public interface IDeviceManager<TKey, TValue> : IEnumerable<TValue> where TValue : IRunDevice{       /// <summary>       /// 新建設(shè)備的ID,且唯一       /// </summary>       /// <returns></returns>       string BuildDeviceID();        /// <summary>       /// 增加設(shè)備       /// </summary>       /// <param name="key"></param>       /// <param name="val"></param>       void AddDevice(TKey key, TValue val);       /// <summary>       /// 刪除設(shè)備       /// </summary>       /// <param name="key"></param>       void RemoveDevice(TKey key);       /// <summary>       /// 移除所有設(shè)備       /// </summary>       void RemoveAllDevice();       /// <summary>       /// 獲得值集合       /// </summary>       /// <returns></returns>       List<TValue> GetValues();       /// <summary>       /// 獲得關(guān)鍵字集合       /// </summary>       /// <returns></returns>       List<TKey> GetKeys();       /// <summary>       /// 獲得設(shè)備的ID和名稱(chēng)       /// </summary>       /// <returns></returns>       Dictionary<int, string> GetDeviceIDAndName();        /// <summary>       /// 獲得高優(yōu)先運(yùn)行設(shè)備       /// </summary>       /// <param name="vals"></param>       /// <returns></returns>       TValue GetPRiorityDevice(TValue[] vals);       /// <summary>       /// 獲得單個(gè)設(shè)備       /// </summary>       /// <param name="key"></param>       /// <returns></returns>       TValue GetDevice(TKey key);       /// <summary>       /// 獲得設(shè)備數(shù)組       /// </summary>       /// <param name="para">ip或串口號(hào)</param>       /// <param name="ioType">通訊類(lèi)型</param>       /// <returns></returns>       TValue[] GetDevices(string para, CommunicationType ioType);       /// <summary>       /// 獲得指定IP和工作模式的網(wǎng)絡(luò)設(shè)備       /// </summary>       /// <param name="remoteIP"></param>       /// <param name="workMode"></param>       /// <returns></returns>       TValue[] GetDevices(string remoteIP, WorkMode workMode);       /// <summary>       /// 獲得指定工作模式的網(wǎng)絡(luò)設(shè)備       /// </summary>       /// <param name="workMode"></param>       /// <returns></returns>       TValue[] GetDevices(WorkMode workMode);       /// <summary>       /// 獲得設(shè)備數(shù)組       /// </summary>       /// <param name="ioType"></param>       /// <returns></returns>       TValue[] GetDevices(CommunicationType ioType);       /// <summary>       /// 按設(shè)備類(lèi)型獲得設(shè)備       /// </summary>       /// <param name="devType"></param>       /// <returns></returns>       TValue[] GetDevices(Device.DeviceType devType);       /// <summary>       /// 判斷設(shè)備是否存在       /// </summary>       /// <param name="key"></param>       /// <returns></returns>       bool ContainDevice(TKey key);       /// <summary>       /// 根據(jù)輸入?yún)?shù),判斷是否包括設(shè)備       /// </summary>       /// <param name="para">IP或串口號(hào)</param>       /// <param name="ioType">設(shè)備通訊類(lèi)型</param>       /// <returns></returns>       bool ContainDevice(string para, CommunicationType ioType);       /// <summary>       /// 設(shè)置用戶(hù)級(jí)別       /// </summary>       /// <param name="userlevel"></param>       void SetUserLevel(UserLevel userlevel);       /// <summary>      /// 設(shè)置是否注冊(cè)       /// </summary>       /// <param name="isreg"></param>       void SetIsRegLicense(bool isreg);       /// <summary>       /// 獲得可用設(shè)備數(shù)       /// </summary>       int Count { get; }       /// <summary>       /// 獲得設(shè)備的計(jì)數(shù)器的值       /// </summary>       /// <param name="key"></param>       ///<returns></returns>       int GetCounter(TKey key);       /// <summary>       /// 設(shè)置計(jì)數(shù)器的值       /// </summary>       /// <param name="key"></param>       /// <param name="val"></param>       void SetCounter(TKey key, int val);}

 4.2    設(shè)備容器

   設(shè)備驅(qū)動(dòng)管理器是對(duì)Dictionary<Key,Value>的封裝,Key是設(shè)備驅(qū)動(dòng)的ID,Value是IRunDevice設(shè)備驅(qū)動(dòng)接口。設(shè)備驅(qū)動(dòng)管理器需要跨線(xiàn)程應(yīng)用,所以對(duì)Dictionary操作要加線(xiàn)程同步鎖。

   當(dāng)時(shí)使用的是.NET Framework 2.0框架,沒(méi)有ConcurrentDictionary(Of TKey, TValue)字典類(lèi),這個(gè)類(lèi)的所有公共和受保護(hù)的成員都是線(xiàn)程安全的,使用原子性操作,適合多個(gè)線(xiàn)程之間同時(shí)使用。再重構(gòu)時(shí)可以使用ConcurrentDictionary類(lèi)代替Dictionary類(lèi),因?yàn)镃oncurrentDictionary的所有操作使用到了Monitor線(xiàn)程同步類(lèi),不需要自己再進(jìn)行封裝。

   不貼ConcurrentDictionary類(lèi)的源代碼了,具體使用參考MSDN。

4.3    生成設(shè)備ID

    查尋設(shè)備驅(qū)動(dòng)管理器中最大的設(shè)備ID,并在此基礎(chǔ)上加1。這塊代碼很簡(jiǎn)單,

如下:

public string BuildDeviceID(){       if(_dic.Count>0)       {          int maxID=_dic.Max(d => d.Value.DeviceParameter.DeviceID);          return (++maxID);       }       else       {              return 0;       }}

    增加設(shè)備驅(qū)動(dòng)是需要生成設(shè)備ID,一般采用手動(dòng)增加設(shè)備驅(qū)動(dòng),所以在這塊不需要加線(xiàn)程同步鎖。

4.4    對(duì)設(shè)備容器操作的互斥

框架平臺(tái)所有組件要共享設(shè)備驅(qū)動(dòng)管理器,所以會(huì)涉及到跨線(xiàn)程應(yīng)用,特別

是當(dāng)集合發(fā)生改變的時(shí)候,可能會(huì)出現(xiàn)異常。例如:?jiǎn)?dòng)框架平臺(tái)的時(shí)候,IO控制器已經(jīng)啟動(dòng),IO控制器從設(shè)備驅(qū)動(dòng)管理器提取自己的設(shè)備列表,但是這時(shí)有可能還沒(méi)有加載完設(shè)備驅(qū)動(dòng),當(dāng)有新的設(shè)備驅(qū)動(dòng)增加到設(shè)備驅(qū)動(dòng)管理時(shí),可能會(huì)引發(fā)沖突。

    所以,在增加設(shè)備、刪除設(shè)備和獲得設(shè)備列表的時(shí)候增加了線(xiàn)程同步鎖,例如:lock (_SyncLock)。

4.5    獲得設(shè)備列表

有多個(gè)獲得設(shè)備的構(gòu)造函數(shù)(GetDevices),主要是滿(mǎn)足不同的應(yīng)用場(chǎng)景。

請(qǐng)參考“4.1接口定義”。

    另外,獲得高優(yōu)先運(yùn)行設(shè)備的GetPriorityDevice函數(shù)在上一章節(jié)已經(jīng)介紹了。

4.6    設(shè)備計(jì)數(shù)器的特殊用處

    在接口定義中有SetCounter和GetCounter兩個(gè)函數(shù),用在通訊過(guò)程中。

    應(yīng)用場(chǎng)景是這樣的,在并發(fā)和自控通訊模式中,設(shè)備驅(qū)動(dòng)一直處于在通訊正常的情況下,但是突然發(fā)生線(xiàn)路中斷或其他原因?qū)е聼o(wú)法接收到數(shù)據(jù)時(shí),那么設(shè)備驅(qū)動(dòng)一直無(wú)法接收到數(shù)據(jù),也無(wú)法對(duì)通訊狀態(tài)進(jìn)行檢測(cè)以及改變相應(yīng)的數(shù)據(jù)信息,也就是說(shuō)現(xiàn)實(shí)情況已經(jīng)發(fā)生改變,但是設(shè)備驅(qū)動(dòng)卻無(wú)法得到響應(yīng)。

    為了防止這種情況的出現(xiàn),設(shè)備驅(qū)動(dòng)每次發(fā)送數(shù)據(jù)時(shí),通過(guò)GetCounter函數(shù)獲得當(dāng)前設(shè)備驅(qū)動(dòng)的計(jì)數(shù)器,對(duì)計(jì)數(shù)器(變量)+1操作,并通過(guò)SetCounter函數(shù)把計(jì)數(shù)器(變量)再寫(xiě)到設(shè)備驅(qū)動(dòng)管理器中。在異常接收數(shù)據(jù)的時(shí)候,執(zhí)行相同的流程,但是執(zhí)行-1操作。如果一直發(fā)送數(shù)據(jù),而沒(méi)有接收到數(shù)據(jù)時(shí),當(dāng)前設(shè)備驅(qū)動(dòng)的計(jì)數(shù)器就會(huì)一直在累加。如果大于等于某個(gè)值的時(shí)候,就會(huì)通過(guò)RunIODevice(new byte[]{})驅(qū)動(dòng)當(dāng)前設(shè)備,執(zhí)行整個(gè)設(shè)備處理流程,二次開(kāi)發(fā)的代碼塊就會(huì)被調(diào)用,來(lái)完成此類(lèi)應(yīng)用場(chǎng)景的狀態(tài)改變和數(shù)據(jù)變化。代碼如下:

int counter = DeviceManager.GetInstance().GetCounter(dev.DeviceParameter.DeviceID.ToString());int sendNum = sessionSocketManager.GetInstance().Send(dev.DeviceParameter.NET.RemoteIP, data);if (sendNum == data.Length && sendNum != 0){       DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "發(fā)送請(qǐng)求數(shù)據(jù)");       Interlocked.Increment(ref counter);}else{       Interlocked.Increment(ref counter);       DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "嘗試發(fā)送數(shù)據(jù)失敗");}dev.ShowMonitorIOData(data, "發(fā)送");if (counter >= 3){       try       {              dev.RunIODevice(new byte[] { });       }       catch (Exception ex)       {              DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, ex.Message);              GeneralLog.WriteLog(ex);       }       Interlocked.Exchange(ref counter, 0);}DeviceManager.GetInstance().SetCounter(dev.DeviceParameter.DeviceID.ToString(), counter);

   對(duì)于發(fā)送和接收數(shù)據(jù)會(huì)在不同的線(xiàn)程上完成,在對(duì)計(jì)數(shù)器(變量)進(jìn)行+1和-1操作的時(shí)候使用到了Interlocked類(lèi),用于多個(gè)線(xiàn)程共享的變量提供原子操作,防止在多處理器上并行操作時(shí)可能引發(fā)的異?;驍?shù)據(jù)遭到破壞。

4.7    小結(jié)

   這樣改造后,不僅可以在IO控制器對(duì)設(shè)備進(jìn)行引用,也可以在其他組件使用。如果遇到類(lèi)似的情況,希望使用ConcurrentDictionary類(lèi)。

 

作者:唯笑志在

Email:504547114@QQ.com

QQ:504547114

.NET開(kāi)發(fā)技術(shù)聯(lián)盟:54256083

文檔下載:http://pan.baidu.com/s/1pJ7lZWf

官方網(wǎng)址:http://www.bmpj.net


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 旅游| 平定县| 隆子县| 页游| 苍南县| 馆陶县| 客服| 洛阳市| 喜德县| 石景山区| 蓬溪县| 河间市| 孙吴县| 青浦区| 木兰县| 滦南县| 乐亭县| 南华县| 木兰县| 九寨沟县| 永丰县| 游戏| 河池市| 海盐县| 临高县| 乌拉特前旗| 云阳县| 玛纳斯县| 奎屯市| 保定市| 武清区| 秦皇岛市| 盐山县| 炉霍县| 常宁市| 齐河县| 互助| 彝良县| 满城县| 南陵县| 贺州市|