asynDriver-1

目的

asynDriver是一个用于连接设备专用代码到底层驱动的通用工具。asynDriver允许非阻塞设备支持,它对阻塞和非阻塞驱动都有作用。

asynDriver的主要目标是EPICS IOC设备支持,但其除了使用libCom外,其大部分独立于EPICS。

asynDriver有以下关键概念:

  • 设备支持通过接口与驱动通信:驱动管理如何与一个设备通信的细节以及实现由设备支持使用的接口。为基于消息和基于寄存器设备都定义了接口。在过去,在为一种新类型设备编写支持时,除了驱动支持,还要必须编写标准EPICS记录的设备支持。现在驱动仅需要实现一个或者多个标准接口。
  • 一个端口提供了对设备实例的访问:一个端口,其由一个端口名称,识别对一个或多个设备实例的一条通信路径。例如,GPIB端口可以最多有15个设备连接它。一个RS232端口与单个设备通信。设备支持连接一个端口。
  • asynManager控制对一个端口的访问:asynManager,asynDriver的一个组件,通过调用queueRequest, lockPort/unlockPort和queueLockPort/queueUnlockPort提供对一个驱动的独占性访问。一旦设备支持能够访问,它知道其它支持不能调用这个驱动,它可以对这个驱动进行任意数目的调用。由于asynManager为设备和驱动支持实现了队列和信号量,它们不需要实现队列或者信号量。
  • asynTrace提供通用诊断功能:为提供诊断消息定义了规则。如果设备和驱动支持实现了这些规则,使用者可以获得若干程度的诊断信息,这些诊断信息可以被显示在console,写入文件或者被发送给EPICS errlog功能。
  • asynRecord:对设备/端口的通用访问。

asynRecord时一个EPICS记录以及相关联MEDM窗口集合,它们提供对以下的访问:

  1. 一个端口或者连接一个端口的设备:端口或者port,addr可以被动态地修改。因而在一个IOC中有一个asynRecord,与有asyn兼容的驱动的任何设备进行会话是可能的。
  2. asynTrace:所有asynTrace选项可以用asynRecord控制。
  3. 连接管理:显示和更改连接,使能以及自动连接状态。
  4. 标准接口:可以使用这些与设备进行通信。例如,如果出现了一个有串口,GPIB或者以太端口的新设备,则仅通过给它连接一个asynRecord,与其进行通信经常是可能的。
  • 扩展的串口:asynDriver提供很多功能,用于与RS232, RS485, GPIB和以太网进行通信。

状态

这个版本提供:

  • asynManager:设备支持和驱动之间的软件层。
  • asynRecord:EPICS记录支持,它提供了对asynManager, asynCommon, asynOctet, asynGpib和其它接口的通用接口。
  • asynPortDriver:一个C++基类,使得编写asyn驱动变得容易,在这个基类方法中处理了大部分的样板asyn代码。
  • asynPortClient:一个C++基类,它使得编写在不运行一个EPICS IOC下直接与asyn端口驱动进行通信的C++ asyn客户端变得容易。
  • 标准接口:定义了基于标准消息和基于寄存器的接口。底层驱动实现了标准接口。设备支持通过标准接口与底层驱动进行通信。
  • devEpics:对EPICS记录的通用设备支持。
  • devGpib:EPICS设备支持,它提供了Winans/Franksen gpibCore支持的设备支持层。
  • asynGpib:对Franksen gpibCore支持的drvGpibCommon层的替代。
  • drvAsynSerialPort:支持连接了串口的设备。
  • drvAsynIPPort:支持TCP/IP和UDP/IP通信,包括通过以太网/串口转换器被访问的串口设备。
  • drvAsynIPServerPort:支持来自远程客户端受访问的asyn套接字服务器。支持TCP/IP套接字和UDP。
  • VXI-11:对Franksen gpibCore支持的VXI-11支持的替代。
  • Linux-gpib:对Linux GPIB包库的支持。
  • gsIP488:Greensprings IP488 Industry Pack模块的底层支持。
  • ni1014:对国家仪器VME 1014D的底层驱动。
  • 串行总线支持:添加了asynLockPortNotify接口,使得更容易支持使用标准串行支持的串行总线驱动。

以下是一些已存在的EPICS通用设备支持系统,这些系统已经被转成了使用asynDriver。

  • streamDevice:这是来自Dirk Zimoch的用于串行/GPIB/CAN的基于协议文件的支持。
  • gpibCore:这是Winans/Franksen GPIB支持的操作系统无关版本。
  • synApps(APS BCDA同步应用程序)。在这个包中的mca,dxp,motor,Ip330, IpUnidig, DAC128V和quadEM应用程序全都转成了asyn。在这个包中的串行和GPIB模块不再需要了,因为asyn记录替代了它们。使用了asyn编写了areaDetector模块,并且是开发asynPortDriver的最初动机。

asynDriver的概况

定义

asynDriver是设备专用代码和与设备通信的驱动之间的软件层。它支持阻塞和非阻塞通信,能够与基于寄存器和基于消息的设备一起使用。asynDriver使用以下术语:

  • 接口:软件层之间的所有通信是通过接口完成的。接口定义是一个包含了整个函数指针的C语言结构体。一个asynDriver接口类似一个C++或Java纯虚接口。虽然用C实现,实质是面向对象。因而本文档使用术语"方法"而不是"函数指针"。
  • 端口:一个物理或逻辑实体,它提供了对设备的访问。一个端口提供对一个或多个设备的访问。
  • portDiver:与一个端口进行通信的代码。
  • portThread:一个portDriver可以阻塞,为每个端口创建一个线程,并且通过这个线程完成对这个portDriver的所有I/O。
  • 设备:一个连接一个端口的设备(仪器)。例如,GPIB接口可以有最多15个设备连接它。其它端口,例如,RS-232串口,仅支持单个设备。当这个文档使用不带修饰符的词语设备时,它表示连接了一个端口的某个东西。
  • 设备支持:与设备交换的代码。
  • 同步:不自愿放弃CPU控制的支持。
  • 异步:不是同步的支持。某些异步操作示例是epicsThreadSleep, epicsEventWait和stdio操作。对epicsMutex的调用被认为是同步操作,即,它们在同步操作中被允许。
  • asynDriver:在本手册中描述的支持的名称。它也是描述核心接口的头文件名称。
  • asynManager:实现了用于接口asynManager和asynTrace的方法的接口和代码。
  • 异步驱动:当与设备通信时,一个阻塞的驱动。代表性示例是基于串行,gpib和网络驱动。
  • 同步驱动:当与设备通信时,不阻塞的驱动。代表性示例是基于VME的设备。
  • 基于消息的接口:对读写操作使用octet数组的接口。
  • 基于寄存器的接口:对读写操作使用整数或浮点数的接口。
  • 中断:如asynManager实现的,中断仅表示"我有一个对应端口,地址的新值"。

同步/异步和基于消息/寄存器是传统概念。例如,一个基于寄存器的驱动可以是同步或异步。术语寄存器 vs 消息是改编自VXI。

标准接口被定义了,使得设备专用代码可以与多个端口驱动进行通信。如果设备支持通过由8位字节(octets)组成的读写,进行所有其通信,则它应该对支持octet消息的所有端口驱动都有效。如果设备支持需要更复杂的支持,则端口类型将更加受限。也为接收32位整数或64位浮点的驱动定义了标准接口。可以定义更多接口,并且预计将定义更多标准接口。

一个或多个设备可以连接到一个端口。例如,仅一个设备可以被连接到一个RS-232端口,但最多15个设备可以连接一个GPIB端口。

在设备专用代码和端口驱动之间可以存在多层。为了被放在设备专用代码和驱动之间,一个软件层称为interposeInterface。对于更复杂的协议,可以创建其它层。例如,GPIB支持被以asynGpib接口实现,它被用户代码调用,并且asynGpibPort接口被asynGpib调用。

一个驱动通常实现多个接口。例如,asynGpib接口实现了和asynCommon, asynOctet和asynGpib。

asynManager使用了EPICS base的操作系统无关特性。但它独立于记录/设备支持。因而,它可以被其它代码,例如sequence程序。

标准接口

这些是由asynManager提供的接口,或者由所有或大部分端口驱动实现的接口。

接口是:

asynManager提供了与连接端口的设备进行进行通信的服务。

asynCommon是一个接口,它必须由所有底层驱动实现。方法是:

  • report:报告端口的状态。
  • connnect:连接端口或设备。
  • disconnect:从端口或设备断开。

asynTrace是一个产生诊断消息的接口。

asynLockPortNotify是一个由一个驱动实现的接口,它是另一个驱动的一个asynUser。一个示例是使用了标准串行支持的串行总线驱动。asynManager在它锁定或解锁端口时调用asynLockPortNotify。

asynDrvUser是一个用于从设备支持传递信息给驱动的接口,不需要设备支持知道被传递东西的任何细节。

通用接口

除了asynCommon和可选的asynDrvUser外,端口驱动还可以实现一个或多个以下基于消息和/或基于寄存器的接口。

  • asynOctet方法用于基于消息的驱动。
  • asynFloat64方法用于读写IEEE float值的设备。
  • asynFloat32Array方法用于读写IEEE 32位浮点值数组的设备。
  • asynFloat64Array方法用于读写IEEE 64位浮点值数组的设备。
  • asynInt32方法用于读写整数值。很多I/O驱动可以使用这个接口。
  • asynInt8Array方法用于读写8位整数值数组的设备。
  • asynInt16Array方法用于读写16位整数值数组的设备
  • asynInt32Array方法用于读写32位整数值数组的设备
  • asynUInt32Digital方法用于读写数字值数组的设备。这个接口提供了一个掩码来寻址寄存器中单独的位。
  • asynGenericPointer方法用于读写任何结构体的设备,通过一个void*指针被传递。客户端和服务器当前需要在被指向的结构体类型上达成一致。
  • asynEnum方法用于设备定义枚举字符串,值和严重性。
  • asynOption方法用于使用键值对的设备配置。

asyManager

asynManager是一个接口和象立代码。由于它管理设备支持代码和驱动之间的交互,它是asynDriver的核心。它提供了以下服务:

1)报告

方法:report

2)asynUser创建

方法:createAsynUser, duplicateAsynUser, freeAsynUser

asynUer是一个用于调用asynManager服务和用于调用由驱动实现的接口的"句柄"。只能通过调用createAsynUser或者duplicateAsynUser才能创建一个asynUser,因为asynManager位每个asynUser维护私有信息。freeAsynUser放置asynUser到一个空闲列表而不是调用free。客户端可以连续快速无碎片化都创建和释放asynUsers。

对createAsynUser的调用指定了一个processCallback和一个timeoutCallback。这些是回调,它们将由于queueRequest的结果被调用。一个asynUser不应该在能够同时访问一个驱动的代码部分之间被共享。例如,对标准EPICS记录的设备支持应该为每个记录实例创建一个asynUser。

3)基本的asynUser服务

方法:connectDevice, disconnect, findInterface

这些方法仅被创建这个asynUser的代码调用。

在创建一个asynUser后,用户调用connectDevice。用户连接了一个能够与一个设备进行通信的端口驱动。为用户所需的每个接口,调用findInterface。

4)排队服务

方法:queueRequest, cancelRequest, lockPort, unlockPort, queueLockPort, queueUnlockPort, blockProcessCallback, unblockProcessCallback

queueRequest是一个调用在对createAsynUser的调用中指定的processCallback的请求。大部分接口方法只能通过调用queueRequest或在lockPort/unlockPort之间从processCallback被调用。这个规则的例外必须被清楚地记录(一个常见例外是registerInterruptUser/cancelInterruptUser)。

queueRequest含义对于能够阻塞的端口和不阻塞的端口不同。

当registerPort被一个可以阻塞的驱动调用时,为这个端口创建一个线程。根据优先级,为此线程创建一个队列集合。queueRequest防止请求到其中一个队列。端口线程接收来自队列的请求并且调用相关回调。一个时刻仅一个回调是有效的。

当registerPort被一个不阻塞的线程调用,为这个端口创建一个mutex。queueRequest获取这个mutex,调用回调,并且释放这个mutex。mutex确保对一个端口的两个回调不同时有效。

lockPort是一个请求,在调用unlockPort前,锁定对底层驱动的所有访问。如果端口阻塞,则lockPort和对这个端口驱动的所有调用会阻塞。提供lockPort/unlockPort为愿意阻塞的代码使用或者用于与同步端口进行通信。对lockPort的调用锁定与一个多地址端口相关联的所有地址。在R4-14前,pasynManager->lockPort()在端口mutex可获取时立即获取它,而不是排队一个获取这个mutex的请求。从asyn R4-14到R4-20,lockPort排队一个访问这个端口的请求,并且接着阻塞,直到这个队列请求回调运行在portThread中。当队列请求运行时,调用paynManager->lockPort()的线程执行,而portThread阻塞,直到调用了pasynManager->unlock()。在R4-21中,排队的lockPort和unlockPort函数被重命名为queueLockPort和queueUnlockPort,原先轻量级lockPort和unlockPort恢复了。直到R4-32,当queueLockPort调用queueRequest时,它没有指定一个超时。在R4-32中,用一个超时进行queueRequest。默认超时值时2.0秒,但这可以通过shell命令asynSetQueueLockPortTimeout(portName, double timeout)被更改。如果传递给queueLockPort的pasynUser->timeout大于当前端口超时值,使用来自pasynUser的更大超时。

blockProcessCallback时一个在queueRequests之间防止其它asynUsers访问一个设备或端口。blockProcessCallback可以被从一个processCallback或者在asynUser没有排队的请求时调用。当从processCallback被调用时,阻塞立即开始,否则阻塞在下次processCallback被调用时,阻塞开始。阻塞表示在unblockProcessCallback被调用前,其它asynUser的processCallback将不被调用。blockProcessCallback仅对可以阻塞的驱动有效,如果为非阻塞驱动调用它,返回一个错误。

5) 基本的驱动服务

方法:registerPort, registerInterface

registerPort被portDriver调用。registerInterface被portDriver或interposeInterface调用。

每个端口驱动提供一个配置命令,为每个端口实例执行它。配置命令为它实现的每个接口,执行端口特定的初始化,调用registerPort和registerInterface。

6)属性提取

方法:enable, autoConnect, setAutoConnectTimeout

这些方法能够被能够访问这个asynUser的任何代码调用。

可以调用这些方法为端口和/或设备设置enable和autoConnect。如果autoConnect是真,则asynManager做以下:

当端口注册了它的asynCommon接口,asynManager排队一个连接请求。它接着等待一小段时间等待连接回调结束。默认时间是0.5秒,但用函数pasynManager->setAutoConnectTimeout(double timeout),更高这个时间。可以从iocsh shell用asynSetAutoConnectTimeout(double timeout)命令访问这个函数。设计这个短的超时允许如果设备可获取有时间连接,而不由于等待例如TCP连接上系统超时,过渡减慢IOC的启动。注意,这意味着注册asynCommon接口后pasynCommon->connect()调用将立即发生,这意味着在驱动注册asynCommon接口前,它必须已经完成了asynCommon->connect()回调所需的所有初始化。如果端口初始没有连接,或者它之后断开,则asynManager将每20秒排队一个连接请求。如果autoConnect是真,并且端口/设备被使能,但设备没有连接,则asynManager调用就在它调用processCallback前调用asynCommon::connect。

7)异常服务

方法:exceptionCallbackAdd, exceptionCallbackRemove, exceptionConnect, exceptionDisconnect

当一个端口驱动连接或断开时,通常作为调用asynCommon::connect或asynCommon::disconnect的结果,它也必须调用exceptionConnect或exceptionDisconnect。

8) 中断服务

方法:registerInterruptSource, getInterruptPvt, createInterruptNode, freeInterruptNode, addInterruptUser, removeInterruptUser, interruptStart, interruptEnd

中断仅表示:"我有一个新值"。很多asyn接口,例如,asynInt32提供了中断支持。这些接口提供方法addInterruptUser和removeInterruptUser。如果设备支持想要在一个中断发射时被调用,它调用addInterruptUser。实现这个接口的驱动或者其它代码在其有新数据时调用注册的用户。asynManager提供了服务,它们帮助驱动实现了对中断线程安全的支持。

一个支持中断的驱动为每个有相关联中断的接口调用registerInterruptSource。它调用interruptStart获取一个所有注册用户的列表,并且在他调用了注册的用户后,调用interruptEnd。驱动也负责调用addInterruptUser和removeInterruptUser。

如果在interruptStart和interruptEnd调用之间,对addInterruptUser或removeInterruptUser进行任何调用,asynManager将放置这个请求在一个列表上并且在interruptEnd被调用后处理这个请求。

很多标准接口,例如asynInt32,提供了registerInterruptUser, cancelInterruptUser方法。这些接口也提供了一个辅助接口,例如asynInt32Base和实现了registerInterruptUser和cancelInterruptUser代码。

在像vxWorks或RTEMS的操作系统上,一定不能从中断级别调用interruptStart, interruptEnd。

9)时间戳服务

方法:updateTimeStamp, getTimeStamp, setTimeStamp, registerTimeStampSource, unreigsterTimeStampSource

这些方法提供了用于为一个端口设置时间戳的支持。这个时间戳一般用于设置pasynUser->timestamp字段,在读取或回调操作时,它被传递给设备支持。设备支持使用pasynUser->timestamp字段来设置记录TIME字段。如果记录TSE字段是-2,这将是此记录时间戳。asynManager提供一个只调用epicsTimeGetCurrernt()的默认时间戳源函数。但registerTimeStampSouce可以用于提供一个不同的用户提供的时间戳源函数,例如,调用epicsTimeGetEvent()一个时间戳源或者某个其它站点专用的时间戳源。unregisterTimeStampSouce恢复pasynManager中的默认时间戳源。

10)通用空闲列表服务

方法:memMalloc memFree

这些方法不需要一个asynUser。为必须连续分配和释放内存的代码提供了这两个方法。由于memFree放置内存到一个空闲列表而不是调用free,它们效率高于calloc/free,并且也防止了内存碎片化。

11)插入服务

方法:interposeInterface

调用interposeInterface的代码实现了一个接口,此接口不被一个中断驱动支持,或者它是插入在调用者和端口驱动之间。例如,asynInterposeEos插入asynOctet。它为不支持字符串结尾处理的端口驱动执行这个功能。

interposeInterfacePos是迭代的,即,在单个端口,addr上可以存在任意数目的插入层。

多设备 vs 单设备端口驱动

当一个底层驱动调用registerPort时,它声明它是否处理多设备。这确定了如何处理传递给connectDevice的addr参数,以及getAddr返回什么。

  • multiDevice false:忽略传给connectDevice的addr参数,并且getAddr总是返回-1。
  • multiDevice true:如果用addr<0调用connectDevice,连接是到端口并且getAddr总是返回-1。如果addr>=0,则调用者是在指定地址连接设备。getAddr将返回这个地址。连接了这个端口的asynUser可以发出影响这个端口上所有地址的请求。例如,禁用对这个端口的访问阻止对这个端口上所有地址的访问。

连接管理

asynManager跟踪以下状态:

  • connection:端口或设备连接了吗?这个状态被初始化成断开。
  • enabled:端口或设备启用了吗?这个状态被初始化成使能。
  • autoConnect:如果asynManager发现端口或设备断开,asynManager自动尝试连接吗?这被初始化成在registerPort调用中指定的状态。

如果这个端口不支持多设备,则端口和设备状态是相同的。如果端口支持多设备,则asynManager跟踪端口的状态和连接这个端口的每个设备。

一旦端口或设备的状态有任何变化,则先前为那个端口或设备调用exceptionCallbackAdd的所有用户被调用。

当底层驱动连接一个端口或端口,addr时,它们必须调用pasynManager.exceptionConnect,和在它们断开时,它们必须调用pasynManager.exceptionConnect。

防止线程阻塞

方法asynManager:report和asynCommon:report可以被任何线程调用,但调用者被阻塞到在这个report结束。lockPort, unlockPort, queueLockPort, queueUnlockPort和大部分端口方法会阻塞。asynManager方法可以被包括portThread的任何线程调用。这些方法都不阻塞。

除非说明,或者其它接口的方法只能被processCallback调用或者在lockPort/unlockPort或者queueLockPort/queueUnlockPort之间调用。

接口方法registerInterruptUser和cancelInterruptUser必能不能阻塞。registerInterruptUser回调一定不阻塞,因为它会被一个非阻塞驱动调用。

portThread

如果驱动用置位了ASYN_CANBLOCK属性调用asynManager:registerPort,则asynManager为这个端口创建一个线程。每个portThread有其自己的用于调用queueRequest的队列集合。一个队列仅用于asynCommon:connect和asynCommon:disconnect请求。其它队列提供了不同优先级:低,中和高。如果端口未连接,对除了连接队列外任何队列的queueRequests将被拒绝。portThread一直运行,实现了以下算法:

  1. 通过调用epicsEventMustWait等待工作。诸如queueRequest的其它代码调用epicsEventSignal。
  2. 如果端口被禁用,回到1。
  3. 对于在队列中任何元素,asynQueuePriorityConnect:1)从队列移除一个元素;2)调用这个用户的回调
  4. 对于队列asynQueuePriorityHigh, ...,asynQueuePriorityLow的每个元素:1) 如果禁用,跳过这个元素,2)如果未连接而autoConnect对这个设备为真,则尝试连接这个设备;3)如果未连接,跳过这个元素;4)如果被另一个线程阻塞,跳过这个元素;如果未阻塞,并且用户请求了阻塞,则阻塞;5)从队列移除并且a) 锁定端口b)调用用户回调c) 解锁端口

实际代码更加复杂,因为在它调用asynManager意外代码前,它解锁。这表示队列可以修改并且移除会发生。

队列概况

当讨论排队时,考虑asyn的三个组件是有用的:

1)asynManager。这是asyn的核心部分。它对EPICS记录一无所知。实际上,除了它为了OS无关的东西,如mutex,消息队列,事件等,使用libCom外,它完全独立于EPICS。它提供的排队是用于回调请求通过pasynManager->queueReqeuest()与异步驱动(ASYN_CANBLOCK)进行通信。

2)标准的asyn设备支持(devEpics目录)。这是asyn了解EPICS记录的仅有部分并且依赖于除了libCom外的EPICS组件。它在3个条件下,支持来自驱动的回调:

  1. SCAN=I/O Intr的输入记录
  2. 周期扫描的输入记录(仅asynInt32Average和asynFloat64Average)
  3. asyn:READBACK=1的输出记录。

回调值可以被放入一个环形缓存,因而如果回调发生快于记录能够处理,值不丢失。可以用asyn:FIFO信息标签控制这个环形缓存的大小。默认值对于标量记录是10。默认值对于waveform记录,和对于stringout和stringin记录是0。如果环形缓存在用,则每次驱动回调产生向这个缓存压入一个新值以及一个在单独回调线程中运行这个记录的请求。如果环形缓存满了,则队列中最老的值被丢弃,并且添加这个新值。这确保了记录加你个最终由最近回调的值,但它在此前可能跳过了一些。如果ASYN_TRACE_WARNING设置了,则打印一条警告消息。驱动回调不阻塞等待这个记录运行。

3)asynPortDriver不支持排队。它有一个存储标量参数的最新值的参数库。它不为数组参数存储值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值