[连载]《C#通讯(串口和网络)框架的筹划与落实》-4.配备驱动管理器的规划

目       录

第四章           设备驱动管理器的设计… 2

4.1           接口定义… 2

4.2           设备容器… 7

4.3           生成设备ID.. 7

4.4           对设备容器操作的互斥… 8

4.5           拿到装备列表… 8

4.6           设备计数器的非凡用处… 8

4.7           小结… 10

 

第四章     设备驱动管理器的规划

   
设备驱动管理器是对IRunDevice设备驱动接口的军事管制,是框架首要的组成部分之一。不管设备驱动管理器怎么统筹、以什么花样存在,在概念上肯定是存在的。设计好的设施驱动管理器对于框架平台的安定团结运转重点。

  
在介绍装备驱动管理器从前,先简单介绍一下IO控制器(IIOController),它至关首要承担对IO和装置举行调度,并驱动装置运行,在《第5章
串口和网络的IO设计》举办详细的介绍。也就是说一个IO控制器可能会对应两个装备驱动(插件)。

  
早期的时候,每个IO控制器都有一个设施驱动管理器。在框架平台启动的时候,依据设备驱动的简报参数把相应的装置驱动分配到对应的IO管理器;当IO参数发生变化的时候,会触发事件,把该设备驱动从脚下IO控制器移动到另一个IO控制器。

  
从业务角度来设想,那样做并不曾什么样问题,并且一直运行的很稳定。不过,从模块化、扩充性角度来设想,不是太非凡,倘诺在其它地方调用某一个装备驱动时,不可以直接、很快的找到该设施驱动,必要遍历IO控制器再配合相应的设备驱动,并且操作麻烦以及功能不高。

  
在对框架平台展开重构的时候,把该问题开展了重新考虑,并把相关联的问题联合化解。把每个IO控制器中的设备驱动管理器举行了组合,用一个设施驱动管理器来成功框架平台的协调工作。

  
这块涉及到的技能并不难,也很容易精通,不过在设计过程中需要小心一些细节问题,那些题目也许影响框架平台的平稳。

4.1    接口定义

    先定义一个接口(IDeviceManager<TKey,
电视机alue>),确定设备驱动管理器都要水到渠成什么效劳,扩充设备、删除设备、拿到装备和列表、以及任何的效率。接口代码如下:

public interface IDeviceManager<TKey, TValue> : IEnumerable<TValue> where TValue : IRunDevice
{
       /// <summary>
       /// 新建设备的ID,且唯一
       /// </summary>
       /// <returns></returns>
       string BuildDeviceID();

       /// <summary>
       /// 增加设备
       /// </summary>
       /// <param name="key"></param>
       /// <param name="val"></param>
       void AddDevice(TKey key, TValue val);

       /// <summary>
       /// 删除设备
       /// </summary>
       /// <param name="key"></param>
       void RemoveDevice(TKey key);

       /// <summary>
       /// 移除所有设备
       /// </summary>
       void RemoveAllDevice();

       /// <summary>
       /// 获得值集合
       /// </summary>
       /// <returns></returns>
       List<TValue> GetValues();

       /// <summary>
       /// 获得关键字集合
       /// </summary>
       /// <returns></returns>
       List<TKey> GetKeys();

       /// <summary>
       /// 获得设备的ID和名称
       /// </summary>
       /// <returns></returns>
       Dictionary<int, string> GetDeviceIDAndName();

       /// <summary>
       /// 获得高优先运行设备
       /// </summary>
       /// <param name="vals"></param>
       /// <returns></returns>
       TValue GetPriorityDevice(TValue[] vals);

       /// <summary>
       /// 获得单个设备
       /// </summary>
       /// <param name="key"></param>
       /// <returns></returns>
       TValue GetDevice(TKey key);

       /// <summary>
       /// 获得设备数组
       /// </summary>
       /// <param name="para">IP或串口号</param>
       /// <param name="ioType">通讯类型</param>
       /// <returns></returns>
       TValue[] GetDevices(string para, CommunicationType ioType);

       /// <summary>
       /// 获得指定IP和工作模式的网络设备
       /// </summary>
       /// <param name="remoteIP"></param>
       /// <param name="workMode"></param>
       /// <returns></returns>
       TValue[] GetDevices(string remoteIP, WorkMode workMode);

       /// <summary>
       /// 获得指定工作模式的网络设备
       /// </summary>
       /// <param name="workMode"></param>
       /// <returns></returns>
       TValue[] GetDevices(WorkMode workMode);

       /// <summary>
       /// 获得设备数组
       /// </summary>
       /// <param name="ioType"></param>
       /// <returns></returns>
       TValue[] GetDevices(CommunicationType ioType);

       /// <summary>
       /// 按设备类型获得设备
       /// </summary>
       /// <param name="devType"></param>
       /// <returns></returns>
       TValue[] GetDevices(Device.DeviceType devType);

       /// <summary>
       /// 判断设备是否存在
       /// </summary>
       /// <param name="key"></param>
       /// <returns></returns>
       bool ContainDevice(TKey key);

       /// <summary>
       /// 根据输入参数,判断是否包括设备
       /// </summary>
       /// <param name="para">IP或串口号</param>
       /// <param name="ioType">设备通讯类型</param>
       /// <returns></returns>
       bool ContainDevice(string para, CommunicationType ioType);

       /// <summary>
       /// 设置用户级别
       /// </summary>
       /// <param name="userlevel"></param>
       void SetUserLevel(UserLevel userlevel);

       /// <summary>
      /// 设置是否注册
       /// </summary>
       /// <param name="isreg"></param>
       void SetIsRegLicense(bool isreg);

       /// <summary>
       /// 获得可用设备数
       /// </summary>
       int Count { get; }

       /// <summary>
       /// 获得设备的计数器的值
       /// </summary>
       /// <param name="key"></param>
       ///<returns></returns>
       int GetCounter(TKey key);

       /// <summary>
       /// 设置计数器的值
       /// </summary>
       /// <param name="key"></param>
       /// <param name="val"></param>
       void SetCounter(TKey key, int val);
}

 4.2    设备容器

  
设备驱动管理器是对Dictionary<Key,Value>的包裹,Key是装备驱动的ID,Value是IRunDevice设备驱动接口。设备驱动管理器需要跨线程应用,所以对Dictionary操作要加线程同步锁。

   当时使用的是.NET Framework 2.0框架,没有ConcurrentDictionary(Of TKey,
电视alue)字典类,那些类的享有国有和受保障的积极分子都是线程安全的,使用原子性操作,适合七个线程之间同时接纳。再重构时可以拔取ConcurrentDictionary类代替Dictionary类,因为ConcurrentDictionary的有所操作使用到了Monitor线程同步类,不需要协调再开展打包。

   不贴ConcurrentDictionary类的源代码了,具体行使参考MSDN。

4.3    生成设备ID

    查寻设备驱动管理器中最大的装备ID,并在此基础上加1。这块代码很简短,

如下:

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

   
扩展设备驱动是亟需转变设备ID,一般拔取手动扩展设备驱动,所以在这块不需要加线程同步锁。

4.4    对设施容器操作的排挤

框架平台具有组件要共享设备驱动管理器,所以会涉嫌到跨线程应用,特别

是当集合暴发变动的时候,可能会出现非凡。例如:启动框架平台的时候,IO控制器已经起步,IO控制器从设备驱动管理器提取自己的设备列表,然而此时有可能还尚无加载完设备驱动,当有新的装置驱动扩大到装备驱动管理时,可能会吸引顶牛。

   
所以,在增多设备、删除设备和拿到装备列表的时候扩展了线程同步锁,例如:lock
(_SyncLock)。

4.5    得到装备列表

有四个得到装备的构造函数(GetDevices),首假使知足不同的应用场景。

请参见“4.1接口定义”。

   
此外,拿到高优先运行设备的GetPriorityDevice函数在上一章节曾经介绍了。

4.6    设备计数器的超常规用途

    在接口定义中有SetCounter和GetCounter多个函数,用在报道过程中。

   
应用场景是如此的,在产出和约束通讯形式中,设备驱动一向处于在报道正常的状态下,但是忽然发生线路中断或其余原因造成不可以收到到多少时,那么设备驱动一向不可以接收到数量,也罔知所措对报道状态举办检测以及变更相应的多少音信,也就是说现实情状已经发出变更,然则设备驱动却无法赢得响应。

   
为了避免这种情景的产出,设备驱动每一次发送数据时,通过GetCounter函数得到当前装备驱动的计数器,对计数器(变量)+1操作,并因而SetCounter函数把计数器(变量)再写到设备驱动管理器中。在分外接收数据的时候,执行同一的流程,但是实施-1操作。假使直接发送数据,而没有收受到数量时,当前设施驱动的计数器就会一向在充裕。要是过量等于某个值的时候,就会通过RunIODevice(new
byte[]{})驱动当前设备,执行总体设施处理流程,二次开发的代码块就会被调用,来形成此类应用场景的情形改变和数据变动。代码如下:

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, "发送请求数据");
       Interlocked.Increment(ref counter);
}
else
{
       Interlocked.Increment(ref counter);
       DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "尝试发送数据失败");
}
dev.ShowMonitorIOData(data, "发送");
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);

  
对于发送和接到数据会在不同的线程上成功,在对计数器(变量)举办+1和-1操作的时候利用到了Interlocked类,用于七个线程共享的变量提供原子操作,避免在多处理器上并行操作时或者引发的不得了或数额遭到破坏。

4.7    小结

  
这样改造后,不仅可以在IO控制器对设施开展引用,也得以在另外零件使用。假如碰着类似的情况,希望拔取ConcurrentDictionary类。

 

笔者:唯笑志在

Email:504547114@qq.com

QQ:504547114

.NET开发技术联盟:54256083

文档下载:http://pan.baidu.com/s/1pJ7lZWf

合法网址:http://www.bmpj.net

相关文章