Merge branch '7.0' (after codeathon 2023) into PSI-7.0
This commit is contained in:
@@ -58,6 +58,7 @@ typedef struct cbQueueSet {
|
||||
int shutdown; // use atomic
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
epicsThreadId *threads;
|
||||
} cbQueueSet;
|
||||
|
||||
static cbQueueSet callbackQueue[NUM_CALLBACK_PRIORITIES];
|
||||
@@ -242,11 +243,15 @@ void callbackStop(void)
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
int j;
|
||||
|
||||
while (epicsAtomicGetIntT(&mySet->threadsRunning)) {
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
epicsEventWaitWithTimeout(startStopEvent, 0.1);
|
||||
}
|
||||
for(j=0; j<mySet->threadsConfigured; j++) {
|
||||
epicsThreadMustJoin(mySet->threads[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +271,8 @@ void callbackCleanup(void)
|
||||
mySet->semWakeUp = NULL;
|
||||
epicsRingPointerDelete(mySet->queue);
|
||||
mySet->queue = NULL;
|
||||
free(mySet->threads);
|
||||
mySet->threads = NULL;
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
@@ -297,17 +304,25 @@ void callbackInit(void)
|
||||
cantProceed("epicsRingPointerLockedCreate failed for %s\n",
|
||||
threadNamePrefix[i]);
|
||||
callbackQueue[i].queueOverflow = FALSE;
|
||||
|
||||
if (callbackQueue[i].threadsConfigured == 0)
|
||||
callbackQueue[i].threadsConfigured = callbackThreadsDefault;
|
||||
|
||||
callbackQueue[i].threads = callocMustSucceed(callbackQueue[i].threadsConfigured,
|
||||
sizeof(*callbackQueue[i].threads),
|
||||
"callbackInit");
|
||||
|
||||
for (j = 0; j < callbackQueue[i].threadsConfigured; j++) {
|
||||
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
|
||||
opts.joinable = 1;
|
||||
opts.priority = threadPriority[i];
|
||||
opts.stackSize = epicsThreadStackBig;
|
||||
if (callbackQueue[i].threadsConfigured > 1 )
|
||||
sprintf(threadName, "%s-%d", threadNamePrefix[i], j);
|
||||
else
|
||||
strcpy(threadName, threadNamePrefix[i]);
|
||||
tid = epicsThreadCreate(threadName, threadPriority[i],
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
(EPICSTHREADFUNC)callbackTask, &priorityValue[i]);
|
||||
callbackQueue[i].threads[j] = tid = epicsThreadCreateOpt(threadName,
|
||||
(EPICSTHREADFUNC)callbackTask, &priorityValue[i], &opts);
|
||||
if (tid == 0) {
|
||||
cantProceed("Failed to spawn callback thread %s\n", threadName);
|
||||
} else {
|
||||
|
||||
@@ -154,7 +154,7 @@ static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
|
||||
const char *pstr = plink->value.constantStr;
|
||||
size_t len;
|
||||
|
||||
if (!pstr)
|
||||
if (!pstr || !pstr[0])
|
||||
return S_db_badField;
|
||||
len = strlen(pstr);
|
||||
|
||||
@@ -181,7 +181,7 @@ static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
const char *pstr = plink->value.constantStr;
|
||||
long status;
|
||||
|
||||
if (!pstr)
|
||||
if (!pstr || !pstr[0])
|
||||
return S_db_badField;
|
||||
|
||||
status = dbLSConvertJSON(pstr, pbuffer, size, plen);
|
||||
@@ -197,7 +197,7 @@ static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
const char *pstr = plink->value.constantStr;
|
||||
long status;
|
||||
|
||||
if (!pstr)
|
||||
if (!pstr || !pstr[0])
|
||||
return S_db_badField;
|
||||
|
||||
/* Choice values must be numeric */
|
||||
|
||||
@@ -76,6 +76,7 @@ struct event_que {
|
||||
unsigned short quota; /* the number of assigned entries*/
|
||||
unsigned short nDuplicates; /* N events duplicated on this q */
|
||||
unsigned short nCanceled; /* the number of canceled entries */
|
||||
unsigned possibleStall;
|
||||
};
|
||||
|
||||
struct event_user {
|
||||
@@ -934,6 +935,7 @@ void db_post_single_event (dbEventSubscription event)
|
||||
static int event_read ( struct event_que *ev_que )
|
||||
{
|
||||
db_field_log *pfl;
|
||||
int notifiedRemaining = 0;
|
||||
void ( *user_sub ) ( void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, db_field_log *pfl );
|
||||
|
||||
@@ -955,6 +957,7 @@ static int event_read ( struct event_que *ev_que )
|
||||
|
||||
while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) {
|
||||
struct evSubscrip *pevent = ev_que->evque[ev_que->getix];
|
||||
int eventsRemaining;
|
||||
|
||||
pfl = ev_que->valque[ev_que->getix];
|
||||
if ( pevent == &canceledEvent ) {
|
||||
@@ -977,6 +980,7 @@ static int event_read ( struct event_que *ev_que )
|
||||
|
||||
event_remove ( ev_que, ev_que->getix, EVENTQEMPTY );
|
||||
ev_que->getix = RNGINC ( ev_que->getix );
|
||||
eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY && !ev_que->nCanceled;
|
||||
|
||||
/*
|
||||
* create a local copy of the call back parameters while
|
||||
@@ -1009,7 +1013,8 @@ static int event_read ( struct event_que *ev_que )
|
||||
if (pfl) {
|
||||
/* Issue user callback */
|
||||
( *user_sub ) ( pevent->user_arg, pevent->chan,
|
||||
ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl );
|
||||
eventsRemaining, pfl );
|
||||
notifiedRemaining = eventsRemaining;
|
||||
}
|
||||
LOCKEVQUE (ev_que);
|
||||
|
||||
@@ -1036,6 +1041,11 @@ static int event_read ( struct event_que *ev_que )
|
||||
db_delete_field_log(pfl);
|
||||
}
|
||||
|
||||
if(notifiedRemaining && !ev_que->possibleStall) {
|
||||
ev_que->possibleStall = 1;
|
||||
errlogPrintf(ERL_WARNING " dbEvent possible queue stall\n");
|
||||
}
|
||||
|
||||
UNLOCKEVQUE (ev_que);
|
||||
|
||||
return DB_EVENT_OK;
|
||||
|
||||
@@ -168,9 +168,13 @@ void scanStop(void)
|
||||
epicsEventSignal(ppsl->loopEvent);
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
for (i = 0; i < nPeriodic; i++) {
|
||||
epicsThreadMustJoin(periodicTaskId[i]);
|
||||
}
|
||||
|
||||
scanOnce((dbCommon *)&exitOnce);
|
||||
epicsEventWait(startStopEvent);
|
||||
epicsThreadMustJoin(onceTaskId);
|
||||
}
|
||||
|
||||
void scanCleanup(void)
|
||||
@@ -761,14 +765,16 @@ void scanOnceQueueShow(const int reset)
|
||||
|
||||
static void initOnce(void)
|
||||
{
|
||||
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
|
||||
opts.joinable = 1;
|
||||
opts.priority = epicsThreadPriorityScanLow + nPeriodic;
|
||||
opts.stackSize = epicsThreadStackBig;
|
||||
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
|
||||
cantProceed("initOnce: Ring buffer create failed\n");
|
||||
}
|
||||
if(!onceSem)
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
onceTaskId = epicsThreadCreate("scanOnce",
|
||||
epicsThreadPriorityScanLow + nPeriodic,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0);
|
||||
onceTaskId = epicsThreadCreateOpt("scanOnce", onceTask, 0, &opts);
|
||||
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
@@ -932,14 +938,16 @@ static void spawnPeriodic(int ind)
|
||||
{
|
||||
periodic_scan_list *ppsl = papPeriodic[ind];
|
||||
char taskName[20];
|
||||
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
|
||||
opts.joinable = 1;
|
||||
opts.priority = epicsThreadPriorityScanLow + ind;
|
||||
opts.stackSize = epicsThreadStackBig;
|
||||
|
||||
if (!ppsl) return;
|
||||
|
||||
sprintf(taskName, "scan-%g", ppsl->period);
|
||||
periodicTaskId[ind] = epicsThreadCreate(
|
||||
taskName, epicsThreadPriorityScanLow + ind,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
periodicTask, (void *)ppsl);
|
||||
periodicTaskId[ind] = epicsThreadCreateOpt(
|
||||
taskName, periodicTask, (void *)ppsl, &opts);
|
||||
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
|
||||
break;
|
||||
}
|
||||
#define OP(DBR,Type,pat) case DBR: {Type expect = *(Type*)pbuf, actual = *(Type*)gbuf; assert(vSize==sizeof(Type)); match &= expect==actual; \
|
||||
if(expect!=actual) testDiag("[%lu] expected=" pat " actual=" pat, n, expect, actual); break;}
|
||||
if(expect!=actual) {testDiag("[%lu] expected=" pat " actual=" pat, n, expect, actual);} break;}
|
||||
|
||||
OP(DBR_CHAR, char, "%c");
|
||||
OP(DBR_UCHAR, unsigned char, "%u");
|
||||
|
||||
@@ -111,7 +111,8 @@ typedef struct inputFile{
|
||||
static ELLLIST inputFileList = ELLLIST_INIT;
|
||||
|
||||
static inputFile *pinputFileNow = NULL;
|
||||
static DBBASE *pdbbase = NULL;
|
||||
/* The DBBASE most recently allocated/used by dbReadCOM() */
|
||||
static DBBASE *savedPdbbase = NULL;
|
||||
|
||||
typedef struct tempListNode {
|
||||
ELLNODE node;
|
||||
@@ -233,15 +234,15 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
}
|
||||
|
||||
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
|
||||
pdbbase = *ppdbbase;
|
||||
savedPdbbase = *ppdbbase;
|
||||
if(path && strlen(path)>0) {
|
||||
dbPath(pdbbase,path);
|
||||
dbPath(savedPdbbase,path);
|
||||
} else {
|
||||
penv = getenv("EPICS_DB_INCLUDE_PATH");
|
||||
if(penv) {
|
||||
dbPath(pdbbase,penv);
|
||||
dbPath(savedPdbbase,penv);
|
||||
} else {
|
||||
dbPath(pdbbase,".");
|
||||
dbPath(savedPdbbase,".");
|
||||
}
|
||||
}
|
||||
my_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
|
||||
@@ -271,7 +272,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
FILE *fp1 = 0;
|
||||
|
||||
if (pinputFile->filename)
|
||||
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp1);
|
||||
pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp1);
|
||||
if (!pinputFile->filename || !fp1) {
|
||||
errPrintf(0, __FILE__, __LINE__,
|
||||
"dbRead opening file %s\n",pinputFile->filename);
|
||||
@@ -297,13 +298,13 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
while (ellCount(&tempList))
|
||||
popFirstTemp(); /* Memory leak on parser failure */
|
||||
|
||||
dbFreePath(pdbbase);
|
||||
dbFreePath(savedPdbbase);
|
||||
if(!status) { /*add RTYP and VERS as an attribute */
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
long localStatus;
|
||||
|
||||
dbInitEntry(pdbbase,pdbEntry);
|
||||
dbInitEntry(savedPdbbase,pdbEntry);
|
||||
localStatus = dbFirstRecordType(pdbEntry);
|
||||
while(!localStatus) {
|
||||
localStatus = dbPutRecordAttribute(pdbEntry,"RTYP",
|
||||
@@ -323,7 +324,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
cleanup:
|
||||
if(dbRecordsAbcSorted) {
|
||||
ELLNODE *cur;
|
||||
for(cur = ellFirst(&pdbbase->recordTypeList); cur; cur=ellNext(cur))
|
||||
for(cur = ellFirst(&savedPdbbase->recordTypeList); cur; cur=ellNext(cur))
|
||||
{
|
||||
dbRecordType *rtype = CONTAINER(cur, dbRecordType, node);
|
||||
|
||||
@@ -416,12 +417,12 @@ static void dbIncludePrint(void)
|
||||
|
||||
static void dbPathCmd(char *path)
|
||||
{
|
||||
dbPath(pdbbase,path);
|
||||
dbPath(savedPdbbase,path);
|
||||
}
|
||||
|
||||
static void dbAddPathCmd(char *path)
|
||||
{
|
||||
dbAddPath(pdbbase,path);
|
||||
dbAddPath(savedPdbbase,path);
|
||||
}
|
||||
|
||||
static void dbIncludeNew(char *filename)
|
||||
@@ -431,7 +432,7 @@ static void dbIncludeNew(char *filename)
|
||||
|
||||
pinputFile = dbCalloc(1,sizeof(inputFile));
|
||||
pinputFile->filename = macEnvExpand(filename);
|
||||
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp);
|
||||
pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp);
|
||||
if (!fp) {
|
||||
epicsPrintf("Can't open include file \"%s\"\n", filename);
|
||||
yyerror(NULL);
|
||||
@@ -453,7 +454,7 @@ static void dbMenuHead(char *name)
|
||||
yyerrorAbort("dbMenuHead: Menu name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->menuList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
return;
|
||||
@@ -501,14 +502,14 @@ static void dbMenuBody(void)
|
||||
}
|
||||
if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty");
|
||||
/* Add menu in sorted order */
|
||||
pMenu = (dbMenu *)ellFirst(&pdbbase->menuList);
|
||||
pMenu = (dbMenu *)ellFirst(&savedPdbbase->menuList);
|
||||
while(pMenu && strcmp(pMenu->name,pnewMenu->name) >0 )
|
||||
pMenu = (dbMenu *)ellNext(&pMenu->node);
|
||||
if(pMenu)
|
||||
ellInsert(&pdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node);
|
||||
ellInsert(&savedPdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node);
|
||||
else
|
||||
ellAdd(&pdbbase->menuList,&pnewMenu->node);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pnewMenu->name,&pdbbase->menuList);
|
||||
ellAdd(&savedPdbbase->menuList,&pnewMenu->node);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,pnewMenu->name,&savedPdbbase->menuList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
} else {
|
||||
@@ -525,14 +526,14 @@ static void dbRecordtypeHead(char *name)
|
||||
yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->recordTypeList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
return;
|
||||
}
|
||||
pdbRecordType = dbCalloc(1,sizeof(dbRecordType));
|
||||
pdbRecordType->name = epicsStrDup(name);
|
||||
if (pdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList);
|
||||
if (savedPdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList);
|
||||
if(ellCount(&tempList))
|
||||
yyerrorAbort("dbRecordtypeHead tempList not empty");
|
||||
allocTemp(pdbRecordType);
|
||||
@@ -564,13 +565,13 @@ static short findOrAddGuiGroup(const char *name)
|
||||
{
|
||||
dbGuiGroup *pdbGuiGroup;
|
||||
GPHENTRY *pgphentry;
|
||||
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash, name, &savedPdbbase->guiGroupList);
|
||||
if (!pgphentry) {
|
||||
pdbGuiGroup = dbCalloc(1,sizeof(dbGuiGroup));
|
||||
pdbGuiGroup->name = epicsStrDup(name);
|
||||
ellAdd(&pdbbase->guiGroupList, &pdbGuiGroup->node);
|
||||
pdbGuiGroup->key = ellCount(&pdbbase->guiGroupList);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash, pdbGuiGroup->name, &pdbbase->guiGroupList);
|
||||
ellAdd(&savedPdbbase->guiGroupList, &pdbGuiGroup->node);
|
||||
pdbGuiGroup->key = ellCount(&savedPdbbase->guiGroupList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash, pdbGuiGroup->name, &savedPdbbase->guiGroupList);
|
||||
pgphentry->userPvt = pdbGuiGroup;
|
||||
}
|
||||
return ((dbGuiGroup *)pgphentry->userPvt)->key;
|
||||
@@ -653,8 +654,8 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"menu")==0) {
|
||||
pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(pdbbase,value);
|
||||
if(!pdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt)
|
||||
pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(savedPdbbase,value);
|
||||
if(!savedPdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt)
|
||||
yyerrorAbort("menu not found");
|
||||
return;
|
||||
}
|
||||
@@ -672,7 +673,7 @@ static void dbRecordtypeCdef(char *text) {
|
||||
tempListNode *ptempListNode;
|
||||
dbRecordType *pdbRecordType;
|
||||
|
||||
if (!pdbbase->loadCdefs || duplicate) return;
|
||||
if (!savedPdbbase->loadCdefs || duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbRecordType = ptempListNode->item;
|
||||
|
||||
@@ -781,14 +782,14 @@ static void dbRecordtypeBody(void)
|
||||
ellInit(&pdbRecordType->attributeList);
|
||||
ellInit(&pdbRecordType->recList);
|
||||
ellInit(&pdbRecordType->devList);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pdbRecordType->name,
|
||||
&pdbbase->recordTypeList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,pdbRecordType->name,
|
||||
&savedPdbbase->recordTypeList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
} else {
|
||||
pgphentry->userPvt = pdbRecordType;
|
||||
}
|
||||
ellAdd(&pdbbase->recordTypeList,&pdbRecordType->node);
|
||||
ellAdd(&savedPdbbase->recordTypeList,&pdbRecordType->node);
|
||||
}
|
||||
|
||||
static void dbDevice(char *recordtype,char *linktype,
|
||||
@@ -798,7 +799,7 @@ static void dbDevice(char *recordtype,char *linktype,
|
||||
dbRecordType *pdbRecordType;
|
||||
GPHENTRY *pgphentry;
|
||||
int i,link_type;
|
||||
pgphentry = gphFind(pdbbase->pgpHash,recordtype,&pdbbase->recordTypeList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,recordtype,&savedPdbbase->recordTypeList);
|
||||
if(!pgphentry) {
|
||||
epicsPrintf("Record type \"%s\" not found for device \"%s\"\n",
|
||||
recordtype, choicestring);
|
||||
@@ -819,7 +820,7 @@ static void dbDevice(char *recordtype,char *linktype,
|
||||
return;
|
||||
}
|
||||
pdbRecordType = (dbRecordType *)pgphentry->userPvt;
|
||||
pgphentry = gphFind(pdbbase->pgpHash,choicestring,&pdbRecordType->devList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,choicestring,&pdbRecordType->devList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
}
|
||||
@@ -827,7 +828,7 @@ static void dbDevice(char *recordtype,char *linktype,
|
||||
pdevSup->name = epicsStrDup(dsetname);
|
||||
pdevSup->choice = epicsStrDup(choicestring);
|
||||
pdevSup->link_type = link_type;
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
} else {
|
||||
@@ -845,18 +846,18 @@ static void dbDriver(char *name)
|
||||
yyerrorAbort("dbDriver: Driver name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->drvList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
}
|
||||
pdrvSup = dbCalloc(1,sizeof(drvSup));
|
||||
pdrvSup->name = epicsStrDup(name);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pdrvSup->name,&pdbbase->drvList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,pdrvSup->name,&savedPdbbase->drvList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
pgphentry->userPvt = pdrvSup;
|
||||
ellAdd(&pdbbase->drvList,&pdrvSup->node);
|
||||
ellAdd(&savedPdbbase->drvList,&pdrvSup->node);
|
||||
}
|
||||
|
||||
static void dbLinkType(char *name, char *jlif_name)
|
||||
@@ -864,19 +865,19 @@ static void dbLinkType(char *name, char *jlif_name)
|
||||
linkSup *pLinkSup;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->linkList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash, name, &savedPdbbase->linkList);
|
||||
if (pgphentry) {
|
||||
return;
|
||||
}
|
||||
pLinkSup = dbCalloc(1,sizeof(linkSup));
|
||||
pLinkSup->name = epicsStrDup(name);
|
||||
pLinkSup->jlif_name = epicsStrDup(jlif_name);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash, pLinkSup->name, &savedPdbbase->linkList);
|
||||
if (!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
pgphentry->userPvt = pLinkSup;
|
||||
ellAdd(&pdbbase->linkList, &pLinkSup->node);
|
||||
ellAdd(&savedPdbbase->linkList, &pLinkSup->node);
|
||||
}
|
||||
|
||||
static void dbRegistrar(char *name)
|
||||
@@ -888,18 +889,18 @@ static void dbRegistrar(char *name)
|
||||
yyerrorAbort("dbRegistrar: Registrar name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->registrarList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
}
|
||||
ptext = dbCalloc(1,sizeof(dbText));
|
||||
ptext->text = epicsStrDup(name);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->registrarList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,ptext->text,&savedPdbbase->registrarList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
pgphentry->userPvt = ptext;
|
||||
ellAdd(&pdbbase->registrarList,&ptext->node);
|
||||
ellAdd(&savedPdbbase->registrarList,&ptext->node);
|
||||
}
|
||||
|
||||
static void dbFunction(char *name)
|
||||
@@ -911,18 +912,18 @@ static void dbFunction(char *name)
|
||||
yyerrorAbort("dbFunction: Function name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->functionList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
}
|
||||
ptext = dbCalloc(1,sizeof(dbText));
|
||||
ptext->text = epicsStrDup(name);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->functionList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,ptext->text,&savedPdbbase->functionList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
pgphentry->userPvt = ptext;
|
||||
ellAdd(&pdbbase->functionList,&ptext->node);
|
||||
ellAdd(&savedPdbbase->functionList,&ptext->node);
|
||||
}
|
||||
|
||||
static void dbVariable(char *name, char *type)
|
||||
@@ -934,19 +935,19 @@ static void dbVariable(char *name, char *type)
|
||||
yyerrorAbort("dbVariable: Variable name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->variableList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
}
|
||||
pvar = dbCalloc(1,sizeof(dbVariableDef));
|
||||
pvar->name = epicsStrDup(name);
|
||||
pvar->type = epicsStrDup(type);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pvar->name,&pdbbase->variableList);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,pvar->name,&savedPdbbase->variableList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
pgphentry->userPvt = pvar;
|
||||
ellAdd(&pdbbase->variableList,&pvar->node);
|
||||
ellAdd(&savedPdbbase->variableList,&pvar->node);
|
||||
}
|
||||
|
||||
static void dbBreakHead(char *name)
|
||||
@@ -958,7 +959,7 @@ static void dbBreakHead(char *name)
|
||||
yyerrorAbort("dbBreakHead: Breaktable name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList);
|
||||
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->bptList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
return;
|
||||
@@ -1042,17 +1043,17 @@ static void dbBreakBody(void)
|
||||
/* Continue with last slope beyond the final point */
|
||||
paBrkInt[number-1].slope = paBrkInt[number-2].slope;
|
||||
/* Add brkTable in sorted order */
|
||||
pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList);
|
||||
pbrkTable = (brkTable *)ellFirst(&savedPdbbase->bptList);
|
||||
while (pbrkTable) {
|
||||
if (strcmp(pbrkTable->name, pnewbrkTable->name) > 0) {
|
||||
ellInsert(&pdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable),
|
||||
ellInsert(&savedPdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable),
|
||||
(ELLNODE *)pnewbrkTable);
|
||||
break;
|
||||
}
|
||||
pbrkTable = (brkTable *)ellNext(&pbrkTable->node);
|
||||
}
|
||||
if (!pbrkTable) ellAdd(&pdbbase->bptList, &pnewbrkTable->node);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pnewbrkTable->name,&pdbbase->bptList);
|
||||
if (!pbrkTable) ellAdd(&savedPdbbase->bptList, &pnewbrkTable->node);
|
||||
pgphentry = gphAdd(savedPdbbase->pgpHash,pnewbrkTable->name,&savedPdbbase->bptList);
|
||||
if (!pgphentry) {
|
||||
yyerrorAbort("dbBreakBody: gphAdd failed");
|
||||
return;
|
||||
@@ -1103,7 +1104,7 @@ static void dbRecordHead(char *recordType, char *name, int visible)
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
|
||||
pdbentry = dbAllocEntry(pdbbase);
|
||||
pdbentry = dbAllocEntry(savedPdbbase);
|
||||
if (ellCount(&tempList))
|
||||
yyerrorAbort("dbRecordHead: tempList not empty");
|
||||
allocTemp(pdbentry);
|
||||
@@ -1260,7 +1261,7 @@ static void dbAlias(char *name, char *alias)
|
||||
if(dbRecordNameValidate(alias))
|
||||
return;
|
||||
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
dbInitEntry(savedPdbbase, pdbEntry);
|
||||
if (dbFindRecord(pdbEntry, name)) {
|
||||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||||
alias, name);
|
||||
|
||||
@@ -716,13 +716,13 @@ int iocShutdown(void)
|
||||
iterateRecords(doCloseLinks, NULL);
|
||||
initHookAnnounce(initHookAfterCloseLinks);
|
||||
|
||||
if (iocBuildMode == buildIsolated) {
|
||||
/* stop and "join" threads */
|
||||
scanStop();
|
||||
initHookAnnounce(initHookAfterStopScan);
|
||||
callbackStop();
|
||||
initHookAnnounce(initHookAfterStopCallback);
|
||||
} else {
|
||||
/* stop and "join" threads */
|
||||
scanStop();
|
||||
initHookAnnounce(initHookAfterStopScan);
|
||||
callbackStop();
|
||||
initHookAnnounce(initHookAfterStopCallback);
|
||||
|
||||
if (iocBuildMode != buildIsolated) {
|
||||
dbStopServers();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "errMdef.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "special.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
@@ -166,9 +167,9 @@ static int compress_array(compressRecord *prec,
|
||||
}
|
||||
if (prec->n <= 0)
|
||||
prec->n = 1;
|
||||
n = prec->n;
|
||||
if (no_elements < n)
|
||||
if (no_elements < prec->n && prec->pbuf != menuYesNoYES)
|
||||
return 1; /*dont do anything*/
|
||||
n = no_elements;
|
||||
|
||||
/* determine number of samples to take */
|
||||
if (no_elements < nsam * n)
|
||||
@@ -272,7 +273,7 @@ static int array_average(compressRecord *prec,
|
||||
prec->inx = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int compress_scalar(struct compressRecord *prec,double *psource)
|
||||
{
|
||||
double value = *psource;
|
||||
@@ -292,19 +293,13 @@ static int compress_scalar(struct compressRecord *prec,double *psource)
|
||||
/* for scalars, Median not implemented => use average */
|
||||
case (compressALG_N_to_1_Average):
|
||||
case (compressALG_N_to_1_Median):
|
||||
if (inx == 0)
|
||||
*pdest = value;
|
||||
else {
|
||||
*pdest += value;
|
||||
if (inx + 1 >= prec->n)
|
||||
*pdest = *pdest / (inx + 1);
|
||||
}
|
||||
*pdest = (inx * (*pdest) + value) / (inx + 1);
|
||||
break;
|
||||
}
|
||||
inx++;
|
||||
if (inx >= prec->n) {
|
||||
if ((inx >= prec->n) || (prec->pbuf == menuYesNoYES)) {
|
||||
put_value(prec,pdest,1);
|
||||
prec->inx = 0;
|
||||
prec->inx = (inx >= prec->n) ? 0 : inx;
|
||||
return 0;
|
||||
} else {
|
||||
prec->inx = inx;
|
||||
|
||||
@@ -40,7 +40,7 @@ the beginning or the end of the VAL array.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The record-specific fields are described below.
|
||||
The record-specific fields are described below, grouped by functionality.
|
||||
|
||||
=recordtype compress
|
||||
|
||||
@@ -60,10 +60,6 @@ menu(bufferingALG) {
|
||||
}
|
||||
recordtype(compress) {
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The record-specific fields are described below, grouped by functionality.
|
||||
|
||||
=head3 Scanning Parameters
|
||||
|
||||
The compression record has the standard fields for specifying under what
|
||||
@@ -85,7 +81,7 @@ algorithms which can be specified as follows:
|
||||
|
||||
The following fields determine what channel to read and how to compress the data:
|
||||
|
||||
=fields ALG, INP, NSAM, N, ILIL, IHIL, OFF, RES
|
||||
=fields ALG, INP, NSAM, N, ILIL, IHIL, OFF, RES, PBUF
|
||||
|
||||
As stated above, the ALG field specifies which algorithm to be performed on the data.
|
||||
|
||||
@@ -167,6 +163,23 @@ Compress N to 1 samples, taking the median value.
|
||||
|
||||
=back
|
||||
|
||||
The behaviour of the record for partially filled buffers depends on the field PBUF.
|
||||
If PBUF is set to NO, then the record will wait until the buffer is completely full
|
||||
before processing. If PBUF is set to YES, then it will start processing immediately.
|
||||
|
||||
For example, if ALG is set to C<<< N to 1 Average >>> with NSAM equal to 4, N equal
|
||||
to 1, and PBUF set to NO, then the first three times that the compress record is
|
||||
processed it will remain in an undefined state. On the fourth process, the average
|
||||
of all four records will be calculated and placed into the VAL field.
|
||||
|
||||
If PBUF is set to YES, then after each process the average of the first several
|
||||
elements will be calculated.
|
||||
|
||||
Note that PBUF has no impact on the C<<< Average >>> method. If one wishes to have a
|
||||
rolling average computed, then the best way to achieve that is with two compress
|
||||
records: a C<<< Circular buffer >>> which is linked to an C<<< N to 1 Average >>>
|
||||
record with PBUF set to YES.
|
||||
|
||||
The compression record keeps NSAM data samples.
|
||||
|
||||
The field N determines the number of elements to compress into each result.
|
||||
@@ -393,7 +406,15 @@ Scan forward link if necessary, set PACT FALSE, and return.
|
||||
interest(1)
|
||||
menu(compressALG)
|
||||
}
|
||||
field(BALG,DBF_MENU) {
|
||||
field(PBUF,DBF_MENU) {
|
||||
prompt("Use Partial buffers")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_RESET)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
initial("NO")
|
||||
}
|
||||
field(BALG,DBF_MENU) {
|
||||
prompt("Buffering Algorithm")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_RESET)
|
||||
|
||||
@@ -271,7 +271,7 @@ static long special(DBADDR *paddr, int after)
|
||||
} else if(after==1 && fieldIndex >= mbboDirectRecordB0 && fieldIndex <= mbboDirectRecordB1F) {
|
||||
/* Adjust VAL corresponding to the bit changed */
|
||||
epicsUInt8 *pBn = (epicsUInt8 *) paddr->pfield;
|
||||
epicsUInt32 bit = 1 << (pBn - &prec->b0);
|
||||
epicsUInt32 bit = 1u << (pBn - &prec->b0);
|
||||
|
||||
/* Because this is !(VAL and PP), dbPut() will always post a monitor on this B* field
|
||||
* after we return. We must keep track of this change separately from MLST to handle
|
||||
|
||||
@@ -162,7 +162,7 @@ static long process(struct dbCommon *pcommon)
|
||||
recGblFwdLink(prec);
|
||||
prec->pact = FALSE;
|
||||
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
static long special(DBADDR *paddr, int after)
|
||||
|
||||
@@ -146,7 +146,7 @@ __EOF__
|
||||
" prt->papFldDes[${rn}Record${fn}]->size = " .
|
||||
"sizeof(prec->${cn});\n" .
|
||||
" prt->papFldDes[${rn}Record${fn}]->offset = " .
|
||||
"(unsigned short)((char *)&prec->${cn} - (char *)prec);"
|
||||
"(unsigned short)offsetof(${rn}Record, ${cn});"
|
||||
} @fields), << "__EOF__";
|
||||
|
||||
prt->rec_size = sizeof(*prec);
|
||||
|
||||
Reference in New Issue
Block a user