asynDriver-2

news2024/11/17 13:44:33

操作理论

初始化

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

用户代码创建一个asynUser, 它是访问asynDriver功能的"句柄",通过调用:

pasynManager->createAsynUser(processCallback,timeoutCallback);

一个asynUser有以下特性:

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

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

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

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

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

请求访问一个端口

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

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

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

queueReqeust-控制流

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

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

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

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

第一个示例展示了对一个可以阻塞的端口的访问。

发生的记录支持事件的顺序从一个应用程序线程起始,并且用以下步骤解释:

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

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

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

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

asynDriver结构体和接口

asynDriver.h描述以下:

  • asynStatus:一个描述很多方法返回的状态的枚举。
  • asynException:一个描述异常的枚举。
  • asynQueuePriority:一个描述队列优先级的枚举。
  • asynUser:一个包含通用信息的结构体,并且是用于调用大多数方法的"句柄"。
  • asynInterface:一个描述一个接口的结构体。
  • userCallback:一个用于以上描述的用户处理回调函数的typedef。
  • exceptionCallback:一个用于用户回调的typedef,当异常发生时,被调用。
  • timeStampCallback:一个用户回调函数的typedef,其将被updateTimeStamp调用。
  • asynManager:一个用于与asynDriver通信的接口。
  • asynCommon:一个提供方法的接口,这些方法必须被所有底层驱动实现。
  • asynTrace:一个接口和相关函数和定义,它们实现了跟踪功能。

asynStatus

定义大多数方法返回的状态。如果一个方法返回除了asynSuccess外的一个状态,并且传给这个方法的其中一个参数是pasynUser,则这个方法预计写一条错误消息到pasynUser->errorMessage。

typedef enum {
    asynSuccess,asynTimeout,asynOverflow,asynError,asynDisconnected,asynDisabled
}asynStatus;

asynStatus

asynSuccess请求成功
asynTimeout请求由超时出错
asynOverflow驱动已经丢失输入数据。如果内部缓存或者用户提供的缓存太小,这会发生。在可能时,应该写底层驱动,使得用户可以以小段读取输入。
asynError某个其它错误发生。
asynDisconnected请求因为端口没有连接,出错
asynDisabled请求因为端口或设备被禁用,出错

asynException

为方法execeptionOccurred定义异常。

typedef enum {
    asynExceptionConnect,asynExceptionEnable,asynExceptionAutoConnect,
    asynExceptionTraceMask,asynExceptionTraceIOMask,asynExceptionTraceInfoMask,
    asynExceptionTraceFile,asynExceptionTraceIOTruncateSize
} asynException;

asynException

asynExceptionConnect端口或设备的连接状态发生变化
asynExceptionEnable端口或设备的使能状态发生变化
asynExceptionAutoConnect端口或设备的自动连接状态发生变化
asynExceptionTrackMask端口或设备的traceMask发生变化
asynExceptionTrackIOMask端口或设备的traceIOMask发生变化
asynExceptionTrackInfoMask端口或设备的traceInfoMask发生变化
asynExceptionTraceFile端口或设备的跟踪文件发生变化
asynExceptionTraceTruncateSize端口或设备的traceIOTruncateSize发生变化

asynQueuePriority

这定义了传递给queueRequest的优先级。

asynQueuePriority

typedef enum {
    asynQueuePriorityLow,asynQueuePriorityMedium,asynQueuePriorityHigh,
    asynQueuePriorityConnect
}asynQueuePriority;

asynUser

描述一个结构体,用户代码传递它到大多数asynManager和驱动方法。代码必须通过调用asynManager:createAsynUsser(或asynManager:duplicateAsynUser)和asynManager:freeAsynuser分配和释放要给asynUser。

typedef struct asynUser {
    char          *errorMessage;
    int            errorMessageSize;
    /* 必须由用户设置的超时 */
    double         timeout;  /* I/O操作的延时 */
    void          *userPvt; 
    void          *userData; 
    /* 以下是由驱动使用的  */
    void          *drvUser;
    /*  以下通常通过asynDrvUser->create()由驱动设置 */
    int            reason;
    epicsTimeStamp timestamp;
    /* 以下是用于来自方法调用的其它信息*/
    int            auxStatus;     /*  用于辅助状态 */
    int            alarmStatus;   /*  尤其对应EPICS记录警报状态 */
    int            alarmSeverity; /*  尤其对应EPICS记录警报严重性 */
}asynUser;

asynUser

errorMessage

当一个方法返回asynError时,它应该通过调用以下放置一条错误消息到errorMessage:

epicsSnprintf(pasynUser->errorMessage, pasynUsser->errorMessageSize, "<format>", ...);

错误消息不应该(不包含)以一个新行字符序列结束(例如,\n)。由用户代码决定是否以及如何显示错误消息。防止错误消息出现新行,使得用户代码嵌入错误消息在另一条消息或输出格式变得简单。

errorMessageSizeerrorMessage的大小。用户不能更改这个值
timeout

在I/O请求的超时前的秒数。这是由用户设置并且在对一个驱动调用期间被修改。如果对一个底层驱动调用导致驱动进行很多I/O请求,这是每个I/O请求的时间。

含义如下:

>0.0:等待I/O结束,最多等待timeout秒

=0.0:执行没有阻塞可以进行的任意I/O。如果没有I/O可以无阻塞的进行,返回timeout错误。

<0.0:无限超时。一直等待结束I/O

userPvt由用户使用。用户一你刚刚在调用pasynManager->createAsynUser后立即设置这个成员。如果在asynUser排队时,这被修改了,结果时未定义的,例如,它会引起崩溃
userData也由用户使用
drvUser一个驱动可以使用这个保存asynUser专用数据。asynDrvUser接口用于asynUser和驱动之间的通信。
reason驱动和asynUsers可以将这个成员用作通用字段。按惯例,它用作确定什么"命令"正在通过一个特定接口被发送。例如,一个实现了asynInt32的A/D驱动可能定义reason=0表示"返回A/D转换",而reason=1可能表示"返回放大器增益"。尤其驱动实现了asynDrvUser接口,并且使用这个成员从这个命令的描述字符串(例如:在本例中"DATA"或"GAIN")转换成枚举"reason"。正在调用一个中断的驱动,用户经常使用reason决定用户回调是否应该被调用。小于0的reason值被保留给了标准含义。例如,ASYN_REASON_SIGNAL用于表示"out of band"请求。devGpib支持使用这个成员报告SRQs。
timestamp提供它们自己时间戳的设备使用这个字段为其TSE字段设为"-2"的记录提供时间值。
auxStatus任何方法在auxStatus中提供其它返回信息。含义由这个方法确定。callbacks可以使用auxStatus在设备支持callback函数中设置记录警报状态。
alarmStatus任何方法在alarmStatus中提供其它返回信息。含义由这个方法确定。callbacks可以使用alarmStatus在设备支持callback函数中设置记录警报状态。
alarmSeveity任何方法在alarmSeverity中提供其它返回信息。含义由这个方法确定。callbacks可以使用alarmSeverity在设备支持callback函数中设置记录警报严重性。

asynInterface

这定义了向asynManager:registerPort或asynManager:interposeInterface注册的接口。

typedef struct asynInterface{
    const char *interfaceType; /* 例如asynCommonType */
    void *pinterface;          /* 例如pasynCommon */
    void *drvPvt;
}asynInterface;

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;

asynManager

1) report:

 void      (*report)(FILE *fp,int details,const char*portName);

报告有关asynManager的状态。如果portName是非NULL,它报告一个特定的端口。如果portName是NULL,则它报告每个注册的端口。它也为每个被报告的端口调用asynCommon:report。

2)createAsynUser

asynUser  *(*createAsynUser)(userCallback process,userCallback timeout);

创建一个asynUser。调用者指定两个回调,process和timeout。这些回调仅由queueRequest的结果被调用。timeout回调是可选的。为errorMessage分配errorMessageSize个字符。存储量不能被修改。如果它不能够分配分配这个存储区,此方法不返回。

3) duplicateAsynUser

asynUser  *(*duplicateAsynUser)(asynUser *pasynUser, userCallback queue,userCallback timeout);

通过调用createAsynUser创建一个asynUser。它接着按如下初始化这个新的asynUser:用从pasynUser获取的值初始化字段timeout, userPvt, userData和drvUser。它的connectDevice状态与pasynUser的相同。

4) freeAsynUser

 asynStatus (*freeAsynUser)(asynUser *pasynUser);

释放一个asynUser。用户必须通过这个调用释放一个asynUser。如果asynUser连接到一个端口,asynManager:disconnect被调用。如果断开出错,这个调用也将出错。asynUser的存储区被保存在一个空闲列表并且在之后调用createAsynUser或dupilicateAsynUser中将被再使用。因而持续调用createAsynUser(或duplicateAsynUser)和freeAsynUser是高效的。

5)memMalloc/memFree

void       *(*memMalloc)(size_t size);
void       (*memFree)(void *pmem,size_t size);

分配和释放内存。memMalloc/memFree维护一个不同大小的空闲列表的集合。因而,需要内存区一小段时间的任何应用程序可以使用memMalloc/memFree分配和释放这个存储区,不会使内存碎片化。传递给memFree的大小必须与再memMalloc调用中指定的值相同。

6) isMultiDevice

asynStatus (*isMultiDevice)(asynUser *pasynUser, const char *portName,int *yesNo);

回调问题"这个端口支持多设备吗?" 在调用connectDevice前,可以调用这个方法。

7)  connectDevice

/* addr = (-1,>=0) => connect to (port,device) */
asynStatus (*connectDevice)(asynUser *pasynUser, const char *portName,int addr);

连接asynUser结构体到由portName, addr指定的设备。端口名与在调用registerPort中指定的侠女沟通。如果这个asynUser已经连接了一个设备,这个调用将出错。如果端口不支持多设备,则忽略addr。connectDevice仅连接这个asynUser到对应portName, addr的端口驱动。端口驱动可能有也有可能没有连接到实际设备。因而,connectDevice和asynCommon:connect完全不同。

单和多设备端口驱动之间的不同,见运行理论的描述。 

8) disconnect

asynStatus (*disconnect)(asynUser *pasynUser); 

从port,addr断开这个asynUser,这个连接是通过之前调用connectDevice连接这个asynUser。如果asynUser排队或被锁定,或者有一个通过exceptionCallback注册的回调,调用将出错。注意asynManager:disconnect和asynCommon:disconnect完全不同。

9)exceptionCallbackAdd

asynStatus (*exceptionCallbackAdd)(asynUser *pasynUser, exceptionCallback callback);

当由asynException定义的异常之一发生时,将调用callback。这个callback可以调用isConnected, isEnabled或isAutoConnect来查找连接状态。asynTrace提供了查明当前跟踪设置的方法。

10)exceptionCallbackRemove

asynStatus (*exceptionCallbackRemove)(asynUser *pasynUser);  

callback被移除。必须在disconnect前,调用这个方法。 

 11)findInterface

asynInterface *(*findInterface)(asynUser *pasynUser, const char *interfaceType,int interposeInterfaceOK);

 查找一个驱动接口。如果interposeInterfaceOK是真,则findInterface返回上个注册的或插入的接口。否则,返回由registerPort注册的接口。如果不支持这个interfaceType,它返回0。

用户需要驱动的接口和pdrvPvt的地址,因而可以从这个驱动进行调用。例如:

asynInterface *pasynInterface;
asynOctet *pasynOctet;
void *pasynOctetPvt;
...
pasynInterface = pasynManager->findInterface(
        pasynUser,asynOctetType,1);
if(!pasynInterface) { /*error do something*/}
pasynOctet = (asynOctet *)pasynInterface->pinterface;
pasynOctetPvt = pasynInterface->pdrvPvt;
...
/* The following call must be made from a callback */
pasynOctet->read(pasynOctetPvt,pasynUser,...

12)queueRequest

asynStatus (*queueRequest)(asynUser *pasynUser,asynQueuePriority priority,double timeout);

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

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

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

如果没有传递timeout回调给createAsynUser,并且一个请求了一个非0 timeout的请求,请求出错。

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

 13)cancelRequest

asynStatus (*cancelRequest)(asynUser *pasynUser,int *wasQueued);

如果一个asynUser排队,从队列移除它。如果在调用cancelRequest时,process或timeout回调是活动的,则cancelRequest在回调结束前将不返回。 

14) blockProcessCallback/unlockProcessCallback

asynStatus (*blockProcessCallback)(asynUser *pasynUser, int allDevices); 
asynStatus (*unblockProcessCallback)(asynUser *pasynUser, int allDevices);

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

  • blockProcessCallback仅对可以阻塞的驱动有作用,并且如果位非阻塞驱动调用它,返回一个错误。
  • 指定asynQueuePriority优先级的queueRequests不能被阻塞。

同时阻塞所有设备和asynUser连接的设备是允许的。

15)lockPort/unlockPort

asynStatus (*lockPort)(asynUser *pasynUser);
asynStatus (*unlockPort)(asynUser *pasynUser);

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

16)queueLockPort/queueUnlockPort

asynStatus (*queueLockPort)(asynUser *pasynUser);
asynStatus (*queueUnlockPort)(asynUser *pasynUser);

锁定对一个端口驱动的访问。这是由当对端口驱动进行调用时愿意阻塞的代码使用。代码可以调用queueLockPort,queueLockPort,对端口驱动进行任意数目调用,并且接着调用queueUnlockPort。调用queueRequest和/或lockPort的其它代码将在queueLockPort和queueUnlockPort调用之间被延时。lockPort和queueLockPort之间的差别是queueLockPort使用与queueRequest相同的队列排队一个锁定端口的请求。这表示一个重复调用queueLockPort而在调用之间没有睡眠的线程将仍然允许其它线程访问端口。对于lockPort不是这样的,lockPort将在端口空闲时立即获取mutex,并且会完全阻止其它线程访问这个端口。

18)setQueueLockPortTimeout

asynStatus (*setQueueLockPortTimeout)(asynUser *pasynUser, double timeout);

设置在queueLockPort()中传递给queueRequest()的超时。当创建端口时,设置默认值2.0秒。这个函数可以用于更改那个值。注意,如果传递给queueLockPort的pasynUser->timeout值大于当前值,则使用这个更大的timeout。

19)canBlock

asynStatus (*canBlock)(asynUser *pasynUser,int *yesNo);

yesNo设置位(0,1),即如果对底层驱动的调用可以阻塞(false, true)。这个值是由传递给registerPort的属性确定的。

20)getAddr

asynStatus (*getAddr)(asynUser *pasynUser,int *addr);

*addr设置为用户在调用connectDevice中指定的地址,或如果端口不支持多设备,-1。单和多设备端口驱动之间的差别,见运行伦理描述。

21)getPortName

asynStatus (*getPortName)(asynUser *pasynUser,const char **pportName);

*pportName设置为用户连接端口的名称。

22)registerPort

  /* drivers call the following*/
asynStatus (*registerPort)(const char *portName, int attributes,int autoConnect, unsigned int priority,unsigned int stackSize);

这个方法由驱动调用。为每个端口实例进行一次调用。attributes是一个位集合。当前定义了两个位:ASYN_MULTIDEVICE和ASYN_CANBLOCK。驱动必须恰当地指定这些,其是(0,1)对应(no, yes),为端口和连接端口地所有设备提供初始值。priority和stacksize仅在ASYN_CANBLOCK=1时才有作用,在此种情况下,asynManager在用epicsThreadCreate()创建线程时,使用这些值。如果priority是0,则将分配默认值epicsThreadPriority。如果stackSize是0, 将分配默认值epicsThreadGetStackSize(epicsThreadStackMedium)。portName参数指定了名称,asyn代码的上层通过这个名称指向这个通信接口实例。registerPort方法对名称参数指向的字符串进行内部复制。

23)registerInterface

asynStatus (*registerInterface)(const char *portName, asynInterface *pasynInterface);

 由端口驱动为每个受到支持的接口调用这个方法。这个方法不复制pasynInterface参数指向的asynInterface。调用者必须在一个位置存储这个接口,为这个端口生命周期保留这个位置。通常通过在'驱动私有'结构体种放置这个asynInterface结构体做这件事。

24)exceptionConnect

asynStatus (*exceptionConnect)(asynUser *pasynUser);

 当且仅当驱动连接一个端口或设备时,这个方法必须被这个驱动调用。

25)exceptionDisconnect

asynStatus (*exceptionDisconnect)(asynUser *pasynUser);

当且仅当驱动从端口或设备断开时,这个方法必须被这个驱动调用。 

26)interposeInterface

/*any code can call the following*/
asynStatus (*interposeInterface)(const char *portName, int addr, asynInterface *pasynInterface, asynInterface **ppPrev);

 这个方法是由客户端代码和端口驱动之间的软件层调用。例如,如果一个设备回显写,则可以创建一个软件模块,它在每次写后发出一次读,并且为接口asynOctet调用InterposeInterface。

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

也可以用先前还未被注册或替代的asynInterface调用interposeInterface。在这种情况,*ppPrev将是null。因而,可以实现底层驱动未知的新接口。

27)enable

asynStatus (*enable)(asynUser *pasynUser,int yesNo);

如果设置enable设为yes,除非队列超时发生,否则queueRequests不出队。 

28)autoConnect

asynStatus (*autoConnect)(asynUser *pasynUser,int yesNo);

当一个用户回调被调度要被调用时,如果autoConnect是true,并且端口或设备没有连接,asynManager调用pasynCommon->connect。详见以下控制流讨论。 

29)isConnected

asynStatus (*isConnected)(asynUser *pasynUser,int *yesNo);

根据端口设备连接有无连接,*yesNo设为(0无,1有) 。

30)isEnabled

asynStatus (*isEnabled)(asynUser *pasynUser,int *yesNo);

根据端口或设备是否使能,*yesNo设为(0否,1是) 。 

31)isAutoConnect

  asynStatus (*isAutoConnect)(asynUser *pasynUser,int *yesNo);

 根据portThread是否将自动连接端口或设备,*yesNo设为(0否,1是)。

32)setAutoConnectTimeout

   asynStatus (*setAutoConnectTimeout)(double timeout);

在等待来自端口驱动的初始连接回调时,更改timeout。这个回调发生响应asynManager排队一个连接请求,当驱动驱动注册它的asynCommon接口时这发生。默认timeout是0.5秒。 

33)waitConnect

  asynStatus (*waitConnect)(asynUser *pasynUser, double timeout);

等待port/device连接,最多timeout秒。 

34)registerInterruptSource

 /*The following are methods for interrupts*/
 asynStatus (*registerInterruptSource)(const char *portName,  asynInterface *pasynInterface, void **pasynPvt);

如果一个底层驱动支持中断,它必须为支持中断的每个接口调用这个方法。pasynPvt必须是一个void *的地址, 由registerInterruptSource给其赋一个值。这个参数被传递给interruptStart和interruptEnd。

35)getInterruptPvt

asynStatus (*getInterruptPvt)(asynUser *pasynUser, const char *interfaceType, void **pasynPvt);

 想要调用createInterrputNode但不知道pasynPvt地址的任何代码通过这个方法可以找到它。调用者必须连接一个设备,即,必须已经调用connectDevice。如果调用者没有连接,getInterruptPvt返回asynError。

36)createInterruptNode/freeInterruptNode

interruptNode *(*createInterruptNode)(void *pasynPvt);
asynStatus    (*freeInterruptNode)(asynUser *pasynUser,interruptNode *pnode);

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

38)addInterruptUser/removeInterruptUser

asynStatus (*addInterruptUser)(asynUser *pasynUser, interruptNode*pinterruptNode);
asynStatus (*removeInterruptUser)(asynUser *pasynUser, interruptNode*pinterruptNode);

实现了registerInterruptUser/cancelInterruptUser的代码必须调用addInterruptUser添加用户到列表或者removeInterruptUser从列表移除用户,否则对interruptStart/interruptEnd的调用将无效。这是一个高效操作,使得用户可以重复调用registerInterruptUser/cancelInterruptUser。如果在中断正在处理时,即在interruptStart/interruptEnd调用之间,调用了这两个方法之一,在调用interruptEnd前,这个调用将阻塞。用于在addInterruptUser调用种指定的asynUser的process回调一定不能调用removeInterruptUser,否则它将永远阻塞。

asynStatus (*removeInterruptUser)(asynUser *pasynUser, interruptNode*pinterruptNode);

37)interruptStart/interruptEnd

asynStatus (*interruptStart)(void *pasynPvt,ELLLIST **plist);
asynStatus (*interruptEnd)(void *pasynPvt);

实现中断的代码是依赖接口的。asynManager提供的唯一服务是用户列表的线程安全实现。当代码想要调用在registerInterruptUser调用种指定的回调,它调用interruptStart获取回调列表。当它结束时,它调用interruptEnd。如果在interruptStart和InterruptEnd调用之间对addInterruptUser/removeInterruptUser进行任何请求,asynManager延时这些请求到interruptEnd被调用。 

38)registerTimeStampeSource

/* Time stamp functions */
asynStatus (*registerTimeStampSource)(asynUser *pasynUser, void *userPvt, timeStampCallback callback);

 注册一个用户定义的时间戳回调函数。

39)unregisterTimeStampSource

asynStatus (*unregisterTimeStampSource)(asynUser *pasynUser);

注销任何用户定义的时间戳回调函数,并且恢复成asynManager种默认的时间戳来源函数,它仅调用epicsTimeGetCurrent()。 

40)updateTimeStamp

asynStatus (*updateTimeStamp)(asynUser *pasynUser);

通过调用默认时间戳来源或者用registerTimeStampSource注册的用户定义的时间戳来源,为端口设置当前时间戳。

41)getTimeStamp

asynStatus (*getTimeStamp)(asynUser *pasynUser, epicsTimeStamp *pTimeStamp);

为这个端口获取当前时间戳,它是由最近一次调用updateTimeStamp返回。 

42)setTimeStamp

asynStatus (*setTimeStamp)(asynUser *pasynUser, const epicsTimeStamp *pTimeStamp);

从传递给这个函数的时间戳值直接为这个端口设置当前时间戳。 

43)strStatus

const char *(*strStatus)(asynStatus status);

返回相应asynStatus值的一个描述字符串。

asynCommon

asynCommon描述了必须由驱动实现的方法。

/* 所有asyn驱动都支持的设备接口 */
#define asynCommonType "asynCommon"
typedef struct  asynCommon {
    void       (*report)(void *drvPvt,FILE *fp,int details);
    /*以下是连接硬件/从硬件断开*/
    asynStatus (*connect)(void *drvPvt,asynUser *pasynUser);
    asynStatus (*disconnect)(void *drvPvt,asynUser *pasynUser);
}asynCommon;
report产生一个有关硬件设备的报告。这是asynCommon方法中唯一不是必须由queueRequest 回调调用或者在lockPort/unlockPort调用之间被调用的方法。
connect连接硬件设备或者通信路径。queueRequest必须指定优先级asynQueuePriorityConnect,
disconnect从硬件设备或通信路径断开。queueRequest必须指定优先级asynQueuePriorityConnect,

asynCommonSyncIO

 asynCommonSyncIO为需要对一个aysn设备执行"同步"操作的软件提供了一个快捷接口,即等待断开可用以及操作结束时阻塞。代码不需要处理回调或者理解asynManager和asynCommon接口的细节。

typedef struct asynCommonSyncIO {
    asynStatus (*connect)(const char *port, int addr, 
                          asynUser **ppasynUser, const char *drvInfo);
    asynStatus (*disconnect)(asynUser *pasynUser);
    asynStatus (*connectDevice)(asynUser *pasynUser);
    asynStatus (*disconnectDevice)(asynUser *pasynUser);
    asynStatus (*report)(asynUser *pasynUser, FILE *fd, int details);
} asynCommonSyncIO;
epicsShareExtern asynCommonSyncIO *pasynCommonSyncIO;

注意:这个接口的connect*和disconnect*中存在混淆的可能。为了与其它SyncIO接口保持一致性,连接调用pasynManager->connectDevice,断开调用pasynManger->disconnect,connectDevice调用asynCommon->connect以及disconnectDevice调用asynCommon->disconnect。

asynDrvUser

asynDrvUser提供了允许一个asynUser和用户特性端口驱动之间传递用户特定信息的方法。

#define asynDrvUserType "asynDrvUser"
typedef struct  asynDrvUser {
    /*The following do not have to be called via queueRequest callback*/
    asynStatus (*create)(void *drvPvt,asynUser *pasynUser,
        const char *drvInfo, const char **pptypeName,size_t *psize);
    asynStatus (*getType)(void *drvPvt,asynUser *pasynUser,
        const char **pptypeName,size_t *psize);
    asynStatus (*destroy)(void *drvPvt,asynUser *pasynUser);
}asynDrvUser;
create

用户,即,设备支持调用create。驱动可以创建它需要的任何资源。它可以使用pasynUser->drvUser提供对这些资源的访问。它asynUser和驱动都知道如何访问这些资源,它们必须在资源名称和尺寸上达成一致。如果pptypeName非null,驱动可以传递一个值给*pptypeName。如果psize非null,驱动可以传递一个值给*psize。除非asynUser接收了一个它知道的typeName和尺寸,否则它一定不能访问asynUser.drvUser。

getType如果其它代码,例如,一个interposeInterface想要访问asynUser.drvUser,它必须调用这个方法,并且确认typeName和size是它所预计的。
destroy销毁由create创建的资源,并且设置asynUser.drvUer为null

asynLockPortNotify

为其是另一个端口驱动的一个aysnUser的端口驱动提供了这个方法。例如,可以通过连接到一个标准串口来执行实际的I/O来实现一个串行总线驱动。当串行总线端口由于请求者调用lockPort或因为queueRequest出队被锁定,则串行总线驱动需要锁定相关联的串口。

串行总线驱动注册接口asynLockPortNotify。当串行总线端口被锁定时,asynManager调用pasynLockPortNotify.lock。串行总线驱动为其连接的串口调用asynManager.lockPort。对于unlockPort类似。因而当串行总线端口被锁定时,串行总线也被锁定。

asynLockPortNotify仅由asynManager使用。它未被放入对应这个端口的接口列表中。

asynLockPortNotify是:

#define asynLockPortNotifyType "asynLockPortNotify"
typedef struct  asynLockPortNotify {
    asynStatus (*lock)(void *drvPvt,asynUser *pasynUser);
    asynStatus (*unlock)(void *drvPvt,asynUser *pasynUser);
}asynLockPortNotify;

asynLockPortNotify 

lock当asynManager.lockPort被调用时,调用。驱动通常为其连接的端口调用asynManger.lockPort
unlock当asynManager.unlockPort被调用时,调用。驱动通常为其连接的端口调用asynManger.unlockPort

asynOption

asynOption提供设置驱动专用选项的通用方式。例如,串口驱动使用这个接口指定波特率,停止位等。

#define asynOptionType "asynOption"
/*The following are generic methods to set/get device options*/
typedef struct asynOption {
    asynStatus (*setOption)(void *drvPvt, asynUser *pasynUser,
                                const char *key, const char *val);
    asynStatus (*getOption)(void *drvPvt, asynUser *pasynUser,
                                const char *key, char *val, int sizeval);
}asynOption;

asynOption

setOption设置与key相关联的值
getOption获取与key相关联的值

Trace接口

/*asynTrace由asynManager实现*/
/*所有asynTrace方法可以被从任何线程调用*/
/* traceMask definitions*/
#define ASYN_TRACE_ERROR     0x0001
#define ASYN_TRACEIO_DEVICE  0x0002
#define ASYN_TRACEIO_FILTER  0x0004
#define ASYN_TRACEIO_DRIVER  0x0008
#define ASYN_TRACE_FLOW      0x0010
#define ASYN_TRACE_WARNING   0x0020

/* traceIO mask definitions*/
#define ASYN_TRACEIO_NODATA 0x0000
#define ASYN_TRACEIO_ASCII  0x0001
#define ASYN_TRACEIO_ESCAPE 0x0002
#define ASYN_TRACEIO_HEX    0x0004

/* traceInfo mask definitions*/
#define ASYN_TRACEINFO_TIME 0x0001
#define ASYN_TRACEINFO_PORT 0x0002
#define ASYN_TRACEINFO_SOURCE 0x0004
#define ASYN_TRACEINFO_THREAD 0x0008

/* asynPrint and asynPrintIO are macros that act like
   int asynPrintSource(asynUser *pasynUser,int reason, __FILE__, __LINE__, const char *format, ... );
   int asynPrintIOSource(asynUser *pasynUser,int reason,
        const char *buffer, size_t len, __FILE__, __LINE__, const char *format, ... );
*/
typedef struct asynTrace {
    /* lock/unlock are only necessary if caller performs I/O other than */
    /* by calling asynTrace methods                                     */
    asynStatus (*lock)(asynUser *pasynUser);
    asynStatus (*unlock)(asynUser *pasynUser);
    asynStatus (*setTraceMask)(asynUser *pasynUser,int mask);
    int        (*getTraceMask)(asynUser *pasynUser);
    asynStatus (*setTraceIOMask)(asynUser *pasynUser,int mask);
    int        (*getTraceIOMask)(asynUser *pasynUser);
    asynStatus (*setTraceInfoMask)(asynUser *pasynUser,int mask);
    int        (*getTraceInfoMask)(asynUser *pasynUser);
    asynStatus (*setTraceFile)(asynUser *pasynUser,FILE *fp);
    FILE       *(*getTraceFile)(asynUser *pasynUser);
    asynStatus (*setTraceIOTruncateSize)(asynUser *pasynUser,size_t size);
    size_t     (*getTraceIOTruncateSize)(asynUser *pasynUser);
#if defined(__GNUC__) && (__GNUC__ < 3)
    /* GCC 2.95 does not allow EPICS_PRINTF_STYLE on function pointers */
    int        (*print)(asynUser *pasynUser,int reason, const char *pformat, ...);
    int        (*printSource)(asynUser *pasynUser,int reason, const char *fileName, int line, const char *pformat, ...);
    int        (*vprint)(asynUser *pasynUser,int reason, const char *pformat, va_list pvar);
    int        (*vprintSource)(asynUser *pasynUser,int reason, const char *file, int line, const char *pformat, va_list pvar);
    int        (*printIO)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *pformat, ...);
    int        (*printIOSource)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *file, int line, const char *pformat, ...);
    int        (*vprintIO)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *pformat, va_list pvar);
    int        (*vprintIOSource)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *file, int line, const char *pformat, va_list pvar);
#else
    int        (*print)(asynUser *pasynUser,int reason, const char *pformat, ...) EPICS_PRINTF_STYLE(3,4);
    int        (*printSource)(asynUser *pasynUser,int reason, const char *fileName, int line, const char *pformat, ...) EPICS_PRINTF_STYLE(5,6);
    int        (*vprint)(asynUser *pasynUser,int reason, const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(3,0);
    int        (*vprintSource)(asynUser *pasynUser,int reason, const char *file, int line, const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(5,0);
    int        (*printIO)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *pformat, ...) EPICS_PRINTF_STYLE(5,6);
    int        (*printIOSource)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *file, int line, const char *pformat, ...) EPICS_PRINTF_STYLE(7,8);
    int        (*vprintIO)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(5,0);
    int        (*vprintIOSource)(asynUser *pasynUser,int reason,
                    const char *buffer, size_t len,const char *file, int line, const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(7,0);
#endif
}asynTrace;
epicsShareExtern asynTrace *pasynTrace;

asynTrace

asynDriver提供了具有以下属性的跟踪功能:

1)对单独设备,即一个portName, addr启用和关闭跟踪。

2)跟踪有一个用于未连接port或port,addr的asynUser的全局跟踪掩码。

3)输出被发送给一个文件或者stdout或者到errlog。

4)掩码确定要被显示的信息类型。不同选项可以被位或在一起。在创建一个端口时,这个掩码的默认值是ASYN_TRACE_ERROR。

  • ASYN_TRACE_ERROR:报告运行时错误,例如超时。
  • ASYN_TRACEIO_DEVICE:设备支持报告I/O活动。
  • ASYN_TRACEIO_FILTER:在设备支持和底层驱动之间的任何层报告它对I/O做的任何过滤。
  • ASYN_TRACE_LOW:报告逻辑流。设备支持应该报告所有队列请求,进入的回调,以及对驱动的所有调用。设备支持和底层驱动之间的层应该它们对底层驱动做的所有调用。底层驱动报告它们对其它支持的调用。
  • ASYN_TRACE_WARNING:报告警告,即,在ASYN_TRACE_ERROR和ASYN_TRACE_LOW之间的情况。

5)另一个掩码确定如何打印消息缓存。各种选项可以按位或到一起。当创建这个一个端口时,这个掩码的默认值是ASYN_TRACEIO_NODATA。

  • ASYN_TRACEIO_NODATA:不要输出来自消息缓存的任何数据。
  • ASYN_TRACEIO_ASCII:用"%s"风格格式打印。
  • ASYN_TRACEIO_ESCAPE:调用epicsStrPrintEscaped
  • ASYN_TRACEIO_HEX:用"%2.2x"打印每个字节。

6) 另一个掩码确定在每条消息开始输出什么信息。各种选项可以按位或到一起。当创建一个端口时,这个掩码的默认值是ASYN_TRACEINFO_TIME。

  • ASYN_TRACEINFO_TIME:打印消息的日期和时间。
  • ASYN_TRACEINFO_PORT:打印[port, add, reason],此处port是端口名,addr是asyn地址,而reason是pasynUser->reason。这些是asyn中3段"寻址"信息。
  • ASYN_TRACEINFO_SOURCE:打印asynPrint或asynPrintIO语句出现位置的文件名和文件编号,即[__FILE__,__LINE__]。
  • ASYN_TRACEINFO_THREAD:打印线程名,线程ID和线程优先级,即[epicsThreadGetNameSelf(), epicsThreadGetIdSelf(), epicsThreadGetPriortitySelf()]。

为了跟踪功能正常执行:设备支持和所有驱动必须使用这个跟踪功能。设备和驱动支持可以直接调用asynTrace方法。提供了asynPrint和asynPrintIO宏,使得设备/驱动支持可以有像这样的调用:

asynPrint(pasynUser,ASYN_TRACE_FLOW,"%s Calling queueRequest\n",someName);

为了发出读或写请求的设备支持或驱动设计了asynPrintIO调用。它们像这样进行调用:

asynPrintIO(pasynUser,ASYN_TRACEIO_DRIVER,data,nchars,"%s nchars %d", someName,nchars);

由asynManager实现了asynTrace方法。这些方法可以被已经创建一个asynUser并且连接了一个设备的任何代码使用。所有方法都可以被任何线程调用。即是,一个应用线程和/或一个portThread。如果一个线程通过调用print或printIO执行所有I/O,则它不需要调用lock或unlock。如果它想要进行它自己的I/O,在任何I/O前,它必须锁定,在之后,解锁。例如:

pasynTrace->lock(pasynUser);
fd = pasynTrace->getTraceFile(pasynUser);
/*perform I/O to fd */
pasynTrace->unlock(pasynUser);

 如果asynUser未连接一个端口,即,还未调用pasynManger->connectDevice,则认为一个"全局"设备。当在connectDevice前调用asynPrint,这是有用的。

asynTrace

lock/unlock这是调用asynTrace.print或asynTrace.printIO而不是asynPrint和asynPrintIO的代码才需要的。print和printIO在执行它们操作时都锁定。get方法不锁定(除了getTraceFile)并且它们是安全的。除了setTraceFile外,set方法不阻塞,由于会发生的最坏是用户gets多了一点或输出少了一点。
。setTraceMask设置跟踪掩码。通常由用户设置,通过一个shell命令或者devTrace设备支持请求它。为一个端口设置跟踪掩码也为连接那个端口的所有设备设置跟踪掩码。
getTraceMask获取跟踪掩码。想要发送跟踪消息的设备支持调用这个方法查看已经请求了什么跟踪选项。
setTraceIOMask设置traceIO掩码。通过由用户设置,通过shell命令或者devTrace设备支持请求它。为一个端口设置traceIO掩码,也为连接那个端口的所有设备设置了traceIO掩码
getTraceIOMask获取traceIO掩码。想要发送其自己IO消息而不是调用asynPrintIO的支持应该调用这个方法并且遵守这个掩码设置。大部分代码需要它。
getTraeInfoMask设置traceInfo掩码。通常由用户设置,通过一个shell命令或者devTrace设备支持请求它。为一个端口设置traceInfo掩码,也为所有连接那个端口的设备设置了traceInfo掩码。
setTraceFile为输出设置要使用的流。一个NULL参数表示使用errlog。通常由用户设置,通过一个shell命令或者通过devTrace设备支持请求它。如果当前输出流不是(NULL, stdout, stderr)中一个,则在使用新流前,关闭当前的输出流。
getTraceInfo获取用于输出的文件描述符。想要发出它自己IO消息而不是调用asynPrintIO的设备支持调用这个方法并且遵守掩码设置。在这种情况下,lock必须首先被调用。大部分代码不需要它。如果返回值是0,则输出应该指向errlog。
setTraceIOTruncateSize确定printIO输出多少数据。在所有情况中,它确定了显示缓存的多少字节。输出的实际字节数取决于traceIO掩码。例如,ASYN_TRACE_IO_HEX导致为每个字节输出3个字符。通常由用户设置,通过一个shell命令或者devTrace设备支持请求它。
getTraceIOTruncateSize获取当前截断大小。由asynPrintIO调用。进行其自身I/O的代码页应该支持traceIO掩码。
print如果reason与当前traceMask按位或到一起非0。为了后向兼容性提供了这个方法。asynPrint宏现在调用printSource()。
printSource如果与当前traceMask按位或在一起不为0,则打印消息。大部分代码应该调用asynPrint来替代调用者而换个方法。这个方法与print()相同,但多了file和line参数。
vprint这与print相同,但使用va_list作为它的最终参数。
vprintSouce与pirntSource相同,但使用va_list作为其最终参数。
printIO如果reason与当前traceMask按位或是非0,输出消息。如果len>0,则使用traceIO掩码和getTraceIOTruncateSize打印缓存。为了后向兼容性提供了这个方法。asynPrintIO宏现在调用pirntIOSource()
printIOSource如果reason与当前traceMask按位或是非0,输出消息。如果len>0,则使用traceIO掩码和getTraceTruncateSize输出缓存。大部分代码应该调用asynPrintIO而不是调用这个方法。这个方法与printIO相同,但多了file和line参数。
vprintIO这与printIO相同,但使用一个va_list作为其最终参数
vprintIOSource这与printIOSource相同,但使用一个va_list作为其最终参数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2174223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于单片机语音智能导盲仪仿真设计

文章目录 前言资料获取设计介绍设计程序具体实现截图设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们…

VulnHub-SickOs1.1靶机笔记

SickOs1.1靶机笔记 概述 Vulnhub的靶机sickos1.1 主要练习从互联网上搜取信息的能力&#xff0c;还考察了对代理使用&#xff0c;目录爆破的能力&#xff0c;很不错的靶机 靶机地址&#xff1a; 链接: https://pan.baidu.com/s/1JOTvKbfT-IpcgypcxaCEyQ?pwdytad 提取码: yt…

AFSim仿真系统 --- 系统简解_02(向导模块)

向导 向导是AFSIM的集成开发环境。它提供了视觉和基于文本的工具&#xff0c;以简化场景的开发和执行。 向导支持嵌入式执行基于文本的WSF应用程序&#xff0c;例如任务和传感器图&#xff0c;并提供快捷方式以方便启动其他WSF视觉应用程序&#xff0c;如Warlock和Mystic。 核…

图解IRF

FW1 配置思路 ① 配置IRF优先级 确认设备的主次 ② 设置批量操作的接口方便后续操作 interface range name fw-irf interface GigabitEthernet1/0/2 to GigabitEthernet1/0/3 ③ 接口 showdown 关闭接口 ④ 创建的IRF 1/1 成员的对应的接口的是 GE1/0/2 GE/1/0/3 ⑤ 开放IRF对…

Mathematica线性优化-单纯形/改善单纯形/内点法

引言 Mathematica提供了多种工具和函数来实现线性优化&#xff0c;这些工具可以处理从简单的线性规划问题到复杂的多变量优化问题&#xff0c;最近运筹学作业要熟悉线性优化的编程方法&#xff0c;我们就使用mathematica进行&#xff1a;所有运行代码都在文章上面的资源中&…

Python | Leetcode Python题解之第435题无重叠区间

题目&#xff1a; 题解&#xff1a; class Solution:def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:if not intervals:return 0intervals.sort(keylambda x: x[1])n len(intervals)right intervals[0][1]ans 1for i in range(1, n):if intervals…

c++速成 01 数据类型与基本运算符

文章目录 前言整型整型短整型长整型无符号整型 浮点型单精度双精度长双精度 变量命名规则&#xff1a;局部变量 全局变量基本运算符算术运算符&#xff1a;赋值运算符比较运算符逻辑运算符位运算符杂项运算符运算符间的优先级 前言 写在前面&#xff1a;本笔记参考b站视频【《…

从零开始手写STL库:Stack

从零开始手写STL库–Stack的实现 Gihub链接&#xff1a;miniSTL 文章目录 从零开始手写STL库–Stack的实现一、stack是什么&#xff1f;二、stack要包含什么函数总结 一、stack是什么&#xff1f; 栈是一种后进先出&#xff08;LIFO&#xff0c;Last In First Out&#xff09…

前端常用动画 直接可以用的代码加详细流程和案例 能应付90%的开发场景

前端项目&#xff0c;特别是Toc的项目&#xff0c;一定少不了各种动效和动画效果。 葫芦七兄弟&#xff1a; CSS 动画 优点&#xff1a;兼容性强&#xff1b;浏览器针对的流畅度优化&#xff1b;语法简单&#xff1b;某些属性&#xff08;如 transform 和 opacity&#xff09;…

CSS 的背景样式

1.1 背景颜色 1.2 背景图片 1.3 背景平铺 1.4 背景图片位置 1.4.1 方位名词 1.4.2 精确单位 1.4.3 混合单位 1.5 背景图像固定 1.6 背景复合写法 1.7 背景色半透明 1.8 总结

Json-Rpc框架(Muduo库快速上手)

阅读导航 引言一、Muduo库简介二、Muduo库常见接口1. TcpServer类基础介绍2. EventLoop类基础介绍3. TcpConnection类基础介绍4. TcpClient类基础介绍5. Buffer类基础介绍 三、Muduo库使用示例⭕英译汉服务器⭕英译汉客户端 引言 在上一篇文章中&#xff0c;我们简要介绍了在项…

业务资源管理模式语言19

相关模式&#xff1a; 如果你考虑类“Resource Maintenance”和“Part used in maintenance”&#xff0c;那么是“Transaction-Transaction Line Item”模式的一个特例[Coa 97]。如果你考虑类“Part”和“Part used in maintenance”&#xff0c;那么是“Item Line Item”模式…

力扣 简单 104.二叉树的最大深度

文章目录 题目介绍解法 题目介绍 解法 如果知道了左子树和右子树的最大深度 l 和 r&#xff0c;那么该二叉树的最大深度即为max(l,r)1&#xff0c;而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用递归的方法来计算二叉树的最大深度。具体而言&#xff…

动态规划(有背包问题)

目录 1.动态规划的介绍 2.动态规划的例题 第1道题 数字三角形 (如果想看递归写法可以到我的记忆化递归里去看看记忆化递归_将递归程序记忆化-CSDN博客) 第2道题最长公共子序列(模板) 第3道题 最长上升子序列 第4道题最大子段和 背包系列问题 01背包 完全背包 1.动态规…

scrapy爬虫基础

一、初识 创建项目&#xff1a; scrapy startproject my_one_project # 创建项目命令 cd my_one_project # 先进去&#xff0c; 后面在里面运行 运行爬虫命令为&#xff1a;scrapy crawl tk spiders下创建test.py 其中name就是scrapy crawl tk &…

LeetCode讲解篇之5. 最长回文子串

文章目录 题目描述题解思路题解代码 题目描述 题目链接 题解思路 从中心点先寻找和中心点相等的左右端点&#xff0c;在基于左右端点进行往外扩散&#xff0c;直至左右端点不相等或者越界&#xff0c;然后左右端点这个范围内就是我们找寻的回文串&#xff0c;我们遍历中心点…

VS Code 配置 Anaconda Python 环境

VS Code 配置 Anaconda Python 环境 董同学是使用 PyCharm 进行 python 开发的老选手了&#xff0c;但同事们都在用 VS Code。为了更好的和大家交流&#xff0c;转身投入 VS Code 的怀抱&#xff08;当然我都要&#xff09;。当我兴致盎然打开 VS Code 软件&#xff0c;真丝滑…

自动化测试实例:Web登录功能性测试(无验证码)

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是自动化测试 把人为驱动的测试行为转化为机器执行的一种过程称为自动化测试。(来自百度百科)本质上来说&#xff0c;自动化测试对比起手工测试除了需要…

自动化check是不是测试?

这篇文章是reddit上关于质量保障讨论的比较多的帖子&#xff0c;我把它转为中文版&#xff0c;供大家交流学习&#xff0c;由于直接用的翻译软件以及截图&#xff0c;大家凑合看下哈。 自动化检查并不是真正的“测试”&#xff1f;编写自动化检查确实很重要——但编写自动化测…

笔记整理—linux进程部分(1)进程终止函数注册、进程环境、进程虚拟地址

对于mian()函数而言&#xff0c;执行前也需要先执行一段引导代码才会去执行main()函数&#xff0c;该部分的代码包含构建c语言的运行环境等配置&#xff0c;如清理bss段等。 在使用gcc去编译程序的时候&#xff0c;使用gcc -v xxx.c可见链接过程。在编译完成后可见xxx.out文件。…