diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index d40205f4a..005edce3d 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -983,10 +983,10 @@ static long dbPutFieldLink(DBADDR *paddr, dbRemoveLink(plink); break; + case PV_LINK: case CONSTANT: break; /* do nothing */ - case PV_LINK: case MACRO_LINK: break; /* should never get here */ @@ -1031,7 +1031,7 @@ static long dbPutFieldLink(DBADDR *paddr, break; case CONSTANT: - break; + break; /* do nothing */ case DB_LINK: case CA_LINK: diff --git a/src/ioc/db/dbCAC.h b/src/ioc/db/dbCAC.h index 9f297b8cc..d4949e568 100644 --- a/src/ioc/db/dbCAC.h +++ b/src/ioc/db/dbCAC.h @@ -55,7 +55,8 @@ #include "db_convert.h" #include "resourceLib.h" -extern "C" void putNotifyCompletion ( putNotify *ppn ); +extern "C" int putNotifyPut ( processNotify *ppn, notifyPutType notifyPutType ); +extern "C" void putNotifyCompletion ( processNotify *ppn ); class dbContext; class dbChannelIO; diff --git a/src/ioc/db/dbCommon.dbd b/src/ioc/db/dbCommon.dbd index d940999e7..981e3b01a 100644 --- a/src/ioc/db/dbCommon.dbd +++ b/src/ioc/db/dbCommon.dbd @@ -171,16 +171,16 @@ extra("struct asgMember *asp") } field(PPN,DBF_NOACCESS) { - prompt("addr of PUTNOTIFY") + prompt("pprocessNotify") special(SPC_NOMOD) interest(4) - extra("struct putNotify *ppn") + extra("struct processNotify *ppn") } field(PPNR,DBF_NOACCESS) { - prompt("pputNotifyRecord") + prompt("pprocessNotifyRecord") special(SPC_NOMOD) interest(4) - extra("struct putNotifyRecord *ppnr") + extra("struct processNotifyRecord *ppnr") } field(SPVT,DBF_NOACCESS) { prompt("Scan Private") diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index 66974fa77..f20cab6b7 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -1,10 +1,9 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -47,39 +46,39 @@ #include "epicsTime.h" #include "cantProceed.h" -/*putNotify.state values */ +/*notify state values */ typedef enum { - putNotifyNotActive, - putNotifyWaitForRestart, - putNotifyRestartCallbackRequested, - putNotifyRestartInProgress, - putNotifyPutInProgress, - putNotifyUserCallbackRequested, - putNotifyUserCallbackActive -} putNotifyState; + notifyNotActive, + notifyWaitForRestart, + notifyRestartCallbackRequested, + notifyRestartInProgress, + notifyProcessInProgress, + notifyUserCallbackRequested, + notifyUserCallbackActive +} notifyState; /*structure attached to ppnr field of each record*/ -typedef struct putNotifyRecord { +typedef struct processNotifyRecord { ellCheckNode waitNode; - ELLLIST restartList; /*list of putNotifys to restart*/ + ELLLIST restartList; /*list of processNotifys to restart*/ dbCommon *precord; -} putNotifyRecord; +} processNotifyRecord; #define MAGIC 0xfedc0123 -typedef struct putNotifyPvt { - ELLNODE node; /*For free list*/ - long magic; - short state; - CALLBACK callback; - ELLLIST waitList; /*list of records for current putNotify*/ - short cancelWait; - short userCallbackWait; +typedef struct notifyPvt { + ELLNODE node; /*For free list*/ + long magic; + short state; + CALLBACK callback; + ELLLIST waitList; /*list of records for current processNotify*/ + short cancelWait; + short userCallbackWait; epicsEventId cancelEvent; epicsEventId userCallbackEvent; -} putNotifyPvt; +} notifyPvt; -/* putNotify groups can span locksets if links are dynamically modified*/ -/* Thus a global lock is taken while putNotify fields are accessed */ +/* processNotify groups can span locksets if links are dynamically modified*/ +/* Thus a global lock is taken while processNotify fields are accessed */ typedef struct notifyGlobal { epicsMutexId lock; ELLLIST freeList; @@ -88,12 +87,12 @@ typedef struct notifyGlobal { static notifyGlobal *pnotifyGlobal = 0; /*Local routines*/ -static void putNotifyInit(putNotify *ppn); -static void putNotifyCleanup(putNotify *ppn); -static void restartCheck(putNotifyRecord *ppnr); -static void callUser(dbCommon *precord, putNotify *ppn); +static void notifyInit(processNotify *ppn); +static void notifyCleanup(processNotify *ppn); +static void restartCheck(processNotifyRecord *ppnr); +static void callDone(dbCommon *precord,processNotify *ppn); +static void processNotifyCommon(processNotify *ppn,dbCommon *precord); static void notifyCallback(CALLBACK *pcallback); -static void putNotifyCommon(putNotify *ppn, dbCommon *precord); #define ellSafeAdd(list,listnode) \ { \ @@ -109,175 +108,197 @@ static void putNotifyCommon(putNotify *ppn, dbCommon *precord); (listnode)->isOnList=0; \ } -static void putNotifyInit(putNotify *ppn) +static void notifyInit(processNotify *ppn) { - putNotifyPvt *pputNotifyPvt; + notifyPvt *pnotifyPvt; - pputNotifyPvt = (putNotifyPvt *) ellFirst(&pnotifyGlobal->freeList); - if (pputNotifyPvt) { - ellDelete(&pnotifyGlobal->freeList, &pputNotifyPvt->node); + pnotifyPvt = (notifyPvt *) ellFirst(&pnotifyGlobal->freeList); + if (pnotifyPvt) { + ellDelete(&pnotifyGlobal->freeList, &pnotifyPvt->node); } else { - pputNotifyPvt = dbCalloc(1,sizeof(putNotifyPvt)); - pputNotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty); - pputNotifyPvt->userCallbackEvent = epicsEventCreate(epicsEventEmpty); - pputNotifyPvt->magic = MAGIC; - pputNotifyPvt->state = putNotifyNotActive; + pnotifyPvt = dbCalloc(1,sizeof(notifyPvt)); + pnotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty); + pnotifyPvt->userCallbackEvent = epicsEventCreate(epicsEventEmpty); + pnotifyPvt->magic = MAGIC; + pnotifyPvt->state = notifyNotActive; } - pputNotifyPvt->state = putNotifyNotActive; - callbackSetCallback(notifyCallback,&pputNotifyPvt->callback); - callbackSetUser(ppn,&pputNotifyPvt->callback); - callbackSetPriority(priorityLow,&pputNotifyPvt->callback); - ellInit(&pputNotifyPvt->waitList); - ppn->status = 0; - pputNotifyPvt->state = putNotifyNotActive; - pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0; - ppn->pputNotifyPvt = pputNotifyPvt; + pnotifyPvt->state = notifyNotActive; + callbackSetCallback(notifyCallback,&pnotifyPvt->callback); + callbackSetUser(ppn,&pnotifyPvt->callback); + callbackSetPriority(priorityLow,&pnotifyPvt->callback); + ellInit(&pnotifyPvt->waitList); + ppn->status = notifyOK; + ppn->wasProcessed = 0; + pnotifyPvt->state = notifyNotActive; + pnotifyPvt->cancelWait = pnotifyPvt->userCallbackWait = 0; + ppn->pnotifyPvt = pnotifyPvt; } -static void putNotifyCleanup(putNotify *ppn) +static void notifyCleanup(processNotify *ppn) { - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; - pputNotifyPvt->state = putNotifyNotActive; - ellAdd(&pnotifyGlobal->freeList, &pputNotifyPvt->node); - ppn->pputNotifyPvt = 0; + pnotifyPvt->state = notifyNotActive; + ellAdd(&pnotifyGlobal->freeList, &pnotifyPvt->node); + ppn->pnotifyPvt = 0; } -static void restartCheck(putNotifyRecord *ppnr) +static void restartCheck(processNotifyRecord *ppnr) { dbCommon *precord = ppnr->precord; - putNotify *pfirst; - putNotifyPvt *pputNotifyPvt; - + processNotify *pfirst; + notifyPvt *pnotifyPvt; + assert(precord->ppn); - pfirst = (putNotify *) ellFirst(&ppnr->restartList); + pfirst = (processNotify *) ellFirst(&ppnr->restartList); if (!pfirst) { precord->ppn = 0; return; } - pputNotifyPvt = (putNotifyPvt *) pfirst->pputNotifyPvt; - assert(pputNotifyPvt->state==putNotifyWaitForRestart); + pnotifyPvt = (notifyPvt *) pfirst->pnotifyPvt; + assert(pnotifyPvt->state == notifyWaitForRestart); /* remove pfirst from restartList */ - ellSafeDelete(&ppnr->restartList,&pfirst->restartNode); + ellSafeDelete(&ppnr->restartList, &pfirst->restartNode); /*make pfirst owner of the record*/ precord->ppn = pfirst; /* request callback for pfirst */ - pputNotifyPvt->state = putNotifyRestartCallbackRequested; - callbackRequest(&pputNotifyPvt->callback); + pnotifyPvt->state = notifyRestartCallbackRequested; + callbackRequest(&pnotifyPvt->callback); } -static void callUser(dbCommon *precord, putNotify *ppn) +static void callDone(dbCommon *precord, processNotify *ppn) { - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; epicsMutexUnlock(pnotifyGlobal->lock); + if (ppn->requestType == processGetRequest || + ppn->requestType == putProcessGetRequest) { + ppn->getCallback(ppn, getFieldType); + } dbScanUnlock(precord); - (*ppn->userCallback)(ppn); + ppn->doneCallback(ppn); epicsMutexMustLock(pnotifyGlobal->lock); - if (pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) { - errlogPrintf("%s putNotify: both cancelWait and userCallbackWait true." - "This is illegal\n", precord->name); - pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0; + if (pnotifyPvt->cancelWait && pnotifyPvt->userCallbackWait) { + errlogPrintf("%s processNotify: both cancelWait and userCallbackWait true." + "This is illegal\n", precord->name); + pnotifyPvt->cancelWait = pnotifyPvt->userCallbackWait = 0; } - if (!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) { - putNotifyCleanup(ppn); + if (!pnotifyPvt->cancelWait && !pnotifyPvt->userCallbackWait) { + notifyCleanup(ppn); + epicsMutexUnlock(pnotifyGlobal->lock); + return; + } + if (pnotifyPvt->cancelWait) { + pnotifyPvt->cancelWait = 0; + epicsEventSignal(pnotifyPvt->cancelEvent); epicsMutexUnlock(pnotifyGlobal->lock); return; } - if (pputNotifyPvt->cancelWait) { - pputNotifyPvt->cancelWait = 0; - epicsEventSignal(pputNotifyPvt->cancelEvent); - epicsMutexUnlock(pnotifyGlobal->lock); - return; - } - assert(pputNotifyPvt->userCallbackWait); - pputNotifyPvt->userCallbackWait = 0; - epicsEventSignal(pputNotifyPvt->userCallbackEvent); + assert(pnotifyPvt->userCallbackWait); + pnotifyPvt->userCallbackWait = 0; + epicsEventSignal(pnotifyPvt->userCallbackEvent); epicsMutexUnlock(pnotifyGlobal->lock); return; } -static void putNotifyCommon(putNotify *ppn, dbCommon *precord) +static void processNotifyCommon(processNotify *ppn,dbCommon *precord) { - long status; - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + int didPut = 0; + int doProcess = 0; if (precord->ppn && - pputNotifyPvt->state != putNotifyRestartCallbackRequested) { /*another putNotify owns the record */ - pputNotifyPvt->state = putNotifyWaitForRestart; - ellSafeAdd(&precord->ppnr->restartList,&ppn->restartNode); + pnotifyPvt->state != notifyRestartCallbackRequested) { + /* Another processNotify owns the record */ + pnotifyPvt->state = notifyWaitForRestart; + ellSafeAdd(&precord->ppnr->restartList, &ppn->restartNode); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } else if (precord->ppn) { assert(precord->ppn == ppn); - assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested); + assert(pnotifyPvt->state == notifyRestartCallbackRequested); } if (precord->pact) { precord->ppn = ppn; - ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - pputNotifyPvt->state = putNotifyRestartInProgress; + ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode); + pnotifyPvt->state = notifyRestartInProgress; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } - status = dbChannelPut(ppn->chan, ppn->dbrType, ppn->pbuffer, ppn->nRequest); - ppn->status = (status == 0) ? putNotifyOK : putNotifyError; - /* Check to see if dbProcess should not be called */ - if (!status /*dont process if dbPut returned error */ - && ((dbChannelField(ppn->chan) == (void *) & precord->proc) /*If PROC call dbProcess*/ - || (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) { + if (ppn->requestType == putProcessRequest || + ppn->requestType == putProcessGetRequest) { + /* Check if puts disabled */ + if (precord->disp && (dbChannelField(ppn->chan) != (void *) &precord->disp)) { + ppn->putCallback(ppn, putDisabledType); + } else { + didPut = ppn->putCallback(ppn, putType); + } + } + /* Check if dbProcess should be called */ + if (didPut && + ((dbChannelField(ppn->chan) == (void *) &precord->proc) || + (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) + doProcess = 1; + else + if (ppn->requestType == processGetRequest && + precord->scan == 0) + doProcess = 1; + + if (doProcess) { + ppn->wasProcessed = 1; precord->ppn = ppn; - ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - pputNotifyPvt->state = putNotifyPutInProgress; + ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode); + pnotifyPvt->state = notifyProcessInProgress; epicsMutexUnlock(pnotifyGlobal->lock); dbProcess(precord); dbScanUnlock(precord); return; } - if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { + if (pnotifyPvt->state == notifyRestartCallbackRequested) { restartCheck(precord->ppnr); } - pputNotifyPvt->state = putNotifyUserCallbackActive; + pnotifyPvt->state = notifyUserCallbackActive; assert(precord->ppn!=ppn); - callUser(precord, ppn); + callDone(precord, ppn); } static void notifyCallback(CALLBACK *pcallback) { - putNotify *ppn = NULL; - dbCommon *precord; - putNotifyPvt *pputNotifyPvt; + processNotify *ppn = NULL; + dbCommon *precord; + notifyPvt *pnotifyPvt; callbackGetUser(ppn,pcallback); - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; precord = dbChannelRecord(ppn->chan); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); assert(precord->ppnr); - assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested - || pputNotifyPvt->state==putNotifyUserCallbackRequested); - assert(ellCount(&pputNotifyPvt->waitList)==0); - if (pputNotifyPvt->cancelWait) { - if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { + assert(pnotifyPvt->state == notifyRestartCallbackRequested || + pnotifyPvt->state == notifyUserCallbackRequested); + assert(ellCount(&pnotifyPvt->waitList) == 0); + if (pnotifyPvt->cancelWait) { + if (pnotifyPvt->state == notifyRestartCallbackRequested) { restartCheck(precord->ppnr); } - epicsEventSignal(pputNotifyPvt->cancelEvent); + epicsEventSignal(pnotifyPvt->cancelEvent); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } - if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { - putNotifyCommon(ppn, precord); + if(pnotifyPvt->state == notifyRestartCallbackRequested) { + processNotifyCommon(ppn, precord); return; } /* All done. Clean up and call userCallback */ - pputNotifyPvt->state = putNotifyUserCallbackActive; + pnotifyPvt->state = notifyUserCallbackActive; assert(precord->ppn!=ppn); - callUser(precord, ppn); + callDone(precord, ppn); } -void epicsShareAPI dbPutNotifyInit(void) +void epicsShareAPI dbProcessNotifyInit(void) { if (pnotifyGlobal) return; @@ -286,146 +307,153 @@ void epicsShareAPI dbPutNotifyInit(void) ellInit(&pnotifyGlobal->freeList); } -void epicsShareAPI dbPutNotify(putNotify *ppn) +void epicsShareAPI dbProcessNotify(processNotify *ppn) { struct dbChannel *chan = ppn->chan; dbCommon *precord = dbChannelRecord(chan); short dbfType = dbChannelFieldType(chan); - long status = 0; - putNotifyPvt *pputNotifyPvt; + notifyPvt *pnotifyPvt; - assert(precord); - /*check for putField disabled*/ - if (precord->disp) { - if (dbChannelField(chan) != (void *) & precord->disp) { - ppn->status = putNotifyPutDisabled; - (*ppn->userCallback)(ppn); - return; - } - } /* Must handle DBF_XXXLINKs as special case. * Only dbPutField will change link fields. * Also the record is not processed as a result - */ - if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { - status = dbChannelPutField(ppn->chan, ppn->dbrType, ppn->pbuffer, - ppn->nRequest); - ppn->status = (status == 0) ? putNotifyOK : putNotifyError; - (*ppn->userCallback)(ppn); + */ + ppn->status = notifyOK; + ppn->wasProcessed = 0; + if (dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) { + if (ppn->requestType == putProcessRequest || + ppn->requestType == putProcessGetRequest) { + /* Check if puts disabled */ + if (precord->disp && (dbChannelField(ppn->chan) != (void *) &precord->disp)) { + ppn->putCallback(ppn, putDisabledType); + } else { + ppn->putCallback(ppn, putFieldType); + } + } + if (ppn->requestType == processGetRequest || + ppn->requestType == putProcessGetRequest) { + ppn->getCallback(ppn, getFieldType); + + } + ppn->doneCallback(ppn); return; } dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; - if (pputNotifyPvt && (pputNotifyPvt->magic != MAGIC)) { - printf("dbPutNotify:pputNotifyPvt was not initialized\n"); - pputNotifyPvt = 0; + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + if (pnotifyPvt && (pnotifyPvt->magic != MAGIC)) { + printf("dbPutNotify:pnotifyPvt was not initialized\n"); + pnotifyPvt = 0; } - if (pputNotifyPvt) { - assert(pputNotifyPvt->state==putNotifyUserCallbackActive); - pputNotifyPvt->userCallbackWait = 1; + if (pnotifyPvt) { + assert(pnotifyPvt->state == notifyUserCallbackActive); + pnotifyPvt->userCallbackWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); - epicsEventWait(pputNotifyPvt->userCallbackEvent); + epicsEventWait(pnotifyPvt->userCallbackEvent); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - putNotifyCleanup(ppn); + notifyCleanup(ppn); } - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; - assert(!pputNotifyPvt); - putNotifyInit(ppn); - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; - if (!precord->ppnr) {/* make sure record has a putNotifyRecord*/ - precord->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + assert(!pnotifyPvt); + notifyInit(ppn); + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + if (!precord->ppnr) { + /* make sure record has a processNotifyRecord*/ + precord->ppnr = dbCalloc(1, sizeof(processNotifyRecord)); precord->ppnr->precord = precord; ellInit(&precord->ppnr->restartList); } - putNotifyCommon(ppn, precord); + processNotifyCommon(ppn, precord); } -void epicsShareAPI dbNotifyCancel(putNotify *ppn) +void epicsShareAPI dbNotifyCancel(processNotify *ppn) { dbCommon *precord = dbChannelRecord(ppn->chan); - putNotifyState state; - putNotifyPvt *pputNotifyPvt; + notifyState state; + notifyPvt *pnotifyPvt; - assert(precord); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; - if (!pputNotifyPvt || pputNotifyPvt->state == putNotifyNotActive) { + ppn->status = notifyCanceled; + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + if (!pnotifyPvt || pnotifyPvt->state == notifyNotActive) { epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } - state = pputNotifyPvt->state; - /*If callback is scheduled or active wait for it to complete*/ - if (state == putNotifyUserCallbackRequested || state - == putNotifyRestartCallbackRequested || state - == putNotifyUserCallbackActive) { - pputNotifyPvt->cancelWait = 1; - epicsMutexUnlock(pnotifyGlobal->lock); - dbScanUnlock(precord); - epicsEventWait(pputNotifyPvt->cancelEvent); - epicsMutexMustLock(pnotifyGlobal->lock); - putNotifyCleanup(ppn); - epicsMutexUnlock(pnotifyGlobal->lock); - return; - } + + state = pnotifyPvt->state; switch (state) { - case putNotifyNotActive: - break; - case putNotifyWaitForRestart: + case notifyUserCallbackRequested: + case notifyRestartCallbackRequested: + case notifyUserCallbackActive: + /* Callback is scheduled or active, wait for it to complete */ + pnotifyPvt->cancelWait = 1; + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + epicsEventWait(pnotifyPvt->cancelEvent); + epicsMutexMustLock(pnotifyGlobal->lock); + notifyCleanup(ppn); + epicsMutexUnlock(pnotifyGlobal->lock); + return; + case notifyNotActive: + break; + case notifyWaitForRestart: assert(precord->ppn); assert(precord->ppn!=ppn); - ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode) - ; + ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode); break; - case putNotifyRestartInProgress: - case putNotifyPutInProgress: { /*Take all records out of wait list */ - putNotifyRecord *ppnrWait; + case notifyRestartInProgress: + case notifyProcessInProgress: + { /*Take all records out of wait list */ + processNotifyRecord *ppnrWait; - while ((ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList))) { - ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode); - restartCheck(ppnrWait); + while ((ppnrWait = (processNotifyRecord *) + ellFirst(&pnotifyPvt->waitList))) { + ellSafeDelete(&pnotifyPvt->waitList, &ppnrWait->waitNode); + restartCheck(ppnrWait); + } } - } if (precord->ppn == ppn) restartCheck(precord->ppnr); break; default: printf("dbNotify: illegal state for notifyCallback\n"); } - pputNotifyPvt->state = putNotifyNotActive; - putNotifyCleanup(ppn); + pnotifyPvt->state = notifyNotActive; + notifyCleanup(ppn); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); } void epicsShareAPI dbNotifyCompletion(dbCommon *precord) { - putNotify *ppn = precord->ppn; - putNotifyPvt *pputNotifyPvt; + processNotify *ppn = precord->ppn; + notifyPvt *pnotifyPvt; epicsMutexMustLock(pnotifyGlobal->lock); assert(ppn); assert(precord->ppnr); - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; - if (pputNotifyPvt->state != putNotifyRestartInProgress - && pputNotifyPvt->state != putNotifyPutInProgress) { + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + if (pnotifyPvt->state != notifyRestartInProgress && + pnotifyPvt->state != notifyProcessInProgress) { epicsMutexUnlock(pnotifyGlobal->lock); return; } - ellSafeDelete(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - if ((ellCount(&pputNotifyPvt->waitList)!=0)) { + ellSafeDelete(&pnotifyPvt->waitList, &precord->ppnr->waitNode); + if ((ellCount(&pnotifyPvt->waitList) != 0)) { restartCheck(precord->ppnr); - } else if (pputNotifyPvt->state == putNotifyPutInProgress) { - pputNotifyPvt->state = putNotifyUserCallbackRequested; + } + else if (pnotifyPvt->state == notifyProcessInProgress) { + pnotifyPvt->state = notifyUserCallbackRequested; restartCheck(precord->ppnr); - callbackRequest(&pputNotifyPvt->callback); - } else if (pputNotifyPvt->state == putNotifyRestartInProgress) { - pputNotifyPvt->state = putNotifyRestartCallbackRequested; - callbackRequest(&pputNotifyPvt->callback); + callbackRequest(&pnotifyPvt->callback); + } + else if(pnotifyPvt->state == notifyRestartInProgress) { + pnotifyPvt->state = notifyRestartCallbackRequested; + callbackRequest(&pnotifyPvt->callback); } else { cantProceed("dbNotifyCompletion illegal state"); } @@ -434,59 +462,112 @@ void epicsShareAPI dbNotifyCompletion(dbCommon *precord) void epicsShareAPI dbNotifyAdd(dbCommon *pfrom, dbCommon *pto) { - putNotify *ppn = pfrom->ppn; - putNotifyPvt *pputNotifyPvt; + processNotify *ppn = pfrom->ppn; + notifyPvt *pnotifyPvt; if (pto->pact) return; /*if active it will not be processed*/ epicsMutexMustLock(pnotifyGlobal->lock); - if (!pto->ppnr) {/* make sure record has a putNotifyRecord*/ - pto->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); + if (!pto->ppnr) {/* make sure record has a processNotifyRecord*/ + pto->ppnr = dbCalloc(1, sizeof(processNotifyRecord)); pto->ppnr->precord = pto; ellInit(&pto->ppnr->restartList); } assert(ppn); - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; if (!pto->ppn && - (pputNotifyPvt->state == putNotifyPutInProgress) && + (pnotifyPvt->state == notifyProcessInProgress) && (pto != dbChannelRecord(ppn->chan))) { - putNotifyPvt *pputNotifyPvt; + notifyPvt *pnotifyPvt; pto->ppn = pfrom->ppn; - pputNotifyPvt = (putNotifyPvt *) pfrom->ppn->pputNotifyPvt; - ellSafeAdd(&pputNotifyPvt->waitList,&pto->ppnr->waitNode); + pnotifyPvt = (notifyPvt *) pfrom->ppn->pnotifyPvt; + ellSafeAdd(&pnotifyPvt->waitList, &pto->ppnr->waitNode); } epicsMutexUnlock(pnotifyGlobal->lock); } typedef struct tpnInfo { epicsEventId callbackDone; - putNotify *ppn; + processNotify *ppn; + char buffer[80]; } tpnInfo; -static void dbtpnCallback(putNotify *ppn) +static int putCallback(processNotify *ppn, notifyPutType type) { - putNotifyStatus status = ppn->status; + tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; + int status = 0; + + if (ppn->status == notifyCanceled) + return 0; + ppn->status = notifyOK; + switch (type) { + case putDisabledType: + ppn->status = notifyError; + return 0; + case putFieldType: + status = dbChannelPutField(ppn->chan, DBR_STRING, ptpnInfo->buffer, 1); + break; + case putType: + status = dbChannelPut(ppn->chan, DBR_STRING, ptpnInfo->buffer, 1); + break; + } + if (status) + ppn->status = notifyError; + return 1; +} + +static void getCallback(processNotify *ppn,notifyGetType type) +{ + tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + int status = 0; + long no_elements = 1; + long options = 0; + + if(ppn->status==notifyCanceled) { + printf("dbtpn:getCallback notifyCanceled\n"); + return; + } + switch(type) { + case getFieldType: + status = dbChannelGetField(ppn->chan, DBR_STRING, ptpnInfo->buffer, + &options, &no_elements, 0); + break; + case getType: + status = dbChannelGet(ppn->chan, DBR_STRING, ptpnInfo->buffer, + &options, &no_elements, 0); + break; + } + if (status) { + ppn->status = notifyError; + printf("dbtpn:getCallback error\n"); + } else { + printf("dbtpn:getCallback value %s\n", ptpnInfo->buffer); + } +} + +static void doneCallback(processNotify *ppn) +{ + notifyStatus status = ppn->status; tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; const char *pname = dbChannelRecord(ppn->chan)->name; if (status == 0) printf("dbtpnCallback: success record=%s\n", pname); else - printf("%s dbtpnCallback putNotify.status %d\n", - pname, (int) status); + printf("%s dbtpnCallback processNotify.status %d\n", + pname, (int) status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { tpnInfo *ptpnInfo = (tpnInfo *) pvt; - putNotify *ppn = (putNotify *) ptpnInfo->ppn; + processNotify *ppn = (processNotify *) ptpnInfo->ppn; - dbPutNotify(ppn); + dbProcessNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); - free(ppn->pbuffer); dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); @@ -496,28 +577,30 @@ long epicsShareAPI dbtpn(char *pname, char *pvalue) { struct dbChannel *chan; tpnInfo *ptpnInfo; - putNotify *ppn; - char *pbuffer; + processNotify *ppn=NULL; - pbuffer = epicsStrDup(pvalue); chan = dbChannelCreate(pname); if (!chan) { printf("dbtpn: No such channel"); return -1; } - - ppn = dbCalloc(1, sizeof(putNotify)); + + ppn = dbCalloc(1, sizeof(processNotify)); + ppn->requestType = pvalue ? putProcessRequest : processGetRequest; ppn->chan = chan; - ppn->pbuffer = pbuffer; - ppn->nRequest = 1; - ppn->dbrType = DBR_STRING; - ppn->userCallback = dbtpnCallback; + ppn->putCallback = putCallback; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ptpnInfo = dbCalloc(1, sizeof(tpnInfo)); ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); + strncpy(ptpnInfo->buffer, pvalue, 80); + ptpnInfo->buffer[79] = 0; + ppn->usrPvt = ptpnInfo; epicsThreadCreate("dbtpn", epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); + epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); return 0; } @@ -525,11 +608,8 @@ int epicsShareAPI dbNotifyDump(void) { epicsMutexLockStatus lockStatus; dbRecordType *pdbRecordType; - dbRecordNode *pdbRecordNode; - dbCommon *precord; - putNotify *ppn; - putNotify *ppnRestart; - putNotifyRecord *ppnrWait; + processNotify *ppnRestart; + processNotifyRecord *ppnr; int itry; for (itry = 0; itry < 100; itry++) { @@ -538,39 +618,52 @@ int epicsShareAPI dbNotifyDump(void) break; epicsThreadSleep(.05); } - for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType - = (dbRecordType *) ellNext(&pdbRecordType->node)) { - for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode - = (dbRecordNode *) ellNext(&pdbRecordNode->node)) { - putNotifyPvt *pputNotifyPvt; - precord = pdbRecordNode->precord; + + for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *) ellNext(&pdbRecordType->node)) { + dbRecordNode *pdbRecordNode; + + for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList); + pdbRecordNode; + pdbRecordNode = (dbRecordNode *) ellNext(&pdbRecordNode->node)) { + dbCommon *precord = pdbRecordNode->precord; + processNotify *ppn; + notifyPvt *pnotifyPvt; + if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) continue; - if (!precord->ppn) - continue; - if (!precord->ppnr) + ppn = precord->ppn; + if (!ppn || !precord->ppnr) continue; if (dbChannelRecord(precord->ppn->chan) != precord) continue; - ppn = precord->ppn; - pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; - printf("%s state %d ppn %p\n waitList\n", precord->name, - pputNotifyPvt->state, (void*) ppn); - ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList); - while (ppnrWait) { - printf(" %s pact %d\n", ppnrWait->precord->name, - ppnrWait->precord->pact); - ppnrWait = (putNotifyRecord *) ellNext(&ppnrWait->waitNode.node); + + pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt; + printf("%s state %d ppn %p\n waitList\n", + precord->name, pnotifyPvt->state, (void*) ppn); + ppnr = (processNotifyRecord *) ellFirst(&pnotifyPvt->waitList); + while (ppnr) { + printf(" %s pact %d\n", + ppnr->precord->name, ppnr->precord->pact); + ppnr = (processNotifyRecord *) ellNext(&ppnr->waitNode.node); } - printf(" restartList\n"); - ppnRestart = (putNotify *) ellFirst(&precord->ppnr->restartList); - while (ppnRestart) { - printf(" %p\n", (void *) ppnRestart); - ppnRestart = (putNotify *) ellNext(&ppnRestart->restartNode.node); + ppnr = precord->ppnr; + if (ppnr) { + ppnRestart = (processNotify *)ellFirst( + &precord->ppnr->restartList); + if (ppnRestart) + printf("%s restartList\n", precord->name); + while (ppnRestart) { + printf(" %s\n", dbChannelRecord(ppnRestart->chan)->name); + ppnRestart = (processNotify *) ellNext( + &ppnRestart->restartNode.node); + } } } } if (lockStatus == epicsMutexLockOK) epicsMutexUnlock(pnotifyGlobal->lock); - return (0); + return 0; } + diff --git a/src/ioc/db/dbNotify.h b/src/ioc/db/dbNotify.h index b7ab1460f..c4f3d4832 100644 --- a/src/ioc/db/dbNotify.h +++ b/src/ioc/db/dbNotify.h @@ -3,8 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbNotify.h */ @@ -22,44 +21,61 @@ #endif struct dbCommon; -struct putNotify; +struct processNotify; typedef struct ellCheckNode{ ELLNODE node; int isOnList; -}ellCheckNode; +} ellCheckNode; typedef enum { - putNotifyOK, - putNotifyCanceled, - putNotifyError, - putNotifyPutDisabled -}putNotifyStatus; + processRequest, + putProcessRequest, + processGetRequest, + putProcessGetRequest +} notifyRequestType; -typedef struct putNotify{ - ellCheckNode restartNode; - /*The following members MUST be set by user*/ - void (*userCallback)(struct putNotify *); - struct dbChannel *chan; /*dbChannel*/ - void *pbuffer; /*address of data*/ - long nRequest; /*number of elements to be written*/ - short dbrType; /*database request type*/ - void *usrPvt; /*for private use of user*/ - /*The following is status of request. Set by dbNotify */ - putNotifyStatus status; - void *pputNotifyPvt; /*for private use of putNotify*/ -}putNotify; +typedef enum { + putDisabledType, + putFieldType, + putType +} notifyPutType; -/* dbPutNotify and dbNotifyCancel are the routines called by user*/ -/* The user is normally channel access client or server */ -epicsShareFunc void epicsShareAPI dbPutNotify(putNotify *pputNotify); -epicsShareFunc void epicsShareAPI dbNotifyCancel(putNotify *pputNotify); +typedef enum { + getFieldType, + getType /* FIXME: Never used? */ +} notifyGetType; -/*dbPutNotifyMapType convenience function for old database access*/ -epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype); +typedef enum { + notifyOK, + notifyCanceled, + notifyError, + notifyPutDisabled +} notifyStatus; -/* dbPutNotifyInit called by iocInit */ -epicsShareFunc void epicsShareAPI dbPutNotifyInit(void); +typedef struct processNotify { + /* following fields are for private use by dbNotify implementation */ + ellCheckNode restartNode; + void *pnotifyPvt; + /* The following fields are set by dbNotify. */ + notifyStatus status; + int wasProcessed; /* (0,1) => (no,yes) */ + /*The following members are set by user*/ + notifyRequestType requestType; + struct dbChannel *chan; /*dbChannel*/ + int (*putCallback)(struct processNotify *,notifyPutType type); + void (*getCallback)(struct processNotify *,notifyGetType type); + void (*doneCallback)(struct processNotify *); + void *usrPvt; /*for private use of user*/ +} processNotify; + + +/* dbProcessNotify and dbNotifyCancel are called by user*/ +epicsShareFunc void epicsShareAPI dbProcessNotify(processNotify *pprocessNotify); +epicsShareFunc void epicsShareAPI dbNotifyCancel(processNotify *pprocessNotify); + +/* dbProcessNotifyInit called by iocInit */ +epicsShareFunc void epicsShareAPI dbProcessNotifyInit(void); /*dbNotifyAdd called by dbScanPassive and dbScanLink*/ epicsShareFunc void epicsShareAPI dbNotifyAdd( @@ -67,29 +83,60 @@ epicsShareFunc void epicsShareAPI dbNotifyAdd( /*dbNotifyCompletion called by recGblFwdLink or dbAccess*/ epicsShareFunc void epicsShareAPI dbNotifyCompletion(struct dbCommon *precord); -/* dbtpn is test routine for put notify */ +/* db_put_process defined here since it requires dbNotify. + * src_type is the old DBR type + * This is called by a dbNotify putCallback that uses oldDbr types + */ +epicsShareFunc int epicsShareAPI db_put_process( + processNotify *processNotify,notifyPutType type, + int src_type,const void *psrc, int no_elements); + +/* dbtpn is test routine for dbNotify putProcessRequest */ epicsShareFunc long epicsShareAPI dbtpn(char *recordname,char *value); /* dbNotifyDump is an INVASIVE debug utility. Don't use this needlessly*/ epicsShareFunc int epicsShareAPI dbNotifyDump(void); -/* This module provides code to handle put notify. If a put causes a record to - * be processed, then a user supplied callback is called when that record - * and all records processed because of that record complete processing. - * For asynchronous records completion means completion of the asyn phase. +/* This module provides code to handle process notify. + * client code semantics are: + * 1) The client code allocates storage for a processNotify structure. + * This structure can be used for multiple calls to dbProcessNotify. + * The client is responsible for setting the following fields : + * requestType - The type of request. + * chan - This is typically set via a call to dbChannelCreate. + * putCallback - If requestType is putProcessRequest or putProcessGetRequest + * getCallback - If request is processGetRequest or putProcessGetRequest + * doneCallback - Must be set + * usrPvt - For exclusive use of client. dbNotify does not access this field + * 2) The client calls dbProcessNotify. + * 3) putCallback is called after dbNotify has claimed the record instance + * but before a potential process is requested. + * The putCallback MUST issue the correct put request + * specified by notifyPutType + * 4) getCallback is called after a possible process is complete + * (including asynchronous completion) but before dbNotify has + * released the record. + * The getCallback MUST issue the correct get request + * specified by notifyGetType + * 5) doneCallback is called when dbNotify has released the record. + * The client can issue a new dbProcessNotify request from + * doneCallback or anytime after doneCallback returns. + * 6) The client can call dbNotifyCancel at any time. + * If a dbProcessNotify is active, dbNotifyCancel will not return until + * the dbNotifyRequest is actually canceled. The client must be prepared + * for a callback to be called while dbNotifyCancel is active. * - * User code calls putNotifyInit, putNotifyCleanup, - * dbPutNotify, and dbNotifyCancel. + * dbProcessNotify handles the semantics of record locking and deciding + * if a process request is issued and also calls the client callbacks. * - * The use must allocate storage for "struct putNotify" - * The user MUST set pputNotifyPvt=0 before the first call to dbPutNotify - * and should never modify it again. + * A process request is issued if any of the following is true. + * 1) The requester has issued a processs request and record is passive. + * 2) The requester is doing a put, the record is passive, and either + * a) The field description is process passive. + * b) The field is PROC. + * 3) The requester has requested processGet and the record is passive. * - * After dbPutNotify is called it may not called for the same putNotify - * until the putCallback is complete. The use can call dbNotifyCancel - * to cancel the operation. - * - * The user callback is called when the operation is completed. + * iocInit calls processNotifyInit. * * The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by: * @@ -101,22 +148,20 @@ epicsShareFunc int epicsShareAPI dbNotifyDump(void); * Unless pact is already true. * recGbl * recGblFwdLink calls dbNotifyCompletion - */ - -/* Two fields in dbCommon are used for put notify. - * ppn pointer to putNotify - * If a record is part of a put notify group, - * This field is the address of the associated putNotify. - * As soon as a record completes processing the field is set NULL - * ppnr pointer to putNotifyRecord - * Address of a putNotifyRecord for 1) list node for records - * put notify is waiting to complete, and 2) a list of records - * to restart. * - * See the Application Developer's Guide for implementation rules + * Two fields in dbCommon are used for put notify. + * ppn pointer to processNotify + * If a record is part of a put notify group, + * This field is the address of the associated processNotify. + * As soon as a record completes processing the field is set NULL + * ppnr pointer to processNotifyRecord, which is a private structure + * owned by dbNotify. + * dbNotify is reponsible for this structure. + * */ #ifdef __cplusplus } #endif #endif /*INCdbNotifyh*/ + diff --git a/src/ioc/db/dbPutNotifyBlocker.cpp b/src/ioc/db/dbPutNotifyBlocker.cpp index 4f9f7143b..d2ffa2fa6 100644 --- a/src/ioc/db/dbPutNotifyBlocker.cpp +++ b/src/ioc/db/dbPutNotifyBlocker.cpp @@ -1,10 +1,9 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -52,7 +51,7 @@ dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : { memset ( & this->pn, '\0', sizeof ( this->pn ) ); memset ( & this->dbrScalarValue, '\0', sizeof ( this->dbrScalarValue ) ); - this->pn.pbuffer = & this->dbrScalarValue; + this->pbuffer = & this->dbrScalarValue; } dbPutNotifyBlocker::~dbPutNotifyBlocker () @@ -64,7 +63,7 @@ void dbPutNotifyBlocker::destructor ( epicsGuard < epicsMutex > & guard ) guard.assertIdenticalMutex ( this->mutex ); this->cancel ( guard ); if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { - char * pBuf = static_cast < char * > ( this->pn.pbuffer ); + char * pBuf = static_cast < char * > ( this->pbuffer ); delete [] pBuf; } this->~dbPutNotifyBlocker (); @@ -88,17 +87,29 @@ void dbPutNotifyBlocker::expandValueBuf ( guard.assertIdenticalMutex ( this->mutex ); if ( this->maxValueSize < newSize ) { if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { - char * pBuf = static_cast < char * > ( this->pn.pbuffer ); + char * pBuf = static_cast < char * > ( this->pbuffer ); delete [] pBuf; this->maxValueSize = sizeof ( this->dbrScalarValue ); - this->pn.pbuffer = & this->dbrScalarValue; + this->pbuffer = & this->dbrScalarValue; } - this->pn.pbuffer = new char [newSize]; + this->pbuffer = new char [newSize]; this->maxValueSize = newSize; } } -extern "C" void putNotifyCompletion ( putNotify *ppn ) +extern "C" int putNotifyPut ( processNotify *ppn, notifyPutType type ) +{ + if(ppn->status==notifyCanceled) return 0; +/* + * No locking in this method because only a dbNotifyCancel could interrupt + * and it does not return until cancel is done. + */ + dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); + return db_put_process(ppn,type, + pBlocker->dbrType,pBlocker->pbuffer,pBlocker->nRequest); +} + +extern "C" void putNotifyCompletion ( processNotify *ppn ) { dbPutNotifyBlocker * const pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); @@ -112,11 +123,11 @@ extern "C" void putNotifyCompletion ( putNotify *ppn ) // unavoidable because its possible that the use callback // might destroy this object. pBlocker->block.signal (); - if ( pBlocker->pn.status != putNotifyOK ) { + if ( pBlocker->pn.status != notifyOK ) { pNtfy->exception ( guard, ECA_PUTFAIL, "put notify unsuccessful", - static_cast < unsigned > (pBlocker->pn.dbrType), - static_cast < unsigned > (pBlocker->pn.nRequest) ); + static_cast < unsigned > (pBlocker->dbrType), + static_cast < unsigned > (pBlocker->nRequest) ); } else { pNtfy->completion ( guard ); @@ -163,25 +174,21 @@ void dbPutNotifyBlocker::initiatePutNotify ( throw cacChannel::badType(); } - int status = dbPutNotifyMapType ( - &this->pn, static_cast ( type ) ); - if ( status ) { - this->pNotify = 0; - throw cacChannel::badType(); - } - - this->pn.nRequest = static_cast < unsigned > ( count ); + this->dbrType = type; + this->nRequest = static_cast < unsigned > ( count ); + this->pn.requestType = putProcessRequest; this->pn.chan = dbch; - this->pn.userCallback = putNotifyCompletion; + this->pn.putCallback = putNotifyPut; + this->pn.doneCallback = putNotifyCompletion; this->pn.usrPvt = this; unsigned long size = dbr_size_n ( type, count ); this->expandValueBuf ( guard, size ); - memcpy ( this->pn.pbuffer, pValue, size ); + memcpy ( this->pbuffer, pValue, size ); { epicsGuardRelease < epicsMutex > autoRelease ( guard ); - ::dbPutNotify ( &this->pn ); + ::dbProcessNotify ( &this->pn ); } } diff --git a/src/ioc/db/dbPutNotifyBlocker.h b/src/ioc/db/dbPutNotifyBlocker.h index adbb9a73a..e4d8e92ed 100644 --- a/src/ioc/db/dbPutNotifyBlocker.h +++ b/src/ioc/db/dbPutNotifyBlocker.h @@ -54,7 +54,7 @@ public: epicsPlacementDeleteOperator (( void *, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & )) private: - putNotify pn; + processNotify pn; // // Include a union of all scalar types // including fixed length strings so @@ -75,10 +75,16 @@ private: epicsMutex & mutex; cacWriteNotify * pNotify; unsigned long maxValueSize; + // arguments for db_put_field + void *pbuffer; + long nRequest; + short dbrType; + // end arguments for db_put_field dbSubscriptionIO * isSubscription (); void expandValueBuf ( epicsGuard < epicsMutex > &, unsigned long newSize ); - friend void putNotifyCompletion ( putNotify * ppn ); + friend void putNotifyCompletion ( processNotify * ppn ); + friend int putNotifyPut ( processNotify *ppn, notifyPutType type ); dbPutNotifyBlocker ( const dbPutNotifyBlocker & ); dbPutNotifyBlocker & operator = ( const dbPutNotifyBlocker & ); virtual ~dbPutNotifyBlocker (); diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index 99663773c..c354fed12 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -973,39 +973,62 @@ int dbChannel_put(struct dbChannel *chan, int src_type, } -epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype) +static int mapOldType (short oldtype) { - switch(oldtype) { - case(oldDBR_STRING): - ppn->dbrType = DBR_STRING; + int dbrType = -1; + + switch (oldtype) { + case oldDBR_STRING: + dbrType = DBR_STRING; break; -/* case(oldDBR_INT): */ - case(oldDBR_SHORT): - ppn->dbrType = DBR_SHORT; +/* case oldDBR_INT: */ + case oldDBR_SHORT: + dbrType = DBR_SHORT; break; - case(oldDBR_FLOAT): - ppn->dbrType = DBR_FLOAT; + case oldDBR_FLOAT: + dbrType = DBR_FLOAT; break; - case(oldDBR_ENUM): - ppn->dbrType = DBR_ENUM; + case oldDBR_ENUM: + dbrType = DBR_ENUM; break; - case(oldDBR_CHAR): - ppn->dbrType = DBR_UCHAR; + case oldDBR_CHAR: + dbrType = DBR_UCHAR; break; - case(oldDBR_LONG): - ppn->dbrType = DBR_LONG; + case oldDBR_LONG: + dbrType = DBR_LONG; break; - case(oldDBR_DOUBLE): - ppn->dbrType = DBR_DOUBLE; + case oldDBR_DOUBLE: + dbrType = DBR_DOUBLE; break; - case(oldDBR_PUT_ACKT): - ppn->dbrType = DBR_PUT_ACKT; + case oldDBR_PUT_ACKT: + dbrType = DBR_PUT_ACKT; break; - case(oldDBR_PUT_ACKS): - ppn->dbrType = DBR_PUT_ACKS; + case oldDBR_PUT_ACKS: + dbrType = DBR_PUT_ACKS; break; default: return -1; } - return 0; + return dbrType; +} + +int epicsShareAPI db_put_process(processNotify *ppn, notifyPutType type, + int src_type, const void *psrc, int no_elements) +{ + int status = 0; + int dbrType = mapOldType(src_type); + switch(type) { + case putDisabledType: + ppn->status = notifyError; + return 0; + case putFieldType: + status = dbChannelPutField(ppn->chan, dbrType, psrc, no_elements); + break; + case putType: + status = dbChannelPut(ppn->chan, dbrType, psrc, no_elements); + break; + } + if (status) + ppn->status = notifyError; + return 1; } diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 2db7b57d7..aa8e3b55c 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -1,10 +1,9 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -180,33 +179,39 @@ int epicsShareAPI pft(char *pname, char *pvalue) typedef struct tpnInfo { epicsEventId callbackDone; - putNotify *ppn; - struct dbChannel *chan; + processNotify *ppn; + char buffer[80]; } tpnInfo; -static void tpnCallback(putNotify *ppn) + +static int putCallback(processNotify *ppn,notifyPutType type) { + tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + + return db_put_process(ppn, type, DBR_STRING, ptpnInfo->buffer, 1); +} + +static void doneCallback(processNotify *ppn) { tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; - putNotifyStatus status = ppn->status; + notifyStatus status = ppn->status; const char *pname = dbChannelRecord(ppn->chan)->name; if (status == 0) - printf("tpnCallback: success record=%s\n", pname); + printf("tpnCallback '%s': Success\n", pname); else - printf("%s tpnCallback status = %d\n", pname, status); + printf("tpnCallback '%s': Notify status %d\n", pname, (int)status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { tpnInfo *ptpnInfo = (tpnInfo *) pvt; - putNotify *ppn = (putNotify *) ptpnInfo->ppn; + processNotify *ppn = (processNotify *) ptpnInfo->ppn; - dbPutNotify(ppn); + dbProcessNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); - free(ppn->pbuffer); dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); @@ -216,8 +221,7 @@ int epicsShareAPI tpn(char *pname, char *pvalue) { struct dbChannel *chan; tpnInfo *ptpnInfo; - putNotify *ppn; - char *pbuffer; + processNotify *ppn = NULL; chan = dbChannel_create(pname); if (!chan) { @@ -225,21 +229,16 @@ int epicsShareAPI tpn(char *pname, char *pvalue) return 1; } - pbuffer = epicsStrDup(pvalue); - ppn = calloc(1, sizeof(putNotify)); + ppn = calloc(1, sizeof(processNotify)); if (!ppn) { printf("calloc failed\n"); return -1; } + ppn->requestType = putProcessRequest; ppn->chan = chan; - ppn->pbuffer = pbuffer; - ppn->nRequest = 1; - if (dbPutNotifyMapType(ppn, DBR_STRING)) { - printf("dbPutNotifyMapType failed\n"); - printf("calloc failed\n"); - return -1; - } - ppn->userCallback = tpnCallback; + ppn->putCallback = putCallback; + ppn->doneCallback = doneCallback; + ptpnInfo = calloc(1, sizeof(tpnInfo)); if (!ptpnInfo) { printf("calloc failed\n"); @@ -247,8 +246,12 @@ int epicsShareAPI tpn(char *pname, char *pvalue) } ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); + strncpy(ptpnInfo->buffer, pvalue, 80); + ptpnInfo->buffer[79] = 0; + ppn->usrPvt = ptpnInfo; epicsThreadCreate("tpn", epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); + epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); return 0; } + diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 85735a34d..c498397b8 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -60,6 +60,7 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = { {"GPIB_IO",GPIB_IO}, {"BITBUS_IO",BITBUS_IO}, {"MACRO_LINK",MACRO_LINK}, + {"PN_LINK",PN_LINK}, {"DB_LINK",DB_LINK}, {"CA_LINK",CA_LINK}, {"INST_IO",INST_IO}, @@ -290,7 +291,8 @@ static long setLinkType(DBENTRY *pdbentry) } type = plink->type; - if ((type == CONSTANT || type == PV_LINK || type == DB_LINK || type == CA_LINK) && + if ((type == CONSTANT || type == PV_LINK || + type == PN_LINK || type == DB_LINK || type == CA_LINK) && (link_type == CONSTANT || link_type == PV_LINK)) goto done; dbFreeLinkContents(plink); @@ -2022,6 +2024,14 @@ char * epicsShareAPI dbGetString(DBENTRY *pdbentry) strcpy(message,""); } break; + case PN_LINK: + if(plink->value.pv_link.pvname) + strcpy(message,plink->value.pv_link.pvname); + else + strcpy(message,""); + strcat(message," "); + strcat(message,msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); + break; case PV_LINK: case CA_LINK: case DB_LINK: { @@ -3709,6 +3719,7 @@ int epicsShareAPI dbGetLinkType(DBENTRY *pdbentry) case CONSTANT: return(DCT_LINK_CONSTANT); case PV_LINK: + case PN_LINK: case DB_LINK: case CA_LINK: return(DCT_LINK_PV); diff --git a/src/ioc/dbStatic/link.h b/src/ioc/dbStatic/link.h index ef3cfc69d..5ad7aeee3 100644 --- a/src/ioc/dbStatic/link.h +++ b/src/ioc/dbStatic/link.h @@ -32,17 +32,19 @@ extern "C" { #define GPIB_IO 5 #define BITBUS_IO 6 #define MACRO_LINK 7 + +#define PN_LINK 9 #define DB_LINK 10 #define CA_LINK 11 #define INST_IO 12 /* instrument */ #define BBGPIB_IO 13 /* bitbus -> gpib */ #define RF_IO 14 #define VXI_IO 15 -#define LINK_NTYPES 14 -typedef struct maplinkType{ +#define LINK_NTYPES 15 +typedef struct maplinkType { char *strvalue; int value; -}maplinkType; +} maplinkType; epicsShareExtern maplinkType pamaplinkType[]; diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 18b0c6cd5..7a3e82464 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -140,7 +140,7 @@ int iocBuild(void) errlogPrintf("iocBuild: asInit Failed.\n"); return -1; } - dbPutNotifyInit(); + dbProcessNotifyInit(); epicsThreadSleep(.5); initHookAnnounce(initHookAfterScanInit); @@ -428,7 +428,6 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, { dbFldDes **papFldDes = pdbRecordType->papFldDes; short *link_ind = pdbRecordType->link_ind; - devSup *pdevSup; int j; /* For all the links in the record type... */ @@ -436,16 +435,21 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + if (ellCount(&precord->rdes->devList) > 0 && + (strcmp(pdbFldDes->name, "INP") == 0 || strcmp(pdbFldDes->name, "OUT") == 0)) { + devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); + + if (pdevSup) { + struct dsxt *pdsxt = pdevSup->pdsxt; + if (pdsxt && pdsxt->add_record) { + pdsxt->add_record(precord); + } + } + } + if (plink->type == PV_LINK) dbInitLink(precord, plink, pdbFldDes->field_type); } - pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); - if (pdevSup) { - struct dsxt *pdsxt = pdevSup->pdsxt; - if (pdsxt && pdsxt->add_record) { - pdsxt->add_record(precord); - } - } } static void doInitRecord1(dbRecordType *pdbRecordType, dbCommon *precord, diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index c52d18347..33abbae2e 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -61,7 +61,7 @@ logBadIdWithFileAndLineno(CLIENT, MP, PPL, __FILE__, __LINE__) */ typedef struct rsrv_put_notify { ELLNODE node; - putNotify dbPutNotify; + processNotify dbPutNotify; caHdrLargeArray msg; /* * Include a union of all scalar types @@ -81,8 +81,13 @@ typedef struct rsrv_put_notify { dbr_long_t longval; dbr_double_t doubleval; } dbrScalarValue; + /* arguments for db_put_field */ + void *pbuffer; + long nRequest; + short dbrType; + /* end arguments for db_put_field */ void * asWritePvt; - unsigned valueSize; /* size of block pointed to by dbPutNotify */ + unsigned valueSize; /* size of block pointed to by pbuffer */ char busy; /* put notify in progress */ char onExtraLaborQueue; } RSRVPUTNOTIFY; @@ -1427,11 +1432,33 @@ static int claim_ciu_action ( caHdrLargeArray *mp, } /* - * write_notify_call_back() - * - * (called by the db call back thread) - */ -static void write_notify_call_back(putNotify *ppn) + * write_notify_put_callback() + * + * (called by the db call back thread) + */ + LOCAL int write_notify_put_callback(processNotify *ppn,notifyPutType type) + { + struct channel_in_use * pciu = (struct channel_in_use *) ppn->usrPvt; + struct rsrv_put_notify *pNotify; + + if(ppn->status==notifyCanceled) return 0; + /* + * No locking in this method because only a dbNotifyCancel could interrupt + * and it does not return until cancel is done. + */ + assert(pciu); + assert(pciu->pPutNotify); + pNotify = pciu->pPutNotify; + return db_put_process(ppn,type, + pNotify->dbrType,pNotify->pbuffer,pNotify->nRequest); + } + + /* + * write_notify_done_callback() + * + * (called by the db call back thread) + */ + LOCAL void write_notify_done_callback(processNotify *ppn) { struct channel_in_use * pciu = (struct channel_in_use *) ppn->usrPvt; struct client * pClient; @@ -1497,7 +1524,7 @@ static void write_notify_reply ( struct client * pClient ) * Map from DB status to CA status * */ - if ( ppnb->dbPutNotify.status != putNotifyOK ) { + if ( ppnb->dbPutNotify.status != notifyOK ) { status = ECA_PUTFAIL; } else{ @@ -1630,14 +1657,16 @@ static struct rsrv_put_notify * pNotify = (RSRVPUTNOTIFY *) freeListCalloc ( rsrvPutNotifyFreeList ); if ( pNotify ) { - pNotify->dbPutNotify.pbuffer = - &pNotify->dbrScalarValue; + pNotify->pbuffer = &pNotify->dbrScalarValue; pNotify->valueSize = sizeof (pNotify->dbrScalarValue); pNotify->dbPutNotify.usrPvt = pciu; pNotify->dbPutNotify.chan = pciu->dbch; - pNotify->dbPutNotify.userCallback = - write_notify_call_back; + pNotify->dbPutNotify.putCallback = + write_notify_put_callback; + pNotify->dbPutNotify.doneCallback = + write_notify_done_callback; + pNotify->dbPutNotify.requestType = putProcessRequest; } } else { @@ -1659,10 +1688,10 @@ static int rsrvExpandPutNotify ( */ if ( pNotify->valueSize > sizeof (pNotify->dbrScalarValue) ) { - free ( pNotify->dbPutNotify.pbuffer ); + free ( pNotify->pbuffer ); } - pNotify->dbPutNotify.pbuffer = casCalloc(1,sizeNeeded); - if ( pNotify->dbPutNotify.pbuffer ) { + pNotify->pbuffer = casCalloc(1,sizeNeeded); + if ( pNotify->pbuffer ) { pNotify->valueSize = sizeNeeded; booleanStatus = TRUE; } @@ -1670,7 +1699,7 @@ static int rsrvExpandPutNotify ( /* * revert back to the embedded union */ - pNotify->dbPutNotify.pbuffer = + pNotify->pbuffer = &pNotify->dbrScalarValue; pNotify->valueSize = sizeof (pNotify->dbrScalarValue); @@ -1731,7 +1760,7 @@ void rsrvFreePutNotify ( client *pClient, if ( pNotify->valueSize > sizeof(pNotify->dbrScalarValue) ) { - free ( pNotify->dbPutNotify.pbuffer ); + free ( pNotify->pbuffer ); } freeListFree ( rsrvPutNotifyFreeList, pNotify ); } @@ -1805,7 +1834,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, if ( busyTmp ) { log_header("put call back time out", client, - &pciu->pPutNotify->msg, pciu->pPutNotify->dbPutNotify.pbuffer, 0); + &pciu->pPutNotify->msg, pciu->pPutNotify->pbuffer, 0); asTrapWriteAfter ( asWritePvtTmp ); putNotifyErrorReply (client, &pciu->pPutNotify->msg, ECA_PUTCBINPROG); } @@ -1838,10 +1867,10 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->pPutNotify->busy = TRUE; pciu->pPutNotify->onExtraLaborQueue = FALSE; pciu->pPutNotify->msg = *mp; - pciu->pPutNotify->dbPutNotify.nRequest = mp->m_count; + pciu->pPutNotify->nRequest = mp->m_count; status = caNetConvert ( - mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, + mp->m_dataType, pPayload, pciu->pPutNotify->pbuffer, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); @@ -1849,12 +1878,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_ERROR; } - status = dbPutNotifyMapType(&pciu->pPutNotify->dbPutNotify, mp->m_dataType); - if(status){ - putNotifyErrorReply (client, mp, ECA_PUTFAIL); - pciu->pPutNotify->busy = FALSE; - return RSRV_OK; - } + pciu->pPutNotify->dbrType = mp->m_dataType; pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, @@ -1862,7 +1886,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->client->pHostName ? pciu->client->pHostName : "", pciu->dbch ); - dbPutNotify(&pciu->pPutNotify->dbPutNotify); + dbProcessNotify(&pciu->pPutNotify->dbPutNotify); return RSRV_OK; } diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index 79eb6cb0c..3ee4e7608 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -44,6 +44,13 @@ dbRecStd_SRCS += devSoSoft.c dbRecStd_SRCS += devWfSoft.c dbRecStd_SRCS += devGeneralTime.c +dbRecStd_SRCS += devAiSoftCallback.c +dbRecStd_SRCS += devBiSoftCallback.c +dbRecStd_SRCS += devLiSoftCallback.c +dbRecStd_SRCS += devMbbiDirectSoftCallback.c +dbRecStd_SRCS += devMbbiSoftCallback.c +dbRecStd_SRCS += devSiSoftCallback.c + dbRecStd_SRCS += devAoSoftCallback.c dbRecStd_SRCS += devBoSoftCallback.c dbRecStd_SRCS += devCalcoutSoftCallback.c diff --git a/src/std/dev/devAiSoftCallback.c b/src/std/dev/devAiSoftCallback.c new file mode 100644 index 000000000..d9427d68b --- /dev/null +++ b/src/std/dev/devAiSoftCallback.c @@ -0,0 +1,233 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devAiSoftCallback.c */ +/* + * Authors: Marty Kraimer & Andrew Johnson + */ + +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cantProceed.h" +#include "dbCommon.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsAssert.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "aiRecord.h" +#include "epicsExport.h" + + +#define GET_OPTIONS (DBR_STATUS | DBR_TIME) + +typedef struct devPvt { + processNotify pn; + CALLBACK callback; + long options; + int status; + int smooth; + struct { + DBRstatus + DBRtime + epicsFloat64 value; + } buffer; +} devPvt; + + +static void getCallback(processNotify *ppn, notifyGetType type) +{ + aiRecord *prec = (aiRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + long no_elements = 1; + + if (ppn->status == notifyCanceled) { + printf("devAiSoftCallback::getCallback notifyCanceled\n"); + return; + } + + assert(type == getFieldType); + pdevPvt->status = dbChannelGetField(ppn->chan, DBR_DOUBLE, + &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); +} + +static void doneCallback(processNotify *ppn) +{ + aiRecord *prec = (aiRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); +} + +static long add_record(dbCommon *pcommon) +{ + aiRecord *prec = (aiRecord *)pcommon; + DBLINK *plink = &prec->inp; + dbChannel *chan; + devPvt *pdevPvt; + processNotify *ppn; + + if (plink->type == CONSTANT) return 0; + + if (plink->type != PV_LINK) { + long status = S_db_badField; + + recGblRecordError(status, (void *)prec, + "devAiSoftCallback (add_record) Illegal INP field"); + return status; + } + + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) { + long status = S_db_notFound; + + recGblRecordError(status, (void *)prec, + "devAiSoftCallback (add_record) link target not found"); + return status; + } + + pdevPvt = calloc(1, sizeof(*pdevPvt)); + if (!pdevPvt) { + long status = S_db_noMemory; + + recGblRecordError(status, (void *)prec, + "devAiSoftCallback (add_record) out of memory, calloc() failed"); + return status; + } + ppn = &pdevPvt->pn; + + plink->type = PN_LINK; + plink->value.pv_link.precord = pcommon; + plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ + + ppn->usrPvt = prec; + ppn->chan = chan; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ppn->requestType = processGetRequest; + + pdevPvt->options = GET_OPTIONS; + + prec->dpvt = pdevPvt; + return 0; +} + +static long del_record(dbCommon *pcommon) { + aiRecord *prec = (aiRecord *)pcommon; + DBLINK *plink = &prec->inp; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (plink->type == CONSTANT) return 0; + assert(plink->type == PN_LINK); + + dbNotifyCancel(&pdevPvt->pn); + dbChannelDelete(pdevPvt->pn.chan); + free(pdevPvt); + + plink->type = PV_LINK; + return 0; +} + +static struct dsxt dsxtSoftCallback = { + add_record, del_record +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoftCallback); + return 0; +} + +static long init_record(aiRecord *prec) +{ + /* INP must be CONSTANT or PN_LINK */ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_DOUBLE, &prec->val)) + prec->udf = FALSE; + break; + case PN_LINK: + /* Handled by add_record */ + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devAiSoftCallback (init_record) Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + return 0; +} + +static long read_ai(aiRecord *prec) +{ + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (!prec->dpvt) + return 2; + + if (!prec->pact) { + dbProcessNotify(&pdevPvt->pn); + prec->pact = TRUE; + return 0; + } + + if (pdevPvt->status) { + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + pdevPvt->smooth = FALSE; + return 2; + } + + /* Apply smoothing algorithm */ + if (prec->smoo != 0.0 && pdevPvt->smooth) + prec->val = prec->val * prec->smoo + + pdevPvt->buffer.value * (1.0 - prec->smoo); + else + prec->val = pdevPvt->buffer.value; + prec->udf = FALSE; + pdevPvt->smooth = TRUE; + + switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (pdevPvt->buffer.severity < INVALID_ALARM) + break; + /* else fall through */ + case pvlOptMS: + recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); + break; + case pvlOptMSS: + recGblSetSevr(prec, pdevPvt->buffer.status, + pdevPvt->buffer.severity); + break; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + prec->time = pdevPvt->buffer.time; + return 2; +} + +/* Create the dset for devAiSoftCallback */ +struct { + dset common; + DEVSUPFUN read_ai; + DEVSUPFUN special_linconv; +} devAiSoftCallback = { + {6, NULL, init, init_record, NULL}, + read_ai, + NULL +}; +epicsExportAddress(dset, devAiSoftCallback); diff --git a/src/std/dev/devBiSoftCallback.c b/src/std/dev/devBiSoftCallback.c new file mode 100644 index 000000000..02887ebe7 --- /dev/null +++ b/src/std/dev/devBiSoftCallback.c @@ -0,0 +1,223 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devBiSoftCallback.c */ +/* + * Authors: Marty Kraimer & Andrew Johnson + */ + +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cantProceed.h" +#include "dbCommon.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsAssert.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "biRecord.h" +#include "epicsExport.h" + + +#define GET_OPTIONS (DBR_STATUS | DBR_TIME) + +typedef struct devPvt { + processNotify pn; + CALLBACK callback; + long options; + int status; + struct { + DBRstatus + DBRtime + epicsEnum16 value; + } buffer; +} devPvt; + + +static void getCallback(processNotify *ppn,notifyGetType type) +{ + biRecord *prec = (biRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + long no_elements = 1; + + if (ppn->status == notifyCanceled) { + printf("devBiSoftCallback::getCallback notifyCanceled\n"); + return; + } + + assert(type == getFieldType); + pdevPvt->status = dbChannelGetField(ppn->chan, DBR_ENUM, + &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); +} + +static void doneCallback(processNotify *ppn) +{ + biRecord *prec = (biRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); +} + +static long add_record(dbCommon *pcommon) +{ + biRecord *prec = (biRecord *)pcommon; + DBLINK *plink = &prec->inp; + dbChannel *chan; + devPvt *pdevPvt; + processNotify *ppn; + + if (plink->type == CONSTANT) return 0; + + if (plink->type != PV_LINK) { + long status = S_db_badField; + + recGblRecordError(status, (void *)prec, + "devBiSoftCallback (add_record) Illegal INP field"); + return status; + } + + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) { + long status = S_db_notFound; + + recGblRecordError(status, (void *)prec, + "devBiSoftCallback (add_record) link target not found"); + return status; + } + + pdevPvt = calloc(1, sizeof(*pdevPvt)); + if (!pdevPvt) { + long status = S_db_noMemory; + + recGblRecordError(status, (void *)prec, + "devBiSoftCallback (add_record) out of memory, calloc() failed"); + return status; + } + ppn = &pdevPvt->pn; + + plink->type = PN_LINK; + plink->value.pv_link.precord = pcommon; + plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ + + ppn->usrPvt = prec; + ppn->chan = chan; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ppn->requestType = processGetRequest; + + pdevPvt->options = GET_OPTIONS; + + prec->dpvt = pdevPvt; + return 0; +} + +static long del_record(dbCommon *pcommon) { + biRecord *prec = (biRecord *)pcommon; + DBLINK *plink = &prec->inp; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (plink->type == CONSTANT) return 0; + assert(plink->type == PN_LINK); + + dbNotifyCancel(&pdevPvt->pn); + dbChannelDelete(pdevPvt->pn.chan); + free(pdevPvt); + + plink->type = PV_LINK; + return 0; +} + +static struct dsxt dsxtSoftCallback = { + add_record, del_record +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoftCallback); + return 0; +} + +static long init_record(biRecord *prec) +{ + /* INP must be CONSTANT or PN_LINK */ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBR_ENUM, &prec->val)) + prec->udf = FALSE; + break; + case PN_LINK: + /* Handled by add_record */ + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devBiSoftCallback (init_record) Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + return 0; +} + +static long read_bi(biRecord *prec) +{ + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (!prec->dpvt) + return 2; + + if (!prec->pact) { + dbProcessNotify(&pdevPvt->pn); + prec->pact = TRUE; + return 0; + } + + if (pdevPvt->status) { + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return 2; + } + + prec->val = pdevPvt->buffer.value; + prec->udf = FALSE; + + switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (pdevPvt->buffer.severity < INVALID_ALARM) + break; + /* else fall through */ + case pvlOptMS: + recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); + break; + case pvlOptMSS: + recGblSetSevr(prec, pdevPvt->buffer.status, + pdevPvt->buffer.severity); + break; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + prec->time = pdevPvt->buffer.time; + return 2; +} + +/* Create the dset for devBiSoftCallback */ +struct { + dset common; + DEVSUPFUN read_bi; +} devBiSoftCallback = { + {5, NULL, init, init_record, NULL}, + read_bi +}; +epicsExportAddress(dset, devBiSoftCallback); diff --git a/src/std/dev/devLiSoftCallback.c b/src/std/dev/devLiSoftCallback.c new file mode 100644 index 000000000..90bbff311 --- /dev/null +++ b/src/std/dev/devLiSoftCallback.c @@ -0,0 +1,223 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLiSoftCallback.c */ +/* + * Authors: Marty Kraimer & Andrew Johnson + */ + +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cantProceed.h" +#include "dbCommon.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsAssert.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "longinRecord.h" +#include "epicsExport.h" + + +#define GET_OPTIONS (DBR_STATUS | DBR_TIME) + +typedef struct devPvt { + processNotify pn; + CALLBACK callback; + long options; + int status; + struct { + DBRstatus + DBRtime + epicsInt32 value; + } buffer; +} devPvt; + + +static void getCallback(processNotify *ppn, notifyGetType type) +{ + longinRecord *prec = (longinRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + long no_elements = 1; + + if (ppn->status == notifyCanceled) { + printf("devLiSoftCallback::getCallback notifyCanceled\n"); + return; + } + + assert(type == getFieldType); + pdevPvt->status = dbChannelGetField(ppn->chan, DBR_LONG, + &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); +} + +static void doneCallback(processNotify *ppn) +{ + longinRecord *prec = (longinRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); +} + +static long add_record(dbCommon *pcommon) +{ + longinRecord *prec = (longinRecord *)pcommon; + DBLINK *plink = &prec->inp; + dbChannel *chan; + devPvt *pdevPvt; + processNotify *ppn; + + if (plink->type == CONSTANT) return 0; + + if (plink->type != PV_LINK) { + long status = S_db_badField; + + recGblRecordError(status, (void *)prec, + "devLiSoftCallback (add_record) Illegal INP field"); + return status; + } + + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) { + long status = S_db_notFound; + + recGblRecordError(status, (void *)prec, + "devLiSoftCallback (init_record) linked record not found"); + return status; + } + + pdevPvt = calloc(1, sizeof(*pdevPvt)); + if (!pdevPvt) { + long status = S_db_noMemory; + + recGblRecordError(status, (void *)prec, + "devLiSoftCallback (add_record) out of memory, calloc() failed"); + return status; + } + ppn = &pdevPvt->pn; + + plink->type = PN_LINK; + plink->value.pv_link.precord = pcommon; + plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ + + ppn->usrPvt = prec; + ppn->chan = chan; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ppn->requestType = processGetRequest; + + pdevPvt->options = GET_OPTIONS; + + prec->dpvt = pdevPvt; + return 0; +} + +static long del_record(dbCommon *pcommon) { + longinRecord *prec = (longinRecord *)pcommon; + DBLINK *plink = &prec->inp; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (plink->type == CONSTANT) return 0; + assert(plink->type == PN_LINK); + + dbNotifyCancel(&pdevPvt->pn); + dbChannelDelete(pdevPvt->pn.chan); + free(pdevPvt); + + plink->type = PV_LINK; + return 0; +} + +static struct dsxt dsxtSoftCallback = { + add_record, del_record +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoftCallback); + return 0; +} + +static long init_record(longinRecord *prec) +{ + /* INP must be CONSTANT or PN_LINK */ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBR_LONG, &prec->val)) + prec->udf = FALSE; + break; + case PN_LINK: + /* Handled by add_record */ + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devLiSoftCallback (init_record) Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + return 0; +} + +static long read_li(longinRecord *prec) +{ + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (!prec->dpvt) + return 0; + + if (!prec->pact) { + dbProcessNotify(&pdevPvt->pn); + prec->pact = TRUE; + return 0; + } + + if (pdevPvt->status) { + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return pdevPvt->status; + } + + prec->val = pdevPvt->buffer.value; + prec->udf = FALSE; + + switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (pdevPvt->buffer.severity < INVALID_ALARM) + break; + /* else fall through */ + case pvlOptMS: + recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); + break; + case pvlOptMSS: + recGblSetSevr(prec, pdevPvt->buffer.status, + pdevPvt->buffer.severity); + break; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + prec->time = pdevPvt->buffer.time; + return 0; +} + +/* Create the dset for devLiSoftCallback */ +struct { + dset common; + DEVSUPFUN read_li; +} devLiSoftCallback = { + {5, NULL, init, init_record, NULL}, + read_li +}; +epicsExportAddress(dset, devLiSoftCallback); diff --git a/src/std/dev/devMbbiDirectSoftCallback.c b/src/std/dev/devMbbiDirectSoftCallback.c new file mode 100644 index 000000000..6234a163b --- /dev/null +++ b/src/std/dev/devMbbiDirectSoftCallback.c @@ -0,0 +1,223 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devMbbiDirectSoftCallback.c */ +/* + * Authors: Marty Kraimer & Andrew Johnson + */ + +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cantProceed.h" +#include "dbCommon.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsAssert.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "mbbiDirectRecord.h" +#include "epicsExport.h" + + +#define GET_OPTIONS (DBR_STATUS | DBR_TIME) + +typedef struct devPvt { + processNotify pn; + CALLBACK callback; + long options; + int status; + struct { + DBRstatus + DBRtime + epicsUInt16 value; + } buffer; +} devPvt; + + +static void getCallback(processNotify *ppn, notifyGetType type) +{ + mbbiDirectRecord *prec = (mbbiDirectRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + long no_elements = 1; + + if (ppn->status == notifyCanceled) { + printf("devMbbiDirectSoftCallback::getCallback notifyCanceled\n"); + return; + } + + assert(type == getFieldType); + pdevPvt->status = dbChannelGetField(ppn->chan, DBR_USHORT, + &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); +} + +static void doneCallback(processNotify *ppn) +{ + mbbiDirectRecord *prec = (mbbiDirectRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); +} + +static long add_record(dbCommon *pcommon) +{ + mbbiDirectRecord *prec = (mbbiDirectRecord *)pcommon; + DBLINK *plink = &prec->inp; + dbChannel *chan; + devPvt *pdevPvt; + processNotify *ppn; + + if (plink->type == CONSTANT) return 0; + + if (plink->type != PV_LINK) { + long status = S_db_badField; + + recGblRecordError(status, (void *)prec, + "devMbbiDirectSoftCallback (add_record) Illegal INP field"); + return status; + } + + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) { + long status = S_db_notFound; + + recGblRecordError(status,(void *)prec, + "devMbbiDirectSoftCallback (add_record) linked record not found"); + return status; + } + + pdevPvt = calloc(1, sizeof(*pdevPvt)); + if (!pdevPvt) { + long status = S_db_noMemory; + + recGblRecordError(status, (void *)prec, + "devMbbiDirectSoftCallback (add_record) out of memory, calloc() failed"); + return status; + } + ppn = &pdevPvt->pn; + + plink->type = PN_LINK; + plink->value.pv_link.precord = pcommon; + plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ + + ppn->usrPvt = prec; + ppn->chan = chan; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ppn->requestType = processGetRequest; + + pdevPvt->options = GET_OPTIONS; + + prec->dpvt = pdevPvt; + return 0; +} + +static long del_record(dbCommon *pcommon) { + mbbiDirectRecord *prec = (mbbiDirectRecord *)pcommon; + DBLINK *plink = &prec->inp; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (plink->type == CONSTANT) return 0; + assert(plink->type == PN_LINK); + + dbNotifyCancel(&pdevPvt->pn); + dbChannelDelete(pdevPvt->pn.chan); + free(pdevPvt); + + plink->type = PV_LINK; + return 0; +} + +static struct dsxt dsxtSoftCallback = { + add_record, del_record +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoftCallback); + return 0; +} + +static long init_record(mbbiDirectRecord *prec) +{ + /* INP must be CONSTANT or PN_LINK */ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBR_ENUM, &prec->val)) + prec->udf = FALSE; + break; + case PN_LINK: + /* Handled by add_record */ + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devMbbiSoftCallback (init_record) Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + return 0; +} + +static long read_mbbiDirect(mbbiDirectRecord *prec) +{ + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (!prec->dpvt) + return 2; + + if (!prec->pact) { + dbProcessNotify(&pdevPvt->pn); + prec->pact = TRUE; + return 0; + } + + if (pdevPvt->status) { + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return 2; + } + + prec->val = pdevPvt->buffer.value; + prec->udf = FALSE; + + switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (pdevPvt->buffer.severity < INVALID_ALARM) + break; + /* else fall through */ + case pvlOptMS: + recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); + break; + case pvlOptMSS: + recGblSetSevr(prec, pdevPvt->buffer.status, + pdevPvt->buffer.severity); + break; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + prec->time = pdevPvt->buffer.time; + return 2; +} + +/* Create the dset for devMbbiDirectSoftCallback */ +struct { + dset common; + DEVSUPFUN read_mbbiDirect; +} devMbbiDirectSoftCallback = { + {5, NULL, init, init_record, NULL}, + read_mbbiDirect +}; +epicsExportAddress(dset, devMbbiDirectSoftCallback); diff --git a/src/std/dev/devMbbiSoftCallback.c b/src/std/dev/devMbbiSoftCallback.c new file mode 100644 index 000000000..5447b4b6e --- /dev/null +++ b/src/std/dev/devMbbiSoftCallback.c @@ -0,0 +1,223 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devMbbiSoftCallback.c */ +/* + * Authors: Marty Kraimer & Andrew Johnson + */ + +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cantProceed.h" +#include "dbCommon.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsAssert.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "mbbiRecord.h" +#include "epicsExport.h" + + +#define GET_OPTIONS (DBR_STATUS | DBR_TIME) + +typedef struct devPvt { + processNotify pn; + CALLBACK callback; + long options; + int status; + struct { + DBRstatus + DBRtime + epicsEnum16 value; + } buffer; +} devPvt; + + +static void getCallback(processNotify *ppn, notifyGetType type) +{ + mbbiRecord *prec = (mbbiRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + long no_elements = 1; + + if (ppn->status == notifyCanceled) { + printf("devMbbiSoftCallback::getCallback notifyCanceled\n"); + return; + } + + assert(type == getFieldType); + pdevPvt->status = dbChannelGetField(ppn->chan, DBR_ENUM, + &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); +} + +static void doneCallback(processNotify *ppn) +{ + mbbiRecord *prec = (mbbiRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); +} + +static long add_record(dbCommon *pcommon) +{ + mbbiRecord *prec = (mbbiRecord *)pcommon; + DBLINK *plink = &prec->inp; + dbChannel *chan; + devPvt *pdevPvt; + processNotify *ppn; + + if (plink->type == CONSTANT) return 0; + + if (plink->type != PV_LINK) { + long status = S_db_badField; + + recGblRecordError(status, (void *)prec, + "devMbbiSoftCallback (add_record) Illegal INP field"); + return status; + } + + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) { + long status = S_db_notFound; + + recGblRecordError(status, (void *)prec, + "devMbbiSoftCallback (add_record) linked record not found"); + return status; + } + + pdevPvt = calloc(1, sizeof(*pdevPvt)); + if (!pdevPvt) { + long status = S_db_noMemory; + + recGblRecordError(status, (void *)prec, + "devMbbiSoftCallback (add_record) out of memory, calloc() failed"); + return status; + } + ppn = &pdevPvt->pn; + + plink->type = PN_LINK; + plink->value.pv_link.precord = pcommon; + plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ + + ppn->usrPvt = prec; + ppn->chan = chan; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ppn->requestType = processGetRequest; + + pdevPvt->options = GET_OPTIONS; + + prec->dpvt = pdevPvt; + return 0; +} + +static long del_record(dbCommon *pcommon) { + mbbiRecord *prec = (mbbiRecord *)pcommon; + DBLINK *plink = &prec->inp; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (plink->type == CONSTANT) return 0; + assert(plink->type == PN_LINK); + + dbNotifyCancel(&pdevPvt->pn); + dbChannelDelete(pdevPvt->pn.chan); + free(pdevPvt); + + plink->type = PV_LINK; + return 0; +} + +static struct dsxt dsxtSoftCallback = { + add_record, del_record +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoftCallback); + return 0; +} + +static long init_record(mbbiRecord *prec) +{ + /* INP must be CONSTANT or PN_LINK */ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBR_ENUM, &prec->val)) + prec->udf = FALSE; + break; + case PN_LINK: + /* Handled by add_record */ + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devMbbiSoftCallback (init_record) Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + return 0; +} + +static long read_mbbi(mbbiRecord *prec) +{ + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (!prec->dpvt) + return 2; + + if (!prec->pact) { + dbProcessNotify(&pdevPvt->pn); + prec->pact = TRUE; + return 0; + } + + if (pdevPvt->status) { + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return 2; + } + + prec->val = pdevPvt->buffer.value; + prec->udf = FALSE; + + switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (pdevPvt->buffer.severity < INVALID_ALARM) + break; + /* else fall through */ + case pvlOptMS: + recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); + break; + case pvlOptMSS: + recGblSetSevr(prec, pdevPvt->buffer.status, + pdevPvt->buffer.severity); + break; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + prec->time = pdevPvt->buffer.time; + return 2; +} + +/* Create the dset for devMbbiSoftCallback */ +struct { + dset common; + DEVSUPFUN read_mbbi; +} devMbbiSoftCallback = { + {5, NULL, init, init_record, NULL}, + read_mbbi +}; +epicsExportAddress(dset,devMbbiSoftCallback); diff --git a/src/std/dev/devSiSoftCallback.c b/src/std/dev/devSiSoftCallback.c new file mode 100644 index 000000000..e2eff3213 --- /dev/null +++ b/src/std/dev/devSiSoftCallback.c @@ -0,0 +1,225 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devSiSoftCallback.c */ +/* + * Authors: Marty Kraimer & Andrew Johnson + */ + +#include +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cantProceed.h" +#include "dbCommon.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbChannel.h" +#include "dbNotify.h" +#include "epicsAssert.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "stringinRecord.h" +#include "epicsExport.h" + + +#define GET_OPTIONS (DBR_STATUS | DBR_TIME) + +typedef struct devPvt { + DBADDR dbaddr; + processNotify pn; + CALLBACK callback; + long options; + int status; + struct { + DBRstatus + DBRtime + char value[MAX_STRING_SIZE]; + } buffer; +} devPvt; + + +static void getCallback(processNotify *ppn, notifyGetType type) +{ + stringinRecord *prec = (stringinRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + long no_elements = 1; + + if (ppn->status==notifyCanceled) { + printf("devSiSoftCallback::getCallback notifyCanceled\n"); + return; + } + + assert(type == getFieldType); + pdevPvt->status = dbChannelGetField(ppn->chan, DBR_STRING, + &pdevPvt->buffer, &pdevPvt->options, &no_elements, 0); +} + +static void doneCallback(processNotify *ppn) +{ + stringinRecord *prec = (stringinRecord *)ppn->usrPvt; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec); +} + +static long add_record(dbCommon *pcommon) +{ + stringinRecord *prec = (stringinRecord *)pcommon; + DBLINK *plink = &prec->inp; + dbChannel *chan; + devPvt *pdevPvt; + processNotify *ppn; + + if (plink->type == CONSTANT) return 0; + + if (plink->type != PV_LINK) { + long status = S_db_badField; + + recGblRecordError(status, (void *)prec, + "devSiSoftCallback (add_record) Illegal INP field"); + return status; + } + + pdevPvt = calloc(1, sizeof(*pdevPvt)); + if (!pdevPvt) { + long status = S_db_noMemory; + + recGblRecordError(status, (void *)prec, + "devSiSoftCallback (add_record) out of memory, calloc() failed"); + return status; + } + ppn = &pdevPvt->pn; + + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) { + long status = S_db_notFound; + + recGblRecordError(status, (void *)prec, + "devSiSoftCallback (add_record) linked record not found"); + return status; + } + + plink->type = PN_LINK; + plink->value.pv_link.precord = pcommon; + plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */ + + ppn->usrPvt = prec; + ppn->chan = chan; + ppn->getCallback = getCallback; + ppn->doneCallback = doneCallback; + ppn->requestType = processGetRequest; + + pdevPvt->options = GET_OPTIONS; + + prec->dpvt = pdevPvt; + return 0; +} + +static long del_record(dbCommon *pcommon) { + stringinRecord *prec = (stringinRecord *)pcommon; + DBLINK *plink = &prec->inp; + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (plink->type == CONSTANT) return 0; + assert(plink->type == PN_LINK); + + dbNotifyCancel(&pdevPvt->pn); + dbChannelDelete(pdevPvt->pn.chan); + free(pdevPvt); + + plink->type = PV_LINK; + return 0; +} + +static struct dsxt dsxtSoftCallback = { + add_record, del_record +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoftCallback); + return 0; +} + +static long init_record(stringinRecord *prec) +{ + /* INP must be CONSTANT or PN_LINK */ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBR_STRING, &prec->val)) + prec->udf = FALSE; + break; + case PN_LINK: + /* Handled by add_record */ + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devSiSoftCallback (init_record) Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + return 0; +} + +static long read_si(stringinRecord *prec) +{ + devPvt *pdevPvt = (devPvt *)prec->dpvt; + + if (!prec->dpvt) + return 0; + + if (!prec->pact) { + dbProcessNotify(&pdevPvt->pn); + prec->pact = TRUE; + return 0; + } + + if (pdevPvt->status) { + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return pdevPvt->status; + } + strncpy(prec->val, pdevPvt->buffer.value, MAX_STRING_SIZE); + prec->val[MAX_STRING_SIZE-1] = 0; + prec->udf = FALSE; + + switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (pdevPvt->buffer.severity < INVALID_ALARM) + break; + /* else fall through */ + case pvlOptMS: + recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity); + break; + case pvlOptMSS: + recGblSetSevr(prec, pdevPvt->buffer.status, + pdevPvt->buffer.severity); + break; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + prec->time = pdevPvt->buffer.time; + return 0; +} + +/* Create the dset for devSiSoftCallback */ +struct { + dset common; + DEVSUPFUN read_li; +} devSiSoftCallback = { + {5, NULL, init, init_record, NULL}, + read_si +}; +epicsExportAddress(dset,devSiSoftCallback); diff --git a/src/std/dev/devSoft.dbd b/src/std/dev/devSoft.dbd index 8178bfef1..9072245ca 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -27,12 +27,18 @@ device(mbbiDirect,CONSTANT,devMbbiDirectSoftRaw,"Raw Soft Channel") device(mbbo,CONSTANT,devMbboSoftRaw,"Raw Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoftRaw,"Raw Soft Channel") +device(ai,CONSTANT,devAiSoftCallback,"Async Soft Channel") device(ao,CONSTANT,devAoSoftCallback,"Async Soft Channel") +device(bi,CONSTANT,devBiSoftCallback,"Async Soft Channel") device(bo,CONSTANT,devBoSoftCallback,"Async Soft Channel") device(calcout,CONSTANT,devCalcoutSoftCallback,"Async Soft Channel") +device(longin,CONSTANT,devLiSoftCallback,"Async Soft Channel") device(longout,CONSTANT,devLoSoftCallback,"Async Soft Channel") +device(mbbi,CONSTANT,devMbbiSoftCallback,"Async Soft Channel") +device(mbbiDirect,CONSTANT,devMbbiDirectSoftCallback,"Async Soft Channel") device(mbbo,CONSTANT,devMbboSoftCallback,"Async Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoftCallback,"Async Soft Channel") +device(stringin,CONSTANT,devSiSoftCallback,"Async Soft Channel") device(stringout,CONSTANT,devSoSoftCallback,"Async Soft Channel") device(ai, INST_IO,devTimestampAI,"Soft Timestamp")