EPICS asyn测试程序--如何连接asynManager

news2024/11/17 16:28:44

这是一个如何连接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

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

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

相关文章

3.Java面试题—JVM基础、内存管理、垃圾回收、JVM 调优

本文目录如下&#xff1a; 七、JVM一、JVM 基础什么是Java虚拟机?Java对象 创建过程?对象有哪几种引用&#xff1f; 二、内存管理说⼀下 JVM 的内存区域 (堆 和 栈)&#xff1f;内存溢出 和 内存泄漏 是什么意思&#xff1f;内存泄漏可能由哪些原因导致呢&#xff1f;Java 堆…

OceanMind海睿思助力南京钢铁苏粮集团入选中国信通院审计“领航”案例

近日&#xff0c;由中国内部审计协会指导&#xff0c;中国信息通信研究院主办的2023年内部审计数字化转型“领航”案例征集结果公布。 其中&#xff0c;中新赛克海睿思参与建设的《南京钢铁-基于数字化审计为核心的全域风控管理》入选标杆案例&#xff0c;《苏粮集团-风控内控合…

Vue中使用echarts

1 安装 npm install -g cnpm --registryhttps://registry.npm.taobao.org cnpm install echarts -S 2 main.js引入 3 引入成功

C++ 成员初始化列表

如果数据成员是常量或者引用的情况&#xff1a; 1.常量和引用不可以在构造函数中进行赋值&#xff1a; #include<iostream> using namespace std; class A { public:A(int i 0){m_i 10;m_j 30; //error 这一句代码叫做赋初值m_k m_i; //error} private:int m_i;/…

openGauss学习笔记-23 openGauss 简单数据管理-时间/日期函数和操作符

文章目录 openGauss学习笔记-23 openGauss 简单数据管理-时间/日期函数和操作符23.1 时间日期操作符23.2 时间/日期函数23.3 TIMESTAMPDIFF23.4 EXTRACT23.5 date_part openGauss学习笔记-23 openGauss 简单数据管理-时间/日期函数和操作符 23.1 时间日期操作符 用户在使用时…

Appium+python自动化(三十三)- 本地调试测试代码,远程控制测试环境-Remote(超详解)

简介 在前边所有涉及启动app的时候有这样一行代码driver webdriver.Remote(http://127.0.0.1:4723/wd/hub, desired_caps)&#xff0c;很多小伙伴们和同学们不知道这个ip和端口哪里来的&#xff0c;我觉得有必要给小伙伴解释一下&#xff0c;于是决定写一篇关于这个appium的服…

直播美颜技术的未来:美型SDK的前沿探索

一、时代背景 随着社交媒体和视频直播的兴起&#xff0c;直播美颜技术的需求日益增长。从简单的滤镜效果到人脸美颜算法的应用&#xff0c;这一领域一直在迅速发展。然而&#xff0c;美型SDK的问世标志着直播美颜技术的新篇章&#xff0c;它以更加精细化的处理和更强大的功能&…

使用win10专业版自带远程桌面公司内网电脑

在现代社会中&#xff0c;各类电子硬件已经遍布我们身边&#xff0c;除了应用在个人娱乐场景的消费类电子产品外&#xff0c;各项工作也离不开电脑的帮助&#xff0c;特别是涉及到数据采集和储存的场景&#xff08;如安保监控、自动化流程等等&#xff09;&#xff0c;更是离不…

大数据课程D4——hadoop的MapReduce

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解MapReduce的作用和特点&#xff1b; ⚪ 掌握MapReduce的组件&#xff1b; ⚪ 掌握MapReduce的Shuffle&#xff1b; ⚪ 掌握MapReduce的小文件问题&#xff1b; ⚪…

Vue项目引入图片的两种场景和三种方式

场景一&#xff1a;public目录下的图片 public目录 public目录下的图片引入方式&#xff1a; Bash <!--/images/图片名称&#xff0c;这种属于绝对路径&#xff0c;/指向public目录 --> <img src"/images/image.png"> 场景二&#xff1a;在src目录…

【Spring】更简单的读取和存储对象,五大类注解

经过前面的学习&#xff0c;我们已经可以实现基本的 Spring 读取和存储对象的操作了&#xff0c;但在操作的过程中我们发现读取和存储对象并没有想象中的那么 “简单”&#xff0c;所以接下来我们要学习更加简单的操作 Bean 对象的方法 在 Spring 中想要更简单的存储和读取对象…

25.8 matlab里面的10中优化方法介绍—— 拉各朗日乘子法求最优化解(matlab程序)

1.简述 拉格朗日乘子法&#xff1a; 拉格朗日乘子法&#xff08;Lagrange multipliers&#xff09;是一种寻找多元函数在一组约束下的极值的方法。通过引入拉格朗日乘子&#xff0c;可将有 变量与 约束条件的最优化问题转化为具有变量的无约束优化问题求解 举个例子&#xff…

YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)

YOLOv5&#xff1a;使用7.0版本训练自己的实例分割模型&#xff08;车辆、行人、路标、车道线等实例分割&#xff09; 前言前提条件相关介绍使用YOLOv5-7.0版本训练自己的实例分割模型YOLOv5项目官方源地址下载yolov5-7.0版源码解压目录结构 准备实例分割数据集在./data目录下&…

使用python库uvicorn替代Nginx发布Vue3项目

目录 一、Vue3项目打包 二、将打包文件放到python项目 三、配置uvicorn服务 四、启动服务 【SpringBoot版传送门&#xff1a;使用SpringBoot替代Nginx发布Vue3项目_苍穹之跃的博客-CSDN博客】 一、Vue3项目打包 &#xff08;博主vue版本&#xff1a;3.2.44&#xff09; 由…

论文精读之BERT

目录 1.摘要&#xff08;Abstract&#xff09; 2.引言&#xff08;Introduction&#xff09;&#xff1a; 3.结论&#xff08;Conlusion&#xff09;&#xff1a; 4.BERT模型算法: 5.总结 1.摘要&#xff08;Abstract&#xff09; 与别的文章的区别是什么:BERT是用来设计去…

Acwing.285 没有上司的舞会(动态规划)

题目 Ural大学有N名职员&#xff0c;编号为1~N。 他们的关系就像—棵以校长为根的树&#xff0c;父节点就是子节点的直接上司。每个职员有一个快乐指数&#xff0c;用整数H给出&#xff0c;其中1≤i≤N。 现在要召开一场周年庆宴会&#xff0c;不过&#xff0c;没有职员愿意和…

js通过下标获取对象值

var a {1: a,2: b,3: c,4: d}var keys Object.keys(a)var values Object.values(a)console.log(keys , values)# 建议使用 console.log(Object.keys(a)[2] : Object.values(a)[2])#无意义 console.log(Object.keys(a)[3] : a[Object.keys(a)[3]])

下级平台级联视频汇聚融合平台EasyCVR,层级显示不正确的原因排查

视频汇聚平台安防监控EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等…

CAN转EtherNet/IP网关can协议是什么意思

你是否曾经遇到过不同的总线协议难以互相通信的问题&#xff1f;远创智控的YC-EIP-CAN网关为你解决了这个烦恼&#xff01; 远创智控YC-EIP-CAN通讯网关是一款自主研发的设备&#xff0c;它能够将各种CAN总线和ETHERNET/IP网络连接起来&#xff0c;解决不同总线协议之间的通信…

C语言队列实现参考示例

C语言队列实现参考示例 目录 C语言队列实现参考示例前言1 代码实现1.1 实现方案1.2 代码编写 结束语 前言 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;…