Merge branch '7.0' (after codeathon 2023) into PSI-7.0
This commit is contained in:
2
.ci
2
.ci
Submodule .ci updated: aea7906839...1e0e326f74
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
# Documentation for this file: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
# Unix-style newlines ending every file,
|
||||
# as some compilers complain about files not ending in newline
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
13
.github/workflows/check-editorconfig.yml
vendored
Normal file
13
.github/workflows/check-editorconfig.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Check EditorConfig
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
editorconfig:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: EditorConfig-Action
|
||||
uses: greut/eclint-action@v0
|
||||
49
.github/workflows/ci-scripts-build.yml
vendored
49
.github/workflows/ci-scripts-build.yml
vendored
@@ -43,11 +43,8 @@ jobs:
|
||||
env:
|
||||
CMP: ${{ matrix.cmp }}
|
||||
BCFG: ${{ matrix.configuration }}
|
||||
WINE: ${{ matrix.wine }}
|
||||
RTEMS: ${{ matrix.rtems }}
|
||||
RTEMS_TARGET: ${{ matrix.rtems_target }}
|
||||
CI_CROSS_TARGETS: ${{ matrix.cross }}
|
||||
EXTRA: ${{ matrix.extra }}
|
||||
EXTRA1: ${{ matrix.extra1 }}
|
||||
TEST: ${{ matrix.test }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -57,13 +54,13 @@ jobs:
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
wine: "64"
|
||||
cross: "windows-x64-mingw"
|
||||
name: "Ub-20 gcc-9 + MinGW"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
wine: "64"
|
||||
cross: "windows-x64-mingw"
|
||||
name: "Ub-20 gcc-9 + MinGW, static"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
@@ -75,8 +72,7 @@ jobs:
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
extra: "CMD_CFLAGS=-funsigned-char"
|
||||
extra1: "CMD_CXXFLAGS=-funsigned-char"
|
||||
extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char"
|
||||
name: "Ub-20 gcc-9 unsigned char"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
@@ -93,67 +89,42 @@ jobs:
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
rtems: "5"
|
||||
rtems_target: RTEMS-pc686-qemu
|
||||
cross: "RTEMS-pc686-qemu@5"
|
||||
name: "Ub-20 gcc-9 + RT-5.1 pc686"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
rtems: "5"
|
||||
rtems_target: RTEMS-beatnik
|
||||
cross: "RTEMS-beatnik@5"
|
||||
test: NO
|
||||
name: "Ub-20 gcc-9 + RT-5.1 beatnik"
|
||||
|
||||
# Only build one RTEMS target per CPU family
|
||||
# unless it's running the tests
|
||||
#
|
||||
# - os: ubuntu-20.04
|
||||
# cmp: gcc
|
||||
# configuration: default
|
||||
# rtems: "5"
|
||||
# rtems_target: RTEMS-mvme3100
|
||||
# test: NO
|
||||
# name: "Ub-20 gcc-9 + RT-5.1 mvme3100"
|
||||
#
|
||||
# - os: ubuntu-20.04
|
||||
# cmp: gcc
|
||||
# configuration: default
|
||||
# rtems: "5"
|
||||
# rtems_target: RTEMS-qoriq_e500
|
||||
# test: NO
|
||||
# name: "Ub-20 gcc-9 + RT-5.1 qoriq_e500"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
rtems: "5"
|
||||
rtems_target: RTEMS-xilinx_zynq_a9_qemu
|
||||
cross: "RTEMS-xilinx_zynq_a9_qemu@5"
|
||||
test: NO
|
||||
name: "Ub-20 gcc-9 + RT-5.1 xilinx_zynq_a9_qemu"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
rtems: "5"
|
||||
rtems_target: RTEMS-uC5282
|
||||
cross: "RTEMS-uC5282@5"
|
||||
test: NO
|
||||
name: "Ub-20 gcc-9 + RT-5.1 uC5282"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
rtems: "4.10"
|
||||
name: "Ub-20 gcc-9 + RT-4.10"
|
||||
rtems_target: RTEMS-pc386-qemu
|
||||
cross: "RTEMS-pc386-qemu@4.10"
|
||||
test: NO
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
rtems: "4.9"
|
||||
name: "Ub-20 gcc-9 + RT-4.9"
|
||||
rtems_target: RTEMS-pc386-qemu
|
||||
cross: "RTEMS-pc386-qemu@4.9"
|
||||
|
||||
- os: macos-latest
|
||||
cmp: clang
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@
|
||||
/modules/Makefile.local
|
||||
O.*/
|
||||
/QtC-*
|
||||
/.qtc_*
|
||||
/.vscode/
|
||||
*.orig
|
||||
*.log
|
||||
|
||||
@@ -12,6 +12,9 @@ ifneq (,$(wildcard /opt/homebrew))
|
||||
else ifneq (,$(wildcard /usr/local/Homebrew))
|
||||
# Default location on x86_64
|
||||
HOMEBREW_DIR = /usr/local
|
||||
else ifneq (,$(wildcard /opt/local/include/readline))
|
||||
# MacPorts
|
||||
READLINE_DIR = /opt/local
|
||||
endif
|
||||
|
||||
# Look for Homebrew's readline
|
||||
|
||||
@@ -45,5 +45,5 @@ COMMANDLINE_LIBRARY ?= READLINE
|
||||
COMMANDLINE_LIBRARY ?= EPICS
|
||||
# endif
|
||||
#else
|
||||
COMMANDLINE_LIBRARY ?= EPICS
|
||||
COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS))
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,38 @@ should also be read to understand what has changed since earlier releases.
|
||||
|
||||
## Changes made on the 7.0 branch since 7.0.7
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
### dbEvent eventsRemaining missed on cancel
|
||||
|
||||
In some cases, RSRV may queue a subscription update, but not flush it.
|
||||
This partially addresses this issue.
|
||||
|
||||
### subRecord on bad INP links
|
||||
|
||||
Previously, if a subRecord has an invalid `INP*` link, it was silently failing
|
||||
(and not running the proc function). Now the the status code returned by the
|
||||
subroutine is returned from `dbProcess()`.
|
||||
|
||||
### COMMANDLINE_LIBRARY fallback to GNU_DIR
|
||||
|
||||
Fall back to the previous behavior when searching for `readline.h` with older compilers.
|
||||
|
||||
### Search for readline installed via HomeBrew.
|
||||
|
||||
Look for `/opt/local/include/readline` on OSX.
|
||||
|
||||
### Always stop worker threads
|
||||
|
||||
The SCAN and callback threads are now stopped during normal IOC shutdown.
|
||||
|
||||
### Allow runtime bypass of free list allocator
|
||||
|
||||
The environment variable `$EPICS_FREELIST_BYPASS` may be set to `YES` to cause the `freeListLib` functions to always call directly to `malloc()`/`free()`. May be useful when troubleshooting some kinds of memory allocation bugs which would otherwise be "hidden". eg. use-after-free data races. This may also improve the results of dynamic analysis tools which are not aware of this internal free list.
|
||||
|
||||
### `compress` record enhancement
|
||||
|
||||
The compress record now supports the use of partially-filled buffers when using
|
||||
any of the N-to-one algorithms. This is achieved by setting the new field `PBUF`
|
||||
to `YES`.
|
||||
|
||||
### Add conditional output (OOPT) to the longout record
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ extern "C" void epicsStdCall ca_dump_dbr (
|
||||
|
||||
if ( INVALID_DB_REQ ( type ) ) {
|
||||
printf ( "bad DBR type %ld\n", type );
|
||||
return;
|
||||
}
|
||||
|
||||
printf ( "%s\t", dbr_text[type] );
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -167,6 +167,16 @@ asyncproctest_SRCS += asyncproctest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/asyncproctest.dbd ../asyncproctest.db
|
||||
TESTS += asyncproctest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/subproctest.dbd
|
||||
DBDDEPENDS_FILES += subproctest.dbd$(DEP)
|
||||
subproctest_DBD += base.dbd
|
||||
TESTPROD_HOST += subproctest
|
||||
subproctest_SRCS += subproctest.c
|
||||
subproctest_SRCS += subproctest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/subproctest.dbd ../subproctest.db
|
||||
TESTS += subproctest
|
||||
|
||||
|
||||
TESTPROD_HOST += linkFilterTest
|
||||
linkFilterTest_SRCS += linkFilterTest.c
|
||||
linkFilterTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
@@ -5,17 +5,24 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "dbLock.h"
|
||||
#include "errlog.h"
|
||||
#include "dbAccess.h"
|
||||
#include "epicsMath.h"
|
||||
#include "menuYesNo.h"
|
||||
|
||||
#include "aiRecord.h"
|
||||
#include "waveformRecord.h"
|
||||
#include "compressRecord.h"
|
||||
|
||||
#define testDEq(A,B,D) testOk(fabs((A)-(B))<(D), #A " (%f) ~= " #B " (%f)", A, B)
|
||||
#define fetchRecordOrDie(recname, addr) if (dbNameToAddr(recname, &addr)) {testAbort("Unknown PV '%s'", recname);}
|
||||
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
@@ -33,8 +40,7 @@ void checkArrD(const char *pv, long elen, double a, double b, double c, double d
|
||||
expect[2] = c;
|
||||
expect[3] = d;
|
||||
|
||||
if (dbNameToAddr(pv, &addr))
|
||||
testAbort("Unknown PV '%s'", pv);
|
||||
fetchRecordOrDie(pv, addr);
|
||||
|
||||
if (dbGet(&addr, DBR_DOUBLE, buf, NULL, &nReq, NULL))
|
||||
testAbort("Failed to get '%s'", pv);
|
||||
@@ -66,8 +72,7 @@ void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32
|
||||
expect[2] = c;
|
||||
expect[3] = d;
|
||||
|
||||
if (dbNameToAddr(pv, &addr))
|
||||
testAbort("Unknown PV '%s'", pv);
|
||||
fetchRecordOrDie(pv, addr);
|
||||
|
||||
if (dbGet(&addr, DBR_LONG, buf, NULL, &nReq, NULL))
|
||||
testAbort("Failed to get '%s'", pv);
|
||||
@@ -85,6 +90,24 @@ void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
writeToWaveform(DBADDR *addr, long count, ...) {
|
||||
va_list args;
|
||||
long i;
|
||||
double *values = (double *)callocMustSucceed(count, sizeof(double), "writeToWaveform");
|
||||
|
||||
va_start(args, count);
|
||||
for (i=0; i< count; i++) {
|
||||
values[i] = va_arg(args, double);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
dbScanLock(addr->precord);
|
||||
dbPut(addr, DBR_DOUBLE, values, count);
|
||||
dbScanUnlock(addr->precord);
|
||||
free(values);
|
||||
}
|
||||
|
||||
static
|
||||
void testFIFOCirc(void)
|
||||
{
|
||||
@@ -100,9 +123,9 @@ void testFIFOCirc(void)
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4");
|
||||
testdbReadDatabase("compressTest.db", NULL, "INP=ai,ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4");
|
||||
|
||||
vrec = (aiRecord*)testdbRecordPtr("val");
|
||||
vrec = (aiRecord*)testdbRecordPtr("ai");
|
||||
crec = (compressRecord*)testdbRecordPtr("comp");
|
||||
|
||||
eltc(0);
|
||||
@@ -230,9 +253,9 @@ void testLIFOCirc(void)
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("compressTest.db", NULL,
|
||||
"ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4");
|
||||
"INP=ai,ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4");
|
||||
|
||||
vrec = (aiRecord*)testdbRecordPtr("val");
|
||||
vrec = (aiRecord*)testdbRecordPtr("ai");
|
||||
crec = (compressRecord*)testdbRecordPtr("comp");
|
||||
|
||||
eltc(0);
|
||||
@@ -346,10 +369,278 @@ void testLIFOCirc(void)
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
void
|
||||
testArrayAverage(void) {
|
||||
DBADDR wfaddr, caddr;
|
||||
|
||||
testDiag("Test Array Average");
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=Average,BALG=FIFO Buffer,NSAM=4,N=2");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
fetchRecordOrDie("wf", wfaddr);
|
||||
fetchRecordOrDie("comp", caddr);
|
||||
|
||||
writeToWaveform(&wfaddr, 4, 1., 2., 3., 4.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
|
||||
writeToWaveform(&wfaddr, 4, 2., 4., 6., 8.);
|
||||
|
||||
dbProcess(caddr.precord);
|
||||
|
||||
checkArrD("comp", 4, 1.5, 3., 4.5, 6.);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
void
|
||||
testNto1Average(void) {
|
||||
double buf = 0.0;
|
||||
long nReq = 1;
|
||||
DBADDR wfaddr, caddr;
|
||||
|
||||
testDiag("Test Average");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=1,N=4");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
fetchRecordOrDie("wf", wfaddr);
|
||||
fetchRecordOrDie("comp", caddr);
|
||||
|
||||
testDiag("Test incomplete input data");
|
||||
|
||||
writeToWaveform(&wfaddr, 3, 1., 2., 3.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testOk1(nReq == 0);
|
||||
testDEq(buf, 0., 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testDiag("Test complete input data");
|
||||
|
||||
writeToWaveform(&wfaddr, 4, 1., 2., 3., 4.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
nReq = 1;
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testDEq(buf, 2.5, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testDiag("Test single input data");
|
||||
|
||||
writeToWaveform(&wfaddr, 1, 5.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
nReq = 1;
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
// Assert that nothing has changed from before
|
||||
testDEq(buf, 2.5, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
void
|
||||
testNto1AveragePartial(void) {
|
||||
double buf = 0.0;
|
||||
long nReq = 1;
|
||||
DBADDR wfaddr, caddr;
|
||||
|
||||
testDiag("Test Average, Partial");
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=1,N=4,PBUF=YES");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("Test incomplete input data");
|
||||
|
||||
fetchRecordOrDie("wf", wfaddr);
|
||||
fetchRecordOrDie("comp", caddr);
|
||||
|
||||
writeToWaveform(&wfaddr, 3, 1., 2., 3.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testDEq(buf, 2.0, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testDiag("Test single entry from wf record");
|
||||
|
||||
writeToWaveform(&wfaddr, 1, 6.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testDEq(buf, 6.0, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
void
|
||||
testNto1LowValue(void) {
|
||||
double buf = 0.0;
|
||||
long nReq = 1;
|
||||
DBADDR wfaddr, caddr;
|
||||
|
||||
testDiag("Test 'N to 1 Low Value'");
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Low Value,BALG=FIFO Buffer,NSAM=1,N=4");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
fetchRecordOrDie("wf", wfaddr);
|
||||
fetchRecordOrDie("comp", caddr);
|
||||
|
||||
testDiag("Test full array");
|
||||
|
||||
writeToWaveform(&wfaddr, 4, 1., 2., 3., 4.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testDEq(buf, 1.0, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
writeToWaveform(&wfaddr, 4, 4., 3., 2., 1.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testDEq(buf, 1.0, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testDiag("Test partial data with PBUF set to NO");
|
||||
|
||||
writeToWaveform(&wfaddr, 3, 5., 6., 7.);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
// We confirm that this hasn't changed i.e. the dbProcess above did nothing
|
||||
testDEq(buf, 1.0, 0.01);
|
||||
|
||||
testDiag("Test partial data with PBUF set to YES");
|
||||
|
||||
((compressRecord *)caddr.precord)->pbuf = menuYesNoYES;
|
||||
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
|
||||
testDEq(buf, 5.0, 0.01);
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
void
|
||||
testAIAveragePartial(void) {
|
||||
double buf = 0.;
|
||||
double data[5] = {1., 2., 3., 4., 5.};
|
||||
/*
|
||||
* Note that the fifth dbPut essentially resets the circular buffer, so the
|
||||
* average is once again the average of the _first_ entry alone.
|
||||
*/
|
||||
double expected[5] = {1., 1.5, 2., 2.5, 5.};
|
||||
long nReq = 1;
|
||||
int i;
|
||||
DBADDR aiaddr, caddr;
|
||||
|
||||
testDiag("Test 'N to 1 Average' with analog in, PBUF=YES");
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("compressTest.db", NULL, "INP=ai,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=1,N=4,PBUF=YES");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
fetchRecordOrDie("ai", aiaddr);
|
||||
fetchRecordOrDie("comp", caddr);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
dbScanLock(aiaddr.precord);
|
||||
dbPut(&aiaddr, DBR_DOUBLE, &data[i], 1);
|
||||
dbScanUnlock(aiaddr.precord);
|
||||
|
||||
dbScanLock(caddr.precord);
|
||||
dbProcess(caddr.precord);
|
||||
if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL))
|
||||
testAbort("dbGet failed on compress record");
|
||||
dbScanUnlock(caddr.precord);
|
||||
|
||||
testDEq(buf, expected[i], 0.01);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
MAIN(compressTest)
|
||||
{
|
||||
testPlan(116);
|
||||
testPlan(132);
|
||||
testFIFOCirc();
|
||||
testLIFOCirc();
|
||||
testArrayAverage();
|
||||
testNto1Average();
|
||||
testNto1AveragePartial();
|
||||
testAIAveragePartial();
|
||||
testNto1LowValue();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
record(ai, "val") {}
|
||||
record(ai, "ai") {}
|
||||
record(waveform, "wf") {
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "4")
|
||||
}
|
||||
record(compress, "comp") {
|
||||
field(INP, "val NPP")
|
||||
field(INP, "$(INP) NPP")
|
||||
field(ALG, "$(ALG)")
|
||||
field(PBUF,"$(PBUF=NO)")
|
||||
field(BALG,"$(BALG)")
|
||||
field(NSAM,"$(NSAM)")
|
||||
field(N, "$(N=1)")
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ MAIN(mbbioDirectTest)
|
||||
|
||||
testDiag("##### clear bit 31 (0x1f) #####");
|
||||
putN("do%u.B1F", N, 0);
|
||||
value &= ~(1<<31);
|
||||
value &= ~(1u<<31u);
|
||||
testN("val%d", N, value);
|
||||
testmbbioRecords(N, value);
|
||||
|
||||
|
||||
55
modules/database/test/std/rec/subproctest.c
Normal file
55
modules/database/test/std/rec/subproctest.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2022 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* SPDX-License-Identifier: EPICS
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* This test covers tests related to invoking subrecords
|
||||
*/
|
||||
|
||||
#include <testMain.h>
|
||||
#include <dbUnitTest.h>
|
||||
#include <dbAccess.h>
|
||||
#include <iocsh.h>
|
||||
#include "registryFunction.h"
|
||||
#include <subRecord.h>
|
||||
|
||||
static
|
||||
long subproc(subRecord *prec)
|
||||
{
|
||||
prec->proc = 77;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void subproctest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(subproctest)
|
||||
{
|
||||
testPlan(2);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("subproctest.dbd", NULL, NULL);
|
||||
subproctest_registerRecordDeviceDriver(pdbbase);
|
||||
registryFunctionAdd("subproc", (REGISTRYFUNCTION) subproc);
|
||||
testdbReadDatabase("subproctest.db", NULL, "TPRO=0");
|
||||
|
||||
testIocInitOk();
|
||||
testDiag("===== Test that invalid link in INPA field fails a put request ======");
|
||||
|
||||
testdbPutFieldFail(-1, "InvalidINPARec.PROC", DBF_LONG, 1);
|
||||
|
||||
/* Since the put to PROC above fails, subproc() never runs
|
||||
* and the value of PROC will not be set by subproc(). However,
|
||||
* the testdbPutField call above goes through, so we get a partial
|
||||
* result of the PROC field being left as 1. */
|
||||
testdbGetFieldEqual("InvalidINPARec.PROC", DBF_LONG, 1);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
4
modules/database/test/std/rec/subproctest.db
Normal file
4
modules/database/test/std/rec/subproctest.db
Normal file
@@ -0,0 +1,4 @@
|
||||
record(sub, "InvalidINPARec") {
|
||||
field(SNAM, "subproc")
|
||||
field(INPA, "nonexistent")
|
||||
}
|
||||
@@ -54,10 +54,10 @@ static struct rtems_bsdnet_ifconfig loopback_config = {
|
||||
*/
|
||||
#if defined(__i386__)
|
||||
extern int
|
||||
rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach);
|
||||
rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config);
|
||||
static struct rtems_bsdnet_ifconfig ne2k_driver_config = {
|
||||
"ne2", /* name */
|
||||
rtems_ne2kpci_driver_attach, /* attach function */
|
||||
(void*)&rtems_ne2kpci_driver_attach, /* attach function */
|
||||
#if RTEMS_VERSION_INT<VERSION_INT(4,11,0,0)
|
||||
&loopback_config, /* link to next interface */
|
||||
#else
|
||||
|
||||
@@ -663,7 +663,7 @@ int otoi(Char *str)
|
||||
|
||||
char *readable_form(int c)
|
||||
{
|
||||
static char rform[10];
|
||||
static char rform[16];
|
||||
|
||||
if ( (c >= 0 && c < 32) || c >= 127 )
|
||||
{
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
LIBCOM_API extern int freeListBypass;
|
||||
|
||||
LIBCOM_API void epicsStdCall freeListInitPvt(void **ppvt, int size, int malloc);
|
||||
LIBCOM_API void * epicsStdCall freeListCalloc(void *pvt);
|
||||
LIBCOM_API void * epicsStdCall freeListMalloc(void *pvt);
|
||||
|
||||
@@ -26,6 +26,20 @@
|
||||
#include "epicsMutex.h"
|
||||
#include "freeList.h"
|
||||
#include "adjustment.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Bypass free list and directly call malloc() every time? */
|
||||
int freeListBypass
|
||||
#ifdef EPICS_FREELIST_DEBUG
|
||||
= 1;
|
||||
#else
|
||||
= 2; /* checks environment $EPICS_FREELIST_BYPASS */
|
||||
#endif
|
||||
|
||||
epicsExportAddress(int, freeListBypass);
|
||||
|
||||
typedef struct allocMem {
|
||||
struct allocMem *next;
|
||||
@@ -44,10 +58,25 @@ LIBCOM_API void epicsStdCall
|
||||
freeListInitPvt(void **ppvt,int size,int nmalloc)
|
||||
{
|
||||
FREELISTPVT *pfl;
|
||||
int bypass = epicsAtomicGetIntT(&freeListBypass);
|
||||
|
||||
if(bypass==2) {
|
||||
const char *str = getenv("EPICS_FREELIST_BYPASS");
|
||||
|
||||
if(str && epicsStrCaseCmp(str, "YES")==0) {
|
||||
bypass = 1;
|
||||
} else if(!str || str[0]=='\0' || epicsStrCaseCmp(str, "NO")==0) {
|
||||
bypass = 0;
|
||||
} else {
|
||||
errlogPrintf(ERL_WARNING " EPICS_FREELIST_BYPASS expected to be YES, NO, or empty. Not \"%s\"\n", str);
|
||||
}
|
||||
epicsAtomicSetIntT(&freeListBypass, bypass);
|
||||
}
|
||||
|
||||
pfl = callocMustSucceed(1,sizeof(FREELISTPVT), "freeListInitPvt");
|
||||
pfl->size = adjustToWorstCaseAlignment(size);
|
||||
pfl->nmalloc = nmalloc;
|
||||
if(!bypass)
|
||||
pfl->nmalloc = nmalloc; /* nmalloc==0 to bypass */
|
||||
pfl->head = NULL;
|
||||
pfl->mallochead = NULL;
|
||||
pfl->nBlocksAvailable = 0u;
|
||||
@@ -60,28 +89,26 @@ LIBCOM_API void epicsStdCall
|
||||
LIBCOM_API void * epicsStdCall freeListCalloc(void *pvt)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
# ifdef EPICS_FREELIST_DEBUG
|
||||
return callocMustSucceed(1,pfl->size,"freeList Debug Calloc");
|
||||
# else
|
||||
void *ptemp;
|
||||
|
||||
ptemp = freeListMalloc(pvt);
|
||||
if(ptemp) memset((char *)ptemp,0,pfl->size);
|
||||
if(!pfl->nmalloc)
|
||||
ptemp = calloc(1u, pfl->size);
|
||||
else if(!!(ptemp = freeListMalloc(pvt)))
|
||||
memset((char *)ptemp,0,pfl->size);
|
||||
return(ptemp);
|
||||
# endif
|
||||
}
|
||||
|
||||
LIBCOM_API void * epicsStdCall freeListMalloc(void *pvt)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
# ifdef EPICS_FREELIST_DEBUG
|
||||
return callocMustSucceed(1,pfl->size,"freeList Debug Malloc");
|
||||
# else
|
||||
void *ptemp;
|
||||
void **ppnext;
|
||||
allocMem *pallocmem;
|
||||
int i;
|
||||
|
||||
if(!pfl->nmalloc)
|
||||
return malloc(pfl->size);
|
||||
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
ptemp = pfl->head;
|
||||
if(ptemp==0) {
|
||||
@@ -125,18 +152,18 @@ LIBCOM_API void * epicsStdCall freeListMalloc(void *pvt)
|
||||
VALGRIND_MEMPOOL_FREE(pfl, ptemp);
|
||||
VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, pfl->size);
|
||||
return(ptemp);
|
||||
# endif
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall freeListFree(void *pvt,void*pmem)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
# ifdef EPICS_FREELIST_DEBUG
|
||||
memset ( pmem, 0xdd, pfl->size );
|
||||
free(pmem);
|
||||
# else
|
||||
void **ppnext;
|
||||
|
||||
if(!pfl->nmalloc) {
|
||||
free(pmem);
|
||||
return;
|
||||
}
|
||||
|
||||
VALGRIND_MEMPOOL_FREE(pvt, pmem);
|
||||
VALGRIND_MEMPOOL_ALLOC(pvt, pmem, sizeof(void*));
|
||||
|
||||
@@ -146,7 +173,6 @@ LIBCOM_API void epicsStdCall freeListFree(void *pvt,void*pmem)
|
||||
pfl->head = pmem;
|
||||
pfl->nBlocksAvailable++;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
# endif
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall freeListCleanup(void *pvt)
|
||||
|
||||
@@ -95,9 +95,9 @@ typedef enum {
|
||||
|
||||
initHookAtShutdown, /**< Start of iocShutdown() (unit tests only) */
|
||||
initHookAfterCloseLinks, /**< Links disabled/deleted */
|
||||
initHookAfterStopScan, /**< Scan tasks stopped */
|
||||
initHookAfterStopScan, /**< Scan tasks stopped. Prior to UNRELEASED, triggered only by unittest code. */
|
||||
initHookAfterStopCallback, /**< Callback tasks stopped */
|
||||
initHookAfterStopLinks, /**< CA links stopped */
|
||||
initHookAfterStopLinks, /**< CA links stopped. Prior to UNRELEASED, triggered only by unittest code. */
|
||||
initHookBeforeFree, /**< Resource cleanup about to happen */
|
||||
initHookAfterShutdown, /**< End of iocShutdown() */
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "taskwd.h"
|
||||
#include "registry.h"
|
||||
#include "epicsGeneralTime.h"
|
||||
#include "freeList.h"
|
||||
#include "libComRegister.h"
|
||||
|
||||
/* Register the PWD environment variable when the cd IOC shell function is
|
||||
@@ -447,7 +448,7 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
|
||||
/* generalTimeReport */
|
||||
static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv};
|
||||
static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgInt};
|
||||
static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 };
|
||||
static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs,
|
||||
"Display time providers and their priority levels"
|
||||
@@ -467,7 +468,11 @@ static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
|
||||
installLastResortEventProvider();
|
||||
}
|
||||
|
||||
static iocshVarDef asCheckClientIPDef[] = { { "asCheckClientIP", iocshArgInt, 0 }, { NULL, iocshArgInt, NULL } };
|
||||
static iocshVarDef comDefs[] = {
|
||||
{ "asCheckClientIP", iocshArgInt, 0 },
|
||||
{ "freeListBypass", iocshArgInt, 0 },
|
||||
{ NULL, iocshArgInt, NULL }
|
||||
};
|
||||
|
||||
void epicsStdCall libComRegister(void)
|
||||
{
|
||||
@@ -504,6 +509,7 @@ void epicsStdCall libComRegister(void)
|
||||
iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc);
|
||||
iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc);
|
||||
|
||||
asCheckClientIPDef[0].pval = &asCheckClientIP;
|
||||
iocshRegisterVariable(asCheckClientIPDef);
|
||||
comDefs[0].pval = &asCheckClientIP;
|
||||
comDefs[1].pval = &freeListBypass;
|
||||
iocshRegisterVariable(comDefs);
|
||||
}
|
||||
|
||||
@@ -371,6 +371,9 @@ void epicsThreadMustJoin(epicsThreadId id)
|
||||
rtems_id target_tid = (rtems_id)id, self_tid;
|
||||
struct taskVar *v = 0;
|
||||
|
||||
if(!id)
|
||||
return;
|
||||
|
||||
rtems_task_ident (RTEMS_SELF, 0, &self_tid);
|
||||
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ set_first_derives(void)
|
||||
k = 0;
|
||||
}
|
||||
|
||||
if (cword & (1 << k))
|
||||
if (cword & (1u << k))
|
||||
{
|
||||
rp = derives[j];
|
||||
while ((rule = *rp++) >= 0)
|
||||
@@ -152,7 +152,7 @@ closure(short int *nucleus, int n)
|
||||
{
|
||||
for (i = 0; i < BITS_PER_WORD; ++i)
|
||||
{
|
||||
if (word & (1 << i))
|
||||
if (word & (1u << i))
|
||||
{
|
||||
itemno = rrhs[ruleno+i];
|
||||
while (csp < csend && *csp < itemno)
|
||||
|
||||
@@ -26,7 +26,7 @@ transitive_closure(unsigned int *R, int n)
|
||||
|
||||
while (rowj < relend)
|
||||
{
|
||||
if (*ccol & (1 << i))
|
||||
if (*ccol & (1u << i))
|
||||
{
|
||||
rp = rowi;
|
||||
rend = rowj + rowsize;
|
||||
@@ -68,7 +68,7 @@ reflexive_transitive_closure(unsigned int *R, int n)
|
||||
rp = R;
|
||||
while (rp < relend)
|
||||
{
|
||||
*rp |= (1 << i);
|
||||
*rp |= (1u << i);
|
||||
if (++i >= BITS_PER_WORD)
|
||||
{
|
||||
i = 0;
|
||||
|
||||
Reference in New Issue
Block a user