使用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文本框中读取了驱动程序回传的数据,与最后一次写入驱动的数据相同。