这是一个如何连接asynManager的示例。这个示例放在<top>/testApp中并且包含以下组件:
root@orangepi5:/usr/local/EPICS/program/asynTest/testApp# ls -R
.:
Db Makefile src
./Db:
Makefile test.db
./src:
echoDriver.c echoDriver.dbd Makefile testMain.cpp
echoDriver是一个端口驱动程序,它回应它接收的消息。
它实现了asynCommon和asynOctet。当asynOctet:write被调用时,它保存此条消息。
当asynOctet:read被调用时,保存的消息被返回并且这条小心被清空。
echoDriverInit有一个参数,它确定它行为是否像一个multiDevice或一个单设备端口驱动程序。
通过iocsh命令:
echoDriverInit(portName,delay,noAutoConnect,multiDevice)
启动一个echoDriver的实例。
此处:
- portName:这个实例的端口名。
- delay:在读或写后要延时的时间。如果delay为0,则echoDriver以一个同步设备驱动程序进行注册,即未设置ASYN_CANBLOCK属性。如果delay>0,则设置ASYN_CANBLOCK。
- noAutoConnect:确定端口的初始设置。
- multiDevice:如果true,则它支持地址0和1的两个设备。如果false,它不设置ASYN_MULTIDEVICE,即,它仅支持单个设备。
echoDriver.c的源代码如下:
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <asynDriver.h>
#include <asynOctet.h>
#include <epicsExport.h>
#define BUFFERSIZE 4096
#define NUM_DEVICES 2
typedef struct deviceBuffer {
char buffer[BUFFERSIZE];
size_t nchars;
}deviceBuffer;
typedef struct deviceInfo {
deviceBuffer buffer;
int connected;
}deviceInfo;
/*
typedef struct asynInterface{
const char *interfaceType; 例如asynCommonType
void *pinterface; 例如pasynCommon
void *drvPvt;
}asynInterface;
*/
typedef struct echoPvt {
deviceInfo device[NUM_DEVICES];
const char *portName;
int connected;
int multiDevice;
double delay;
asynInterface common;
asynInterface octet;
char eos[2];
int eoslen;
void *pasynPvt; /*用于注册registerInterruptSource*/
}echoPvt;
/* init routine */
static int echoDriverInit(const char *dn, double delay, int noAutoConnect,int multiDevice);
/*
asynUser:描述一个结构体,用户代码传递此结构体给大多数asynManager和驱动方法。
代码必须调用asynManager:createAsynUser(或asynManager:duplicateAsynUser)和asynMananger:freeAsynUser分配和释放一个asynUser.
typedef struct asynUser {
char *errorMessage;
int errorMessageSize;
double timeout; // 必须由用户设置timeout,用于I/O操作的Timeout
void *userPvt;
void *userData;
void *drvUser; // 驱动程序使用
int reason;
// 以下通常由驱动程序通过asynDrvUser->create()设置
epicsTimeStamp timestamp;
// 以下用于来自方法调用的更多信息
int auxStatus;
int alarmStatus;
int alarmSeverity;
}
*/
/*
产生一个关于硬件设备的报告。
这是唯一的asynCommon方法,它不是必须被queueRequest回调或者在lockPort/unlockPort调用之间调用
*/
static void report(void *drvPvt,FILE *fp,int details);
/*
连接硬件设备或者通信路径。queueRequest必须指定优先级asynQueuePriorityConnect.
*/
static asynStatus connect(void *drvPvt,asynUser *pasynUser);
/*
从硬件设备或者通信路径断开。queueRequest必须指定优先级asynQueuePriorityConnect
*/
static asynStatus disconnect(void *drvPvt,asynUser *pasynUser);
static asynCommon asyn = { report, connect, disconnect };
static asynStatus echoWrite(void *drvPvt,asynUser *pasynUser,
const char *data,size_t numchars,size_t *nbytesTransfered);
static asynStatus echoRead(void *drvPvt,asynUser *pasynUser,
char *data,size_t maxchars,size_t *nbytesTransfered,int *eomReason);
static asynStatus echoFlush(void *drvPvt,asynUser *pasynUser);
static asynStatus setEos(void *drvPvt,asynUser *pasynUser,
const char *eos,int eoslen);
static asynStatus getEos(void *drvPvt,asynUser *pasynUser,
char *eos, int eossize, int *eoslen);
static int echoDriverInit(const char *dn, double delay,
int noAutoConnect,int multiDevice)
{
echoPvt *pechoPvt;
char *portName;
asynStatus status;
size_t nbytes;
int attributes;
asynOctet *pasynOctet;
// 计算所需空间:echoPvt结构体 + asynOctet结构体 + 用于存储传入字符串的字节,并且分配所需空间
nbytes = sizeof(echoPvt) + sizeof(asynOctet) + strlen(dn) + 1;
pechoPvt = callocMustSucceed(nbytes,sizeof(char),"echoDriverInit");
// pasynOctet指向分配空间中asynOctet结构体首地址
pasynOctet = (asynOctet *)(pechoPvt + 1);
// 指向存储传入字符串的首地址
portName = (char *)(pasynOctet + 1);
// 把传入字符串复制到为其准备的空间
strcpy(portName,dn);
// 初始化分配空间中echoPvt结构体中的成员变量
pechoPvt->portName = portName;
pechoPvt->delay = delay;
pechoPvt->multiDevice = multiDevice;
/*
初始化echoPvt结构体中成员变量:
common成员变量:asynInterface结构体,const char * interfaceType, void * pinterface和void * drvPvt
*/
pechoPvt->common.interfaceType = asynCommonType;
pechoPvt->common.pinterface = (void *)&asyn;
pechoPvt->common.drvPvt = pechoPvt;
attributes = 0;
if(multiDevice) attributes |= ASYN_MULTIDEVICE;
if(delay>0.0) attributes|=ASYN_CANBLOCK;
/*
这个方法被驱动程序调用。为每个端口实例调用一次。
属性是一个位集合。当前定义了两个属性:ASYN_MULTIDEVICE和ASYN_CANBLOCK.
驱动程序必须合适地指定这些。autoConnect,其是(0,1)代表(no, yes),为这个端口和连接这个端口的所有设备提供初始值
仅在ASYN_CANBLOCK=1时,优先级和栈大小才有作用,在这种情况下,
在asynManager用epicsThreadCreate()创建这个端口驱动时,它使用这些值。
如果priority是0,则将分配默认值epicsThreadPriorityMedium.
如果stackSize是0,则将分配默认值epicsThreadGetStackSize(epicsThreadStackMedium).
portName参数指定名称,asyn代码的上层通过其指向这个通信接口实例。
registerPort方法进行这个字符串的内部复制到名称参数指向的位置。
*/
status = pasynManager->registerPort(portName,attributes,!noAutoConnect,0,0);
if(status!=asynSuccess) {
printf("echoDriverInit registerDriver failed\n");
return 0;
}
/*
由端口驱动程序为每个支持的接口调用。
这个方法不复制这个aysnInterface到pasynInterface参数指向的地方。
调用者必须在一个为此端口存在期间保留的地方中存储这个asynInterface.
常见通过'driver private'结构体中放置这个asynInterface做这件事。
echoPvt结构体中类型为asynInterface的成员变量common
*/
status = pasynManager->registerInterface(portName,&pechoPvt->common);
if(status!=asynSuccess){
printf("echoDriverInit registerInterface failed\n");
return 0;
}
/* 初始化asynOctet结构体部分:指向echoPvt结构体中asynInterface类型成员变量octet*/
pasynOctet->write = echoWrite;
pasynOctet->read = echoRead;
pasynOctet->flush = echoFlush;
pasynOctet->setInputEos = setEos;
pasynOctet->getInputEos = getEos;
pechoPvt->octet.interfaceType = asynOctetType;
pechoPvt->octet.pinterface = pasynOctet;
pechoPvt->octet.drvPvt = pechoPvt;
/*
在驱动程序调用regisgerPort后,它调用pasynOctetBase->initialize(...)
在asynIntreface中任何null方法被默认实现替代。如果端口不是多设备的并且指定了processEosIn或processEosOut
asynInterposeEosConfig被调用。如果端口不是多设备并且指定了interruptProcess,
则在read被调用时,asynBase调用所有注册的中断用户。如果端口是多设备端口,asynOctetBase不能实现processEosIn
,processEosOut和interruptProcess.
由于此方法仅在初始化过程中才被调用,它能够被直接调用而不是通过queueRequest.
asynStatus (*initialize)(const char *portName,
asynDriverasynInterface *pasynOctetInterface,
int processEosIn,int processEosOut,int interruptProcess);
asynOctetBase是一个对实现了接口asynOctet的驱动程序的接口和实现。
asynOctetBase实现了registerInterruptUser和cancelInterruptUser.
对于单设备支持,它能够可选地实现中断支持。实现了中断地驱动程序必须调用registerInterruptSource.
如果它请求asynOctetBase处理中断,当其有新数据时它调用synOctetBase:callInterruptUsers 。
对于单设备支持, asynOctetBase能够可选地调用asynInterposeEosConfig来为输入和/或输出处理字符串结尾。
在此接口中被传递去初始化地任何null方法被asynOctetBase提供地方法替代。
*/
if(multiDevice) {
status = pasynOctetBase->initialize(portName,&pechoPvt->octet,0,0,0);
} else {
status = pasynOctetBase->initialize(portName,&pechoPvt->octet,1,1,0);
}
if(status==asynSuccess)
/*
如果一个底层驱动程序支持中断,它必须为每个支持中断的接口调用这个方法。
pasynPvt必须是一个void*的地址,其将由registerInterruptSource传给一个值。
这个参数在interruptStart和interruptEnd中传递。
asynStatus (*interruptStart)(void *pasynPvt,ELLLIST **plist);
asynStatus (*interruptEnd)(void *pasynPvt);
*/
status = pasynManager->registerInterruptSource(
portName,&pechoPvt->octet,&pechoPvt->pasynPvt);
if(status!=asynSuccess){
printf("echoDriverInit registerInterface failed\n");
return 0;
}
return(0);
}
/* asynCommon 方法:drvPvt实际传递的是echoPvt的地址 */
static void report(void *drvPvt,FILE *fp,int details)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
int i,n;
// 向指定文件输出:是否多设备,端口是否连接,延时时间,
// 然后输出端口下每个设备信息:是否连接,以及设备中内容
fprintf(fp," echoDriver. "
"multiDevice:%s connected:%s delay = %f\n",
(pechoPvt->multiDevice ? "Yes" : "No"),
(pechoPvt->connected ? "Yes" : "No"),
pechoPvt->delay);
n = (pechoPvt->multiDevice) ? NUM_DEVICES : 1;
for(i=0;i<n;i++) {
fprintf(fp," device %d connected:%s nchars = %d\n",
i,
(pechoPvt->device[i].connected ? "Yes" : "No"),
(int)pechoPvt->device[i].buffer.nchars);
}
}
static asynStatus connect(void *drvPvt,asynUser *pasynUser)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
deviceInfo *pdeviceInfo;
int addr;
asynStatus status;
/*
addr被设置为用户在调用connectDevice中指定的地址或者如果端口不支持多设备,为-1。
*/
status = pasynManager->getAddr(pasynUser,&addr);
if(status!=asynSuccess) return status;//获取设备地址失败
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"%s echoDriver:connect addr %d\n",pechoPvt->portName,addr);
if(!pechoPvt->multiDevice) {//端口非多设备
if(pechoPvt->connected) {//端口已经连接
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:connect port already connected\n",
pechoPvt->portName);
return asynError;
}
/* 模拟连接延时 */
if(pechoPvt->delay>0.0) epicsThreadSleep(pechoPvt->delay*10.);
pechoPvt->connected = 1; //设置端口连接
pechoPvt->device[0].connected = 1; // 设置端口上第一个设备连接
/*
当前仅当驱动程序连接一个端口或设备时,它必须调用这个方法。
*/
pasynManager->exceptionConnect(pasynUser);
return asynSuccess;
}
if(addr<=-1) {//addr小于等于1,表示是单设备,测试这个端口是否已经连接
// 已经连接,则报错,未连接,则设置其连接
if(pechoPvt->connected) {
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:connect port already connected\n",
pechoPvt->portName);
return asynError;
}
/* simulate connection delay */
if(pechoPvt->delay>0.0) epicsThreadSleep(pechoPvt->delay*10);
pechoPvt->connected = 1;
pasynManager->exceptionConnect(pasynUser);
return asynSuccess;
}// addr已经超出了最大设备索引,直接报错
if(addr>=NUM_DEVICES) {
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:connect illegal addr %d\n",pechoPvt->portName,addr);
return asynError;
}
// 由addr获取设备结构体deviceInfo,并且测试此设备其是否已经连接
// 如果此设备已经连接,则报错,未连接,则设置此设备连接
pdeviceInfo = &pechoPvt->device[addr];
if(pdeviceInfo->connected) {
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:connect device %d already connected\n",
pechoPvt->portName,addr);
return asynError;
}
/* 模拟连接延时 */
if(pechoPvt->delay>0.0) epicsThreadSleep(pechoPvt->delay*10.);
pdeviceInfo->connected = 1;
pasynManager->exceptionConnect(pasynUser);
return(asynSuccess);
}
static asynStatus disconnect(void *drvPvt,asynUser *pasynUser)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
deviceInfo *pdeviceInfo;
int addr;
asynStatus status;
// addr被设置为用户在调用connectDevice中指定的地址或者如果端口不支持多设备,为-1。
status = pasynManager->getAddr(pasynUser,&addr);
if(status!=asynSuccess) return status;
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"%s echoDriver:disconnect addr %d\n",pechoPvt->portName,addr);
// 不是多设备端口
if(!pechoPvt->multiDevice) {
if(!pechoPvt->connected) {// 端口未连接,报错
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:disconnect port not connected\n",
pechoPvt->portName);
return asynError;
}
// 端口连接,则设置其断开,设备也设置未断开
pechoPvt->connected = 0;
pechoPvt->device[0].connected = 0;
/*
当且仅当驱动程序从一个端口或设备断开时,它调用这个方法。
*/
pasynManager->exceptionDisconnect(pasynUser);
return asynSuccess;
}
if(addr<=-1) {//表示单设备端口,测试端口是否连接,如果未连接,则报错,否则设置端口断开
if(!pechoPvt->connected) {
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:disconnect port not connected\n",
pechoPvt->portName);
return asynError;
}
pechoPvt->connected = 0;
/*
当且仅当驱动程序从一个端口或设备断开时,它调用这个方法。
*/
pasynManager->exceptionDisconnect(pasynUser);
return asynSuccess;
}
if(addr>=NUM_DEVICES) {// 设备索引超过了最大设备索引,报错
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:disconnect illegal addr %d\n",pechoPvt->portName,addr);
return asynError;
}
pdeviceInfo = &pechoPvt->device[addr]; // 获取addr对应的设备结构体
if(!pdeviceInfo->connected) {//测试这个设备是否连接,未连接则报错,否则设置此设备断开
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:disconnect device %d not connected\n",
pechoPvt->portName,addr);
return asynError;
}
pdeviceInfo->connected = 0;
pasynManager->exceptionDisconnect(pasynUser);
return(asynSuccess);
}
/* asynOctet 方法:将指定内容复制到指定设备内容缓存中 */
static asynStatus echoWrite(void *drvPvt,asynUser *pasynUser,
const char *data,size_t nchars,size_t *nbytesTransfered)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
deviceInfo *pdeviceInfo; //存储设备信息的结构体
deviceBuffer *pdeviceBuffer; //存储设备中内容的结构体
int addr;
asynStatus status;
int i;
// addr被设置为用户在调用connectDevice中指定的地址或者如果端口不支持多设备,为-1。
status = pasynManager->getAddr(pasynUser,&addr);
if(status!=asynSuccess) return status;
// 如果非多设备端口,设置addr=0
if(!pechoPvt->multiDevice) addr = 0;
// 调试信息,端口名称 echoDriver:write addr 获取的设备索引
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"%s echoDriver:write addr %d\n",pechoPvt->portName,addr);
// addr在有效范围外
if(addr<0 || addr>=NUM_DEVICES) {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"addr %d is illegal. Must be 0 or 1",addr);
return asynError;
}
// 根据addr获取deviceInfo结构体
pdeviceInfo = &pechoPvt->device[addr];
// 设备未连接,则产生报错信息,并且返回
if(!pdeviceInfo->connected) {
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:write device %d not connected\n",
pechoPvt->portName,addr);
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"%s echoDriver:write device %d not connected",
pechoPvt->portName,addr);
return asynError;
}
if(pechoPvt->delay>pasynUser->timeout) {
if(pasynUser->timeout>0.0) epicsThreadSleep(pasynUser->timeout);
asynPrint(pasynUser, ASYN_TRACE_ERROR,
"%s echoDriver write timeout\n",pechoPvt->portName);
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"%s echoDriver write timeout",pechoPvt->portName);
return asynTimeout;
}
// 获取设备中内容地址
pdeviceBuffer = &pdeviceInfo->buffer;
if(nchars>BUFFERSIZE) nchars = BUFFERSIZE;
// 复制nchars个data中字节到设备内容缓存
if(nchars>0)
{
memcpy(pdeviceBuffer->buffer,data,nchars);
printf("write to device: %d\n", addr);
for (i = 0; i < nchars; i++){
printf("%c", data[i]);
}
printf("\n");
}
asynPrintIO(pasynUser,ASYN_TRACEIO_DRIVER,data,nchars,
"echoWrite nchars %lu\n",(unsigned long)nchars);
pdeviceBuffer->nchars = nchars;//记录复制的字节数目
if(pechoPvt->delay>0.0) epicsThreadSleep(pechoPvt->delay);
*nbytesTransfered = nchars;
return status;
}
static asynStatus echoRead(void *drvPvt,asynUser *pasynUser,
char *data,size_t maxchars,size_t *nbytesTransfered,int *eomReason)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
deviceInfo *pdeviceInfo;
deviceBuffer *pdeviceBuffer;
char *pfrom,*pto;
char thisChar;
size_t nremaining;
size_t nout = 0;
int addr;
asynStatus status;
if(eomReason) *eomReason=0; //emoReason不指向空,则初始化emoReaso指向位置上为0
if(nbytesTransfered) *nbytesTransfered = 0; // nbytesTransfered不指向空,则初始化nbytesTransfered指向位置上为0
// 获取设备地址
status = pasynManager->getAddr(pasynUser,&addr);
if(status!=asynSuccess) return status;
if(!pechoPvt->multiDevice) addr = 0; //不是多设备端口,addr为0
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"%s echoDriver:read addr %d\n",pechoPvt->portName,addr);
if(addr<0 || addr>=NUM_DEVICES) {//设备地址不再有效范围内,报错
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"addr %d is illegal. Must be 0 or 1",addr);
return(0);
}
// 根据设备地址,获取设备信息结构体deviceInfo
pdeviceInfo = &pechoPvt->device[addr];
if(!pdeviceInfo->connected) {// 设备未连接,返回错误
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:read device %d not connected\n",
pechoPvt->portName,addr);
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"%s echoDriver:read device %d not connected",
pechoPvt->portName,addr);
return asynError;
}
if(pechoPvt->delay>pasynUser->timeout) {//设备延时大于用户指定延时,报超时错误
if(pasynUser->timeout>0.0) epicsThreadSleep(pasynUser->timeout);
asynPrint(pasynUser, ASYN_TRACE_ERROR,
"%s echoDriver read timeout\n",pechoPvt->portName);
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"%s echoDriver read timeout",pechoPvt->portName);
return asynTimeout;
}
// 延时设备指定的延时
if(pechoPvt->delay>0.0) epicsThreadSleep(pechoPvt->delay);
pdeviceBuffer = &pdeviceInfo->buffer;//设备内容缓冲区,为结构体deviceBuffer
nremaining = pdeviceBuffer->nchars; //缓冲区中的字节数
pdeviceBuffer->nchars = 0;
pfrom = pdeviceBuffer->buffer; //字节源
pto = data; //目标地址
printf("read from device: %d\n", addr);
while(nremaining>0 && nout<maxchars) {// 设备缓存中剩余字节数,nout已经复制了多少字节
// 从源地址取一个字节存入目标地址,并把当前字节赋给thisChar, 输出字节数目增加1,剩余字节数目减1
thisChar = *pto++ = *pfrom++; nremaining--; nout++;
printf("%c", thisChar);
if(pechoPvt->eoslen>0) {
if(thisChar==pechoPvt->eos[0]) {// 测试当前字节是否是结束字符
if(pechoPvt->eoslen==1) {// 如果当前字节是结束字符,则结束原因位或ASYN_EOM_EOS,退出复制过程
if(eomReason) *eomReason |= ASYN_EOM_EOS;
break;
}
if(nremaining==0) {// 源地址中字节已经被全部复制
if(eomReason) *eomReason |= ASYN_EOM_CNT;
break;
}
if(*pfrom==pechoPvt->eos[1]) {//两个结束字节都符合
*pto++ = *pfrom++; nremaining--; nout++; //在从源地址复制一个字节到目标地址
if(eomReason) {// 结束原因按位或ASYN_EOM_EOS,如果剩余字节为0,再位或ASYN_EOM_CNT
*eomReason |= ASYN_EOM_EOS;
if(nremaining==0) *eomReason |= ASYN_EOM_CNT;
break;
}
}
}
}
}
printf("\n");
// 设置实际输出字节数目
if(nbytesTransfered) *nbytesTransfered = nout;
if(eomReason) {//输出字节数目大于等于设定输出的最大字节,则结束原因带上ASYN_EOM_CNT
if(*nbytesTransfered>=maxchars) *eomReason |= ASYN_EOM_CNT;
if(nremaining==0) *eomReason |= ASYN_EOM_END;//如果剩余字节数目为零,则结束原因带上ASYN_EOM_END
}
/*
void (*callInterruptUsers)(asynUser *pasynUser,void *pasynPvt,
char *data,size_t *nbytesTransfered,int *eomReason);
调用通过registerInterruptUser注册的回调。
*/
pasynOctetBase->callInterruptUsers(pasynUser,pechoPvt->pasynPvt,
data,nbytesTransfered,eomReason);
asynPrintIO(pasynUser,ASYN_TRACEIO_DRIVER,data,nout,
"echoRead nbytesTransfered %lu\n",(unsigned long)*nbytesTransfered);
return status;
}
static asynStatus echoFlush(void *drvPvt,asynUser *pasynUser)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
deviceInfo *pdeviceInfo;
deviceBuffer *pdeviceBuffer;
int addr;
asynStatus status;
// 获取设备地址
status = pasynManager->getAddr(pasynUser,&addr);
if(status!=asynSuccess) return status;
if(!pechoPvt->multiDevice) addr = 0;//端口为单设备
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"%s echoDriver:flush addr %d\n",pechoPvt->portName,addr);
if(addr<0 || addr>=NUM_DEVICES) {//设备索引在有效范围外
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"addr %d is illegal. Must be 0 or 1",addr);
return(0);
}
//通过addr获取devcieInfo结构体
pdeviceInfo = &pechoPvt->device[addr];
if(!pdeviceInfo->connected) {
asynPrint(pasynUser,ASYN_TRACE_ERROR,
"%s echoDriver:flush device %d not connected\n",
pechoPvt->portName,addr);
return -1;
}
// 获取缓存deviceBuffer,并且设置其nchar=0
pdeviceBuffer = &pdeviceInfo->buffer;
asynPrint(pasynUser,ASYN_TRACE_FLOW,
"%s echoFlush\n",pechoPvt->portName);
pdeviceBuffer->nchars = 0;
return(asynSuccess);
}
static asynStatus setEos(void *drvPvt,asynUser *pasynUser,
const char *eos,int eoslen)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
int i;
if(eoslen>2 || eoslen<0) {
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
"setEos illegal eoslen %d",eoslen);
return(asynError);
}
pechoPvt->eoslen = eoslen;
for(i=0; i<eoslen; i++) pechoPvt->eos[i] = eos[i];
asynPrint(pasynUser,ASYN_TRACE_FLOW, "%s setEos\n",pechoPvt->portName);
return(asynSuccess);
}
static asynStatus getEos(void *drvPvt,asynUser *pasynUser,
char *eos, int eossize, int *eoslen)
{
echoPvt *pechoPvt = (echoPvt *)drvPvt;
int i;
*eoslen = pechoPvt->eoslen;
for(i=0; i<*eoslen; i++) eos[i] = pechoPvt->eos[i];
asynPrint(pasynUser,ASYN_TRACE_FLOW, "%s setEos\n",pechoPvt->portName);
return(asynSuccess);
}
/* 注册:echoDriverInit*/
/*
echoDriverInit需要四个参数,定义参数名称以及参数类型
*/
static const iocshArg echoDriverInitArg0 = { "portName", iocshArgString };
static const iocshArg echoDriverInitArg1 = { "delay", iocshArgDouble };
static const iocshArg echoDriverInitArg2 = { "disable auto-connect", iocshArgInt };
static const iocshArg echoDriverInitArg3 = { "multiDevice", iocshArgInt };
static const iocshArg *echoDriverInitArgs[] = {
&echoDriverInitArg0,&echoDriverInitArg1,
&echoDriverInitArg2,&echoDriverInitArg3};
// 函数定义:初始化函数名称,需要的参数数目,参数数组
static const iocshFuncDef echoDriverInitFuncDef = {
"echoDriverInit", 4, echoDriverInitArgs};
// 函数调用
static void echoDriverInitCallFunc(const iocshArgBuf *args)
{
echoDriverInit(args[0].sval,args[1].dval,args[2].ival,args[3].ival);
}
static void echoDriverRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
// 注册:函数定义,函数调用
iocshRegister(&echoDriverInitFuncDef, echoDriverInitCallFunc);
}
}
epicsExportRegistrar(echoDriverRegister);
需要加载的test.db文件内容如下:
record(calc,"$(P)$(R)_P$(PORT)_A$(A)_calc") {
field(DESC, "Counter")
field(SCAN,"Passive")
field(CALC, "(A<99)?(A+1):0")
field(INPA,"$(P)$(R)_P$(PORT)_A$(A)_calc NPP NMS")
field(FLNK,"$(P)$(R)_P$(PORT)_A$(A)_so")
field(EGU, "Counts")
field(HOPR, "10")
field(FLNK,"$(P)$(R)_P$(PORT)_A$(A)_so")
}
record(stringout,"$(P)$(R)_P$(PORT)_A$(A)_so") {
field(DOL,"$(P)$(R)_P$(PORT)_A$(A)_calc NPP NMS")
field(OMSL,"closed_loop")
field(FLNK,"$(P)$(R)_P$(PORT)_A$(A)_si")
}
record(stringin,"$(P)$(R)_P$(PORT)_A$(A)_si") {
field(DTYP,"asynOctetWriteRead")
field(INP,"@asyn($(PORT),$(A)) $(P)$(R)_P$(PORT)_A$(A)_so")
}
record(stringout, "$(P)$(R)_P$(PORT)_A$(A)_writetodevice")
{
field(OMSL, "supervisory")
field(DTYP, "asynOctetWrite")
field(OUT, "@asyn($(PORT),$(A)) $(P)$(R)_P$(PORT)_A$(A)_writestringtodevice")
field(FLNK,"$(P)$(R)_P$(PORT)_A$(A)_readfromdevice")
}
record(stringin, "$(P)$(R)_P$(PORT)_A$(A)_readfromdevice")
{
field(INP,"@asyn($(PORT),$(A)) $(P)$(R)_P$(PORT)_A$(A)_readfromdevice")
field(DTYP, "asynOctetRead")
}
为了便于理解,画了一下这个程序中使用到结构体的示意图:
编译文件后,设置启动文件st.cmd,内容如下:
#!../../bin/linux-aarch64/test
#- You may have to change test to something else
#- everywhere it appears in this file
< envPaths
cd "${TOP}"
## Register all support components
dbLoadDatabase "dbd/test.dbd"
test_registerRecordDeviceDriver pdbbase
asynSetAutoConnectTimeout(.5)
echoDriverInit("A",0.05,0,0)
## Load record instances
#dbLoadRecords("db/test.db","user=orangepi")
dbLoadRecords("${ASYN}/db/asynRecord.db","P=asyn,R=Record_PA_A0,PORT=A,ADDR=0,OMAX=0,IMAX=0")
dbLoadRecords("db/test.db","P=test,R=Client,PORT=A,A=0")
cd "${TOP}/iocBoot/${IOC}"
iocInit
启动这个IOC,执行dbl查看这个IOC中加载的记录:
epics> dbl
asynRecord_PA_A0
testClient_PA_A0_calc
testClient_PA_A0_si
testClient_PA_A0_readfromdevice
testClient_PA_A0_so
testClient_PA_A0_writetodevice
进行测试,向设备写入一个字符数组:
orangepi@orangepi4-lts:/usr/local/EPICS/synApps/support/asyn/testApp/Db$ caput testClient_PA_A0_writetodevice "helloworld"
Old : testClient_PA_A0_writetodevice
New : testClient_PA_A0_writetodevice helloworld
ioc shell中显示如下:
epics> write to device: 0
helloworld
read from device: 0
helloworld
然后查看testClient_PA_A0_readfromdevice:
orangepi@orangepi4-lts:/usr/local/EPICS/synApps/support/asyn/testApp/Db$ caget testClient_PA_A0_readfromdevice
testClient_PA_A0_readfromdevice helloworld