EPICS asynManager介绍

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

1)报告

方法:report

2)asynUser创建

方法:createAsynUser, duplicateAsynUser, freeAsynUser

asynUser是一个用于访问asynManager服务和用于调用驱动实现的接口的"句柄"。只能通过createAsynUser或duplicateAsynUser创建一个asynUser,由于asynManager为每个asynUser保有私有信息。freeAsynUsesr把这个asynUser放到一个空闲列表,而不是调用free。客户端可以快速持续地创建和释放asynUser而不使内存碎片化。对createAsynUser的调用指定一个processCallback和一个timeoutCallback。这些是回调,将由于一个queueReques的结果被调用。

在会同时访问一个驱动的代码部分之间不应该共享一个asynUser。例如,对标准EPICS记录的设备支持应该为每个记录实例创建一个asynUser。

3)基本asynUser服务

方法:connectDevice, disconnect, findInterface

这些方法只应该被创建asynUser的代码调用。

在创建一个asynUser后,使用者调用connectDevice。这个使用者连接到了一个能够与一个设备仅限通信的端口驱动。为这个使用者需要的每个接口调用findInterface. 当这个使用者用完这个设备时,disconnect被调用。

4)排队服务

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

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

queueRequest语义对可以阻塞的端口和不可以阻塞地端口不同。

当registerPort被一个可以阻塞的驱动调用,为这个端口端口一个线程。基于优先级,为这个线程创建一个队列集合。queueRequest在这些队列之一上放置请求。这个端口驱动从这些队列获取这些请求并且调用相关联的回调。一个时刻,仅一个回调有效。

当registerPort被不阻塞的驱动程序调用时,为这个端口创建一个互斥量。queueRequest获取这个互斥量,调用回调,并且释放这个互斥量。互斥量确保对一个端口的两个回调不同时活动。

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

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

5)基本驱动服务

方法:registerPort, registerInterface

registerPort被portDriver调用。registerInterface被portDriver或者一个InterposeInterface调用。

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

6) 属性获取

方法:isMultiDevice, canBlock, getAddr, getPortName, isConnected, isEnabled, isAutoConnect

这些代码可以被任何可以访问asynUser的代码调用。

7) 连接服务

方法:enable,autoConnect, setAutoConnectTimeout

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

这些方法能够被调用来为一个端口和/或设备设置enable和autoConnect设置。如果autoConnect为真,则asynManager做以下事情:

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

方法:exceptionCallbackAdd, exceptionCallbackRemove, exceptionConnect, exceptionDisconnect

设备支持调用exceptionCallbackAdd或exceptionCallbackRemove。异常的完整列表以"enum asynExeption"被定义在asynDriver.h中。当一个端口驱动连接或者断开时,通常作为对asynCommon:connect或asynCommon:disconnect的调用结果,它也必须调用exceptionConnect或exceptionDisconnect。

9)中断服务

方法: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,以及实现了registerInterrutUser和cancelInterruptUser的代码。

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

10) 时间戳服务

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

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

11) 通用的freelist服务:

方法:memMalloc, memFree

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

12) 插入服务:

方法:interposeInterface

调用interposeInterface的代码实现了一个接口,它要么不受一个端口驱动支持,或者那是调用这和端口驱动之间的"插入"。例如,asynInterposeEos插入asynOctet。它为不支持字符串末尾处理的端口驱动执行字符串末尾处理。

interpoeInterface是可迭代的,即:在单个端口,地址上,可以存在任意数目的interpose层。

多设备和单设备端口驱动

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

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

连接管理

asynManager跟踪以下状态:

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

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

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

底层驱动连接一个端口或者端口,地址时,底层驱动程序必须调用pasynManager:exception而在它们断开时,调用exceptionDisconnect。

防止一个线程阻塞

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

除非说明,否则用于其它接口的方法只能被processCallback调用护着通过lockPort/unlockPort,或者queueLockPort/queueUnlockPort之间调用。

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

portThread

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

1)通过调用epicsEventMustWait等待工作。其它诸如queueRequest的代码调用epicsEventSignal。

2)如果端口被禁用,回到1。

3)对于在队列中每个元素,asynQueuePriorityConnect:

  • 从队列移除元素
  • 调用用户的回调

4)对于队列asynQueuePriorityHigh, ..., asynQueuePriorityLow

  • 如果禁用,跳过这个元素
  • 如果对于这个设备未连接并且autoConnect是true,则尝试连接这个设备。
  • 如果未连接,跳过这个元素。
  • 如果被另一个线程阻塞,跳过这个元素。
  • 如果未被阻塞并且用户已经请求阻塞,则被阻塞
  • 从队列移除并且:a) 锁定端口;b) 调用用户的回调;3)解锁端口

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

排队概述

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

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

2)标准asyn设备支持(devEpics目录)。这是是知道EPICS记录并且取决于除了libCom外EPICS组件的asyn唯一组件。它支持在3个条件下来自驱动的回调。

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

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

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

操作理论

1)初始化

在初始化中,端口驱动注册每个通信端口以及所有直接的接口。

用户代码通过调用以下创建一个asynUser, 它是用于访问asynDriver的"句柄"。

  pasynManager->createAsynUser(processCallback,timeoutCallback);

一个asynUser有以下特性:

  • asynManager通过asynUser管理用于访问一个端口的多个请求。
  • processCallback,由以下描述的queueRequest使用,是一个用户提供的回调例程的地址。
  • timeoutCallback是用户提供的回调的地址,如果一个queueRequest在队列上保持太长时间,它将被调用。
  • 驱动支持代码应该为对底层驱动的"原子"访问创建一个asynUser,即,一组调用一定不能与对底层驱动的其它调用交织。例如,EPICS记录支持的设备支持应该为每个记录实例创建一个asynUser。
  • 设备支持代码不应该尝试在请求访问一个端口的多个请求源之间共享一个asynUser。如果这么做了,则设备支持必须自己处理已经由asynManager处理的竞争问题。

用户代码通过对以下的调用连接一个底层驱动程序:

  status = pasynManager->connectDevice(pasynUser,portName,addr);

这个调用必须指定这个端口的名称和设备地址。它接着调用findInterface来定位这些接口,它用这些接口调用驱动。例如:

pasynInterface = pasynManager->findInterface(pasynUser,asynOctetType,1);

2) 请求访问一个端口

用户代码通过两种方法请求访问一个端口。

1) queueRequest:传递给createAsynUser的processCallback对这个端口接口仅限调用。

2)lockPort/unlockPort, queueLockPort/queueUnlockPort:当持有锁时,调用者可以对端口接口仅限调用。这些调用和对端口的调用可能阻塞,并且因而不应该被不应该阻塞的代码使用,例如对EPICS记录的同步设备支持。

queueRequest -- 控制流

通过调用以下用户代码请求访问一个端口:

 status = pasynManager->queueRequest(pasynUser,priority,timeout);

这导致processCallback或timeoutCallback被调用。对一个端口的大多数请求必须从processCallback进行。queueRequest不阻塞。如果对一个能够阻塞的端口调用queueRequest,这个请求排队到一个专用于这个端口的线程。如果为一个不阻塞的端口调用queueRequest,它仅调用processCallback。只在底层驱动仅通过调用queueRequest,lockPort/unlockPort或queueLockPort/queueUnlockPort被访问时,保证才有效。

以下示例是基于EPICS IOC记录/设备支持。

第一个示例展示了一个能够阻塞的端口:

从一个应用程序线程开始,发生记录设备支持事件的顺序,如上图所示,并且用以下步骤解释:

  1. 记录processing用PACT 0调用设备支持。
  2. 设备支持调用queueRequest。
  3. queueRequest在驱动工作队列行放置请求。应用程序线程现在能够继续并且执行其它操作。这个I/O请求的后续操作在端口驱动线程中被处理。
  4. portThread从工作队列移除I/O请求。
  5. 这个portThread调用在记录设备支持中找到的processCallback。
  6. processCallback调用底层驱动。在I/O结束或者在发生超时前,低层驱动程序才读取或者写例程阻塞。底层驱动例程返回I/O操作的结果给processCallback。
  7. processCallback请求那个记录被运行。注意:这个process请求将由标准回调请求之一而不是这个端口线程进行。
  8. 记录支持再次调用设备支持,这次用PACT 1。设备支持更新在记录中的字段并且返回到记录支持,这结束了记录运行。

第二个示例显示了对不能阻塞端口的访问。

从一个应用程序线程开始,记录设备支持事件发生的顺序如上所示,并且用以下步骤解释:

  1. 记录运行调用设备支持。
  2. 设备支持调用queueRequest。
  3. 由于端口时同步的,即,不能阻塞,queueRequest锁定这个端口并且接着调用processCallback。
  4. processCallback调用底层驱动读或写例程。底层驱动例程返回I/O操作的结果给processCallback。
  5. processCallback返回到queueRequest,这解锁这个端口并且返回到设备支持,这返回到记录支持,这结束了记录运行。

asynInterface

这定义一个向asynManager:reigsterPort或asynManager:interposeInterface注册的接口。

typedef struct asynInterface{
    const char *interfaceType; /*For example, asynCommonType */
    void *pinterface;          /*For example, pasynCommon */
    void *drvPvt;
}asynInterface;
interfaceType一个描述这个接口的字符串
pinterface一个指向这个接口的指针。用户必须转换这个为正确的类型
drvPvt用于调用registerPort或interposeInterface的代码专用

asynManager

这是与asynDriver通信的主要接口。

/*registerPort attributes*/
#define ASYN_MULTIDEVICE  0x0001
#define ASYN_CANBLOCK     0x0002

/*standard values for asynUser.reason*/
#define ASYN_REASON_SIGNAL -1

#define ASYN_REASON_RESERVED_LOW 0x70000000
#define ASYN_REASON_RESERVED_HIGH 0x7FFFFFFF

#define ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED ASYN_REASON_RESERVED_LOW

typedef void (*userCallback)(asynUser *pasynUser);
typedef void (*exceptionCallback)(asynUser *pasynUser,asynException exception);
typedef void (*timeStampCallback)(void *userPvt, epicsTimeStamp *pTimeStamp);

typedef struct interruptNode{
    ELLNODE node;
    void    *drvPvt;
}interruptNode;

typedef struct asynManager {
    void      (*report)(FILE *fp,int details,const char*portName);
    asynUser  *(*createAsynUser)(userCallback process,userCallback timeout);
    asynUser  *(*duplicateAsynUser)(asynUser *pasynUser,
                                 userCallback queue,userCallback timeout);
    asynStatus (*freeAsynUser)(asynUser *pasynUser);
    void       *(*memMalloc)(size_t size);
    void       (*memFree)(void *pmem,size_t size);
    asynStatus (*isMultiDevice)(asynUser *pasynUser,
                                const char *portName,int *yesNo);
    /* addr = (-1,>=0) => connect to (port,device) */
    asynStatus (*connectDevice)(asynUser *pasynUser,
                                const char *portName,int addr);
    asynStatus (*disconnect)(asynUser *pasynUser);
    asynStatus (*exceptionCallbackAdd)(asynUser *pasynUser,
                                       exceptionCallback callback);
    asynStatus (*exceptionCallbackRemove)(asynUser *pasynUser);
    asynInterface *(*findInterface)(asynUser *pasynUser,
                            const char *interfaceType,int interposeInterfaceOK);
    asynStatus (*queueRequest)(asynUser *pasynUser,
                              asynQueuePriority priority,double timeout);
    asynStatus (*cancelRequest)(asynUser *pasynUser,int *wasQueued);
    asynStatus (*blockProcessCallback)(asynUser *pasynUser, int allDevices);
    asynStatus (*unblockProcessCallback)(asynUser *pasynUser, int allDevices);
    asynStatus (*lockPort)(asynUser *pasynUser);
    asynStatus (*unlockPort)(asynUser *pasynUser);
    asynStatus (*queueLockPort)(asynUser *pasynUser);
    asynStatus (*queueUnlockPort)(asynUser *pasynUser);
    asynStatus (*setQueueLockPortTimeout)(asynUser *pasynUser, double timeout);
    asynStatus (*canBlock)(asynUser *pasynUser,int *yesNo);
    asynStatus (*getAddr)(asynUser *pasynUser,int *addr);
    asynStatus (*getPortName)(asynUser *pasynUser,const char **pportName);
    /* drivers call the following*/
    asynStatus (*registerPort)(const char *portName,
                              int attributes,int autoConnect,
                              unsigned int priority,unsigned int stackSize);
    asynStatus (*registerInterface)(const char *portName,
                              asynInterface *pasynInterface);
    asynStatus (*exceptionConnect)(asynUser *pasynUser);
    asynStatus (*exceptionDisconnect)(asynUser *pasynUser);
    /*any code can call the following*/
    asynStatus (*interposeInterface)(const char *portName, int addr,
                              asynInterface *pasynInterface,
                              asynInterface **ppPrev);
    asynStatus (*enable)(asynUser *pasynUser,int yesNo);
    asynStatus (*autoConnect)(asynUser *pasynUser,int yesNo);
    asynStatus (*isConnected)(asynUser *pasynUser,int *yesNo);
    asynStatus (*isEnabled)(asynUser *pasynUser,int *yesNo);
    asynStatus (*isAutoConnect)(asynUser *pasynUser,int *yesNo);
    asynStatus (*setAutoConnectTimeout)(double timeout);
    asynStatus (*waitConnect)(asynUser *pasynUser, double timeout);
    /*The following are methods for interrupts*/
    asynStatus (*registerInterruptSource)(const char *portName,
                               asynInterface *pasynInterface, void **pasynPvt);
    asynStatus (*getInterruptPvt)(asynUser *pasynUser,
                               const char *interfaceType, void **pasynPvt);
    interruptNode *(*createInterruptNode)(void *pasynPvt);
    asynStatus (*freeInterruptNode)(asynUser *pasynUser,interruptNode *pnode);
    asynStatus (*addInterruptUser)(asynUser *pasynUser,
                                  interruptNode*pinterruptNode);
    asynStatus (*removeInterruptUser)(asynUser *pasynUser,
                                  interruptNode*pinterruptNode);
    asynStatus (*interruptStart)(void *pasynPvt,ELLLIST **plist);
    asynStatus (*interruptEnd)(void *pasynPvt);
    /* Time stamp functions */
    asynStatus (*registerTimeStampSource)(asynUser *pasynUser, void *userPvt, timeStampCallback callback);
    asynStatus (*unregisterTimeStampSource)(asynUser *pasynUser);
    asynStatus (*updateTimeStamp)(asynUser *pasynUser);
    asynStatus (*getTimeStamp)(asynUser *pasynUser, epicsTimeStamp *pTimeStamp);
    asynStatus (*setTimeStamp)(asynUser *pasynUser, const epicsTimeStamp *pTimeStamp);

    const char *(*strStatus)(asynStatus status);
}asynManager;
epicsShareExtern asynManager *pasynManager;
report报告有关这个asynPortManager的状态。如果portName是非NULL,它报告一个特定端口。如果portName是NULL,它报告每个注册的端口。它为每个被报告的端口,调用asynCommon。
createAsynUser创建一个asynUser。调用者指定两个回调,process和timeout。这些回调只作为queueRequest的结果才被调用。timeout回调可选。errorMessageSize个字符被分配给errorMessage。存储量不能被更改。如果这个方法不能分配存储区,它不返回。
duplicateAsynUser通过调用createAsynUser创建一个asynUser。它接着按如下初始化这个新的asynUser:用来自pasynUser的值初始化字段timeout,userPvt, userData和drvUser。他的connectDevice状态与对应pasynUser的相同。
freeAsynUser释放一个asynUser。用户必须通过这个调用释放asynUser。如果asynUser连接了一个端口,asynManager:disconnect被调用。如果disconnect出错,这个调用也将出错。在一个空闲列表上保存这个asynUser的存储区,并且在之后createAsynUser或duplicateAsynUser的调用中在被使用。因而持续调用createAsynUser(或duplicateAsynUser)和freeAsynUser是高效的。

memMalloc

memFree

分配/释放内存。memMalloc/memFree维护一组不同尺寸的freelists。因而任何需要短时间存储区的程序可以使用memMalloc/memFree来分配和释放存储区,而不引起内存碎片化。传递给memFree的尺寸必须与在memMalloc调用中指定值相同。
isMultiDevice回答问题"端口支持多设备吗?" 这个方法可以在调用connectDevice前被调用。
connectDevice连接asynUser结构体到一个由portName,addr指定的设备。端口名与在registerPort调用中指定的相同。如果asynUser已经连接了一个设备,这个调用将出错。如果端口不支持多个设备,则忽略addr。connectDevice只连接asynUser到对应portName,addr的端口驱动。端口驱动可能连接了实际设备,或者也可能没有连接。因而,connectDevice和asynCommon:connect完全不同。
diisconnect从port,addr断开这个asynUser,通过之前调用connectDevice连接了这个Port,addr。如果asynUser在排队或者被锁定,或者有一个通过exceptionCallbackAdd注册的回调,这个调用将出错。注意:asynManager:disconnect和asynCommon:disconnect完全不同。
exceptionCallbackAdd当由awsynException定义的异常之一发生时,将调用回调。回调可以调用isConnected, isEnabled, 或者isAutoConnect来查找连接状态。asynTrace提供了查找当前跟踪设置的方法。
excptionCallbackRemove移除回调。在disconnect前,这被调用。
findInterface

查找一个驱动接口。如果interposeInterfaceOK是真,则findInterface返回最后被注册或插入的接口。否则,返回通过registerPort注册的接口。如果是0,则不支持这种interfaceType

使用者需要这个驱动的接口和pdrvPvt的地址,因而能够对这个驱动进行调用。例如:

asynInterface * pasynInterface;

asynOctet * pasynOctet;

...

pasynInterface = pasynManager->findInterface(pasynUser, asynOctetType, 1);

if (!asynInterface) {/* 出错时,要做的事情 */}

pasynOctet = (asynOctet *)pasynInterface->pinterface;

pasynOctetPvt = pasynOctet->pdrvPvt;

...

/*  以下调用必须从一个回调进行 */

pasynOcetet->read(pasynOctetPvt, pasynUser, ...)

queueRequest

当registerPort被调用时,调用者必须指定它是否可以阻塞,即属性位ASYN_CANBLOCK被置位或清零。如果用ASYN_CANBLOCK true注册了这个端口,这个请求被放在了一个队列上。。如果用ASYN_CANBLOCK false注册了这个端口,则queueRequest锁定这个端口并且调用这个process回调。在两种情况下,在createAsynUser调用中指定的process回调被调用。

如果这个asynUser已经放在队列上了,返回asynError。当这个请求排队时,timeout启动。一个小于或等于0.0的值表示没有超时。在这个回调被调用前,请求被从这个队列移开。允许回调对asynManager进行请求,诸如queueRequest, blockProcessCallback等。甚至允许从一个回调调用freeAsynUser,但这个请求将被推迟到回调结束之后。

优先级asynQueuePriority必须用于asynCommon:connect和asynCommon:disconnect调用,并且一定不能用于任何其它调用。

如果一个timeout回调未被传递给createAsynUser, 并且请求了非0超时的queueRequest,请求失败。

尝试排队一个除了连接请求外的请求到一个断开的端口,将出错,除非reason是ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED。

cancelRequest如果asynUser排队了,从这个队列移除它。如果在调用cancelRequest时,process或timeout回调在运行,则在这个回调前,cancelRequest将不返回

blockProcessCallback

unblockProcessCallback

blockProcessCallback是一个在queueRequests之间防止其它asynUsers对设备或端口访问的请求。blockProcessCallback可以从一个processCallback或者当这个asynUser没有排队的请求时被调用。当从processCallback被调用时,阻塞立即开始,否则阻塞从下次调用processCallback开始。阻塞表示在unblockProcessCallback被调用前,其它asynUser的processCallback将被调用。注意:对blockProcessCallback的以下限制:

1)blockProcessCallback仅对能够阻塞的驱动有效,并且如果位非阻塞驱动调用它,返回一个错误。

2)指定asynQueuePriorityConnect优先级的queueRequests不被阻塞

同时阻塞所有设备以及asynUser连接的设备是可能的。

lockPort/

unlockPort

锁定对一个驱动的访问。这是由在对一个驱动进行调用时愿意阻塞的代码使用。代码可以调用lockPort, 进行对端口驱动的任意数目的调用,并且接着调用unlockPort。调用queueRequest和/或lockPort的其它代码将在对lockPort和unlockPort调用之间被延时。

queueLockPort/

queueUnlockPort

锁定对一个端口驱动的访问。这是由在对一个端口驱动及逆行访问时愿意阻塞的代码使用。代码可以调用queueLockPort, 进行对端口驱动任意次数的调用,并且接着调用queueUnlockPort。调用queueRequest和/或lockPort的其它代码在对queueLockPort和queueUnlockPort调用之间被延时。lockPort和queueLockPort的差别是queueLockPort排队一个锁定这个端口的请求,使用与queueRequest相同队列。这表示一个重复调用queueLockPort而在调用之间没有睡眠的线程将仍然允许其它线程访问这个端口。对于lockPort不是这样,它将在端口空闲时立即获取互斥量,并且完全阻塞其它线程访问这个端口。
setQueueLockPortTimeout设置在queueLockPort()中传递给queueRequest()的超时。在创建这个端口时,设置默认值2.0秒。这个函数可以用于更改那个值。注意:如果传递给queueLockPort的pasynUser->timeout值大于当前值,则使用这个更大的timeout值。
canBlock如果对底层驱动的调用能够阻塞,yesNo设为(0,1),即(false, true)。这个值是由传递给registerPort的属性确定的。
getAddr*addr是设置为用户在调用connectDevice中指定的地址,如果端口不支持多设备,为-1。
getPortName*pportName被设置为用户连接的端口的名称。
registerPort这个方法被驱动调用。为每个端口实例进行一个调用。属性是位集合:当前只定义两个比特位:ASYN_MULTIDEVICE和ASYN_CANBLOCK。驱动必须指定这些。autoConnect,它是(0,1)对应(no,yes),位这个端口和连接这个端口的所有设备提供初始值。只在ASYN_CANBLOCK=1时,优先级和栈才有用,在此中情况下,asynManager用epicsThreadCreate()创建这个端口线程时使用这些值。如果优先级是0,则将分配默认值epicsThreadPriorityMedium。如果stackSize是0,将分配epicsThreadGetStackSize(epicsThreadStackMedium)的默认值。portName参数指定名称,asynchronous代码的上层将通过这个名称指向这个通信接口实例。registerPort方法内部不制这个字符串到名称参数指向的位置。
registerInterface由端口驱动位每个支持的接口调用这个。这个方法不复制asynInterface到pasynInterface指向的东西。调用者必须在一个位置存储这个asynInterface,位这个端口声明周期维护这个位置。通常通过防止asynInterface结构体到"驱动私有"结构体来做这件事。
exceptionConnect当且仅当驱动连接端口或设备时,此方法必须被驱动调用。
exceptionDisconnect当且仅当驱动从端口或设备断开时,此方法必须被驱动调用。
interposeInterface

这是由客户端代码和端口驱动之间的软件层调用。例如,如果一个设备应答写,则创建一个软在每次写后发送读的模块,并且为接口asynOctet调用interposeInterface。

能够发出一个port/addr/interface的多个interposeInterface调用。*ppPrev被设置为先前asynInterface的地址。因而,最后调用interposeInterface的软件模块被用户代码调用。它接着调用倒数第二次调用interposeInterface的软件模块。这继续,直到实际的端口驱动被调用。

enable如果enable设为yes,则这些queueRequests在它们的队列超时发生前,不出队。
autoConnect如果autoConnect为真,并且端口或设备没有连接,在一个用户回调被调度要被调用时,asynManager调用pasynCommon->connect。
isConnected如果端口或设备(没有,有)连接,*yesNo设为(0,1)
isEnabled如果端口或设备(没有,有)启用,*yesNo设为(0,1)
isAutoConnect如果portThread(没有,有)自动连接,*yesNo设为(0,1)
setAutoConnectTimeout更改在等待来此端口驱动的初始连接回调时的timeout。这个回调发生响应asynManager排队一个连接请求,这在端口驱动注册它的asynCommon接口是发生。默认timeout是0.5秒。
waitConnect最多等待port/device连接timeout秒。
registerInterruptSorce如果一个底层驱动支持中断,它必须为每个支持中断的接口调用这个函数。pasynPvt必须是一个void *的地址,registerInterruptSource将传给它一个值。这个参数被传递给InteruptStart和interruptEnd。
getInterruptPvt想要调用createInterruptNode但不直到pasynPvt地址的任何代码可以通过它找到这个方法。调用者必须连接了设备,即,必须已经调用connectDevice。如果调用者没有连接,getInterruptPvt返回asynError。

createInterruptNode/

freeInterruptNode

这些方法是用户可以分配和释放InterruptNode的唯一方法。pasynPvt是从getInterruptPvt获取的值。createInterruptNode/freeInterruptNode是单独方法,而不是通过addInterruptUser/removeInterruptUser被自动做的方法,因而addInterruptUser/removeInterruptUser是高效的。

addInterruptUser/

removeInterruptUser

实现了registerInterruptUser/cancelInterruptUser的代码必须调用addInterruptUser/removeInterruptUser来添加用户到列表或者从列表移除用户,否则对interruptStaart/interruptEnd的调用将无效。这是一个高效操作,使得用户可以重复调用registerInterrupt/cancelInterruptUser。当一个中断正在被处理时,即在addInterruptUser/interruptEnd调用之间,二者之一被调用了,这个调用将阻塞到interruptEnd被调用时。在addInterruptUser调用中指定的asynUser的process回调一定不能调用removeInterruptUser否则它将一直阻塞。

interruptStart/

interruptEnd

实现中断的代码是取决于接口的。asynManager提供的唯一服务是用户列表的线程安全实现。当代码想要调用在registerInterruptUser调用中指定的回调,它调用interruptStart获取回调列表。当它结束时,它调用interruptEnd。如果在interruptStart和interruptEnd调用之间对addInterruptUser/removeInterruptUser进行任何请求,asynManager延迟这些请求,直到interruptEnd被调用。
registerTimeStampSource注册一个用户定义的时间戳回调函数
unregisterTimeStampSource注销任何用户定义的时间戳回调函数并且恢复为asynManager中的默认时间戳源函数,它只是调用epicsTimeGetCurrent()。
updateTimeStamp通过调用默认时间戳源或者用registerTimeStamp注册的用户定义的时间戳源为这个端口设置当前时间戳。
getTimeStamp为这个端口获取当前时间戳,它是由最近对updateTimeStamp的调用返回的。
setTimeStamp设置这个端口的当前时间戳,直接传递给这个函数的时间戳值。
strStatus返回一个对应这个asynStatus值的描述字符串
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值