asyn queueRequest使用实例

 使用queueRequest读写端口驱动的示例,驱动驱动程序使用一个基于asyn实现了asynCommon和asynOctet的驱动程序-****博客中编写的驱动程序,本程序的C代码如下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <cantProceed.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsStdio.h>
#include <epicsAssert.h>
#include <asynDriver.h>
#include <asynOctet.h>
#include <iocsh.h>
#include <registryFunction.h>
#include <epicsExport.h>

#define BUFFSIZE 80
/* 定义一个结构体:事件ID在驱动程序可以阻塞时,才有作用,一个asynOcte接口,驱动专用数据以及数据缓存*/
typedef struct MyData{
    epicsEventId     done;
    asynOctet       *pasynOctet;
    void            *drvPvt;
    char            buffer[BUFFSIZE];
    int             id;
}MyData;

static void timeoutCallback(asynUser *pasynUser){
        printf("in timeoutCallback\n");
}

/* queueRequest的回调函数,原型:typedef void (*userCallback)(asynUser *pasynUser);  */
static void queueCallback(asynUser *pasynUser)
{
    MyData      *pmydata    = pasynUser->userPvt;       //用户数据区
    asynOctet   *pasynOctet = pmydata->pasynOctet;      //asynOcte接口
    void        *pdrvPvt    = pmydata->drvPvt;          //驱动程序专用
    asynStatus  status;
    size_t      writeBytes, readBytes;
    int         eomReason;

    asynPrint(pasynUser,    ASYN_TRACE_FLOW,    "queueCallback entered for Data ID: %d\n", pmydata->id);

    //将用户传入的数据写入设备
    printf("Start to  Write '%s' to device\n", pmydata->buffer);
    /*
        原型:asynStatus (*write)(void *drvPvt,asynUser *pasynUser,
                    const char *data,size_t numchars,size_t *nbytesTransfered);
    */
    status = pasynOctet->write(pdrvPvt, pasynUser, pmydata->buffer, strlen(pmydata->buffer), &writeBytes);

    if (status != asynSuccess){
        asynPrint(pasynUser, ASYN_TRACE_ERROR, "queueCallback write failed:%s\n"  ,pasynUser->errorMessage);
    }
    else{
        asynPrintIO(pasynUser, ASYN_TRACEIO_DEVICE, pmydata->buffer, strlen(pmydata->buffer),
                    "queueCallback write sent %lu bytes\n", (unsigned long)writeBytes);
        printf("finished writing!\n");
    }

    // 清空用户缓存中的数据,为了验证回读的数据
    memset(pmydata->buffer, 0, BUFFSIZE);

    //  从设备读取数据到用户传入的缓存
    //  asynStatus (*read)(void *drvPvt,asynUser *pasynUser,
    //               char *data,size_t maxchars,size_t *nbytesTransfered,
    //               int *eomReason);
    printf("Start to read from device\n");
    status = pasynOctet->read(pdrvPvt, pasynUser, pmydata->buffer, BUFFSIZE, &readBytes, &eomReason);

    if (status != asynSuccess){
        asynPrint(pasynUser, ASYN_TRACE_ERROR, "queueCallback read failed %s\n",  pasynUser->errorMessage);
    }
    else{
        asynPrintIO(pasynUser, ASYN_TRACEIO_DEVICE, pmydata->buffer, BUFFSIZE, "queueCallback read returned: retlen %lu eomReason 0x%x data %s\n",
                    (unsigned long)readBytes, eomReason, pmydata->buffer);
        printf("Finished reading,content: '%s'\n", pmydata->buffer);
    }

    // 如果驱动程序可阻塞,需要对阻塞线程发送信号,表示数据已经处理完成
    if (pmydata->done){
        epicsEventSignal(pmydata->done);
    }
    else{
        pasynManager->memFree(pasynUser->userPvt, sizeof(MyData));
    }

    status = pasynManager->freeAsynUser(pasynUser);

    if (status){
        asynPrint(pasynUser, ASYN_TRACE_ERROR, "freeAsynUser failed %s\n", pasynUser->errorMessage);
    }
}

static void asynQueueRequest(const char *port, int addr, const char *message)
{
        MyData                  *pmyData1,*pmyData2;
        asynUser                *pasynUser, *pasynUserDuplicate;
        asynStatus              status;
        asynInterface           *pasynInterface;
        int                     canBlock;

        // 分配两个结构体,并且初始化这两个结构体
        pmyData1 = (MyData *)pasynManager->memMalloc(sizeof(MyData));
        pmyData2 = (MyData *)pasynManager->memMalloc(sizeof(MyData));
        memset(pmyData1, 0, sizeof(MyData));
        memset(pmyData2, 0, sizeof(MyData));
        // 字符串传入第一个结构体缓存中
        strcpy(pmyData1->buffer,  message);
        pmyData1->id = 1;

        // 使用queueRequest需要创建asynUser结构体,并且放入回调程序
        pasynUser = pasynManager->createAsynUser(queueCallback,  NULL);
        pasynUser->timeout = 1.0;
        pasynUser->userPvt = pmyData1;

        // 通过port和addr连接驱动程序和使用的asynUser
        status = pasynManager->connectDevice(pasynUser, port, addr);
        if (status != asynSuccess){
                printf("can't connect to port:%s\n", pasynUser->errorMessage);
                return;
        }

        // 根据接口名称,查找对应的接口
        /*
            typedef struct asynInterface{
                const char *interfaceType;
                void *pinterface;
                void *drvPvt;
            }asynInterface;
        */
        pasynInterface = pasynManager->findInterface(pasynUser, asynOctetType, 1);
        if (!pasynInterface){
                printf("this driver do not support interface: %s\n", asynOctetType);
                return;
        }

        // 获取asynOctet接口起始位置
        pmyData1->pasynOctet = (asynOctet *)pasynInterface->pinterface;
        // 获取驱动程序专用数据
        pmyData1->drvPvt = pasynInterface->drvPvt;

        // 结构体复制
        *pmyData2 = *pmyData1;
        pmyData2->id = 2;
        strcat(pmyData2->buffer, " repeated!");

        // 获取驱动程序是否可以阻塞
        canBlock = 0;
        pasynManager->canBlock(pasynUser, &canBlock);

        if (canBlock) pmyData2->done = epicsEventCreate(epicsEventEmpty);
        pasynUserDuplicate = pasynManager->duplicateAsynUser(
                        pasynUser, queueCallback, NULL);
        pasynUserDuplicate->userPvt = pmyData2;

        // 排队两个请求
        status = pasynManager->queueRequest(pasynUser, asynQueuePriorityLow, 0.0);
        if (status){
                asynPrint(pasynUser, ASYN_TRACE_ERROR, "First queueRequest failed %s\n", pasynUser->errorMessage);
        }
        status = pasynManager->queueRequest(pasynUserDuplicate, asynQueuePriorityLow,  0.0);
        if (status){
                asynPrint(pasynUser, ASYN_TRACE_ERROR, "Second queueRequest failed %s\n", pasynUser->errorMessage);
        }

        if (canBlock){
                // 读写端口驱动的操作是在另外一个线程中进行,等待那个线程发送数据操作结束的事件
                epicsEventWait(pmyData2->done); // 数据2已经处理好了
                epicsEventDestroy(pmyData2->done);
                pasynManager->memFree(pmyData2, sizeof(MyData));
        }
}

/* 向IOC shell注册 */
static const iocshArg asynQueueRequestArg0 = {"port", iocshArgString};
static const iocshArg asynQueueRequestArg1 = {"addr", iocshArgInt};
static const iocshArg asynQueueRequestArg2 = {"message", iocshArgString};
static const iocshArg *const asynQueueRequestArgs[] = {
        &asynQueueRequestArg0, &asynQueueRequestArg1, &asynQueueRequestArg2
};
static const iocshFuncDef asynQueueRequestDef = {"asynQueueRequest", 3, asynQueueRequestArgs};

static void asynQueueRequestCall(const iocshArgBuf * args)
{
        asynQueueRequest(args[0].sval, args[1].ival, args[2].sval);
}

static void asynQueueRequestRegister(void)
{
        static int firstTime = 1;
        if (!firstTime) return;
        firstTime = 0;
        iocshRegister(&asynQueueRequestDef, asynQueueRequestCall);
}

epicsExportRegistrar(asynQueueRequestRegister);

添加一个文件asynQueueRequest.dbd,内容如下:

registrar("asynQueueRequestRegister")

更改同一路径下的Makefile文件:

...
echoDriver_DBD += asynQueueRequest.dbd

echoDriver_SRCS += asynQueueRequest.c
...

编译以上文件,编写启动脚本:

...
# 可以阻塞驱动程序,模拟单设备端口
echoDriverInit("SIM",  0.1, 0, 0)
...

启动IOC,测试以上asynQueueRequest命令读写为名SIM的端口驱动:

可以看出客户端进行了两次写入-回读操作。

epics> asynQueueRequest "SIM" 0 "hello world"
Start to  Write 'hello world' to device
finished writing!
Start to read from device
Finished reading,content: 'hello world'
Start to  Write 'hello world repeated!' to device
finished writing!
Start to read from device
Finished reading,content: 'hello world repeated!'

使用asynOctet接口的窗口进行测试,直接运行Read一次,可以从Input ASCII文本框中读取了驱动程序回传的数据,与最后一次写入驱动的数据相同。

上一篇:一套医药订单管理系统。该系统旨在通过数字化手段,实现医药订单的自动化处理、库存精准管理、供应链高效协同,以及数据驱动的决策支持。


下一篇:用C语言编写一个函数