[连载]《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, TValue>),确定设备驱动管理器都要完成什么功能,增加设备、删除设备、获得设备和列表、以及其他的功能。接口代码如下:
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, TValue)字典类,这个类的所有公共和受保护的成员都是线程安全的,使用原子性操作,适合多个线程之间同时使用。再重构时可以使用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
[连载]《C#通讯(串口和网络)框架的设计与实现》-4.设备驱动管理器的设计的更多相关文章
- 开源跨平台IOT通讯框架ServerSuperIO,集成到NuGet程序包管理器,以及Demo使用说明
物联网涉及到各种设备.各种传感器.各种数据源.各种协议,并且很难统一,那么就要有一个结构性的框架解决这些问题.SSIO就是根据时代发展的阶段和现实实际情况的结合产物. 各种数据信息,如下图 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 6.通讯控制器的设计
目 录 第六章 通讯控制器的设计... 2 6.1 控制器接口... 2 6.2 串口控制器... 3 6.3 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》-1.通讯框架介绍
[连载]<C#通讯(串口和网络)框架的设计与实现>- 0.前言 目 录 第一章 通讯框架介绍... 2 1.1 通讯的本质... 2 1 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》-2.框架的总体设计
目 录 C#通讯(串口和网络)框架的设计与实现... 1 (SuperIO)- 框架的总体设计... 1 第二章 框架总体的设计... 2 2.1 ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》-3.设备驱动的设计
目 录 第三章 设备驱动的设计... 2 3.1 初始化设备... 4 3.2 运行设备接口设计... 4 3.3 ...
- 结合Retrofit,RxJava,Okhttp,FastJson的网络框架RRO
Retrofit以其灵活的调用形式, 强大的扩展性著称. 随着RxAndroid的推出, Retrofit这样的可插拔式的网络框架因其可以灵活兼容各种数据解析器, 回调形式(主要还是RxJava啦)而 ...
- Linux设备驱动框架设计
引子 Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码.能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Li ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 0.前言
目 录 前言 前言 刚参加工作,使用过VB.VC开发软件,随着C#的崛起,听说是C++++,公司决定以后开发软件使用C#,凭借在 ...
- 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
随机推荐
- How.To.Process.Image.Infomation.Of.Rotate.And.Flip.From.Server
需求说明 客户端接收到服务器传送过来的图像数据,客户端通过对图像进行旋转和反转操作. 然后把这个旋转和反转的数据上传到服务器. 客户端在接收图像的时候, 也会下载以前的旋转和反转参数, 然后客户端根据 ...
- Fiddler调式使用知多少(一)深入研究
Fiddler调式使用(一)深入研究 阅读目录 Fiddler的基本概念 如何安装Fiddler 了解下Fiddler用户界面 理解不同图标和颜色的含义 web session的常用的快捷键 了解we ...
- iOS开发-捕获程序崩溃日志
iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时 ...
- iOS-----dSYM 文件分析工具
来到新公司后,前段时间就一直在忙,前不久 项目 终于成功发布上线了,最近就在给项目做优化,并排除一些线上软件的 bug,因为项目中使用了友盟统计,所以在友盟给出的错误信息统计中能比较方便的找出客户端异 ...
- python调取C/C++的dll生成方法
本文针对Windows平台下,python调取C/C++的dll文件. 1.如果使用C语言,代码如下,文件名为test.c. __declspec(dllexport) int sum(int a,i ...
- 深入理解DOM事件机制系列第三篇——事件对象
× 目录 [1]获取 [2]事件类型 [3]事件目标[4]事件代理[5]事件冒泡[6]事件流[7]默认行为 前面的话 在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事 ...
- Hibernate学习之hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hi ...
- SubSonic3.0 Demo1.0——应用了T4模版可减少开发过程中70%以上的代码量以及80%以上的出错率
应网友的要求,抽了点时间写了这个Demo,希望对2.2版想升级到3.0的朋友或正在使用3.0的朋友有所帮助.大家在使用Demo过程中如果发现什么问题或有什么建议,可以直接将Bug提交给我或告诉我,我会 ...
- 前端工程优化:javascript的优化小结
我觉得优化javascript是一门高深的学问,在这里也只能站在前人的肩膀上,说一些我浅显的认识,更希望的是抛钻引玉,如有不对,敬请斧正. 首先,要认识到是,优化js的关键之处在于,优化它的运行速度 ...
- Notes: select选择框
HTML选择框通过select标签创建,该元素是HTMLSelectElement的实例,拥有以下属性和方法: selectedIndex:选中项的索引 options:选择框的所有选项 add:向选 ...