From 636f5517b2ae4ee001dc40fde3dde8417c68b169 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 6 Mar 2023 09:30:02 +0000 Subject: [PATCH 01/42] Add QT Creator 9.x dir to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 94e36205a..b7a25673a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ /modules/Makefile.local O.*/ /QtC-* +/.qtc_* /.vscode/ *.orig *.log From fa00572780a00bea2f57dbff69653b14bd792be0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 6 Mar 2023 15:54:34 +0000 Subject: [PATCH 02/42] update pvData --- modules/pvData | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pvData b/modules/pvData index 9447eacbd..2547514ab 160000 --- a/modules/pvData +++ b/modules/pvData @@ -1 +1 @@ -Subproject commit 9447eacbd2d32b5cad077f1d4dc93f3081e13bb5 +Subproject commit 2547514abb90875bf47dda728ecaa9e30a5ab354 From d9052f71056ab38217344e0f6d395e6937f15965 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 1 Feb 2023 09:06:03 -0800 Subject: [PATCH 03/42] update ci-scripts --- .ci | 2 +- .github/workflows/ci-scripts-build.yml | 49 ++++++-------------------- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/.ci b/.ci index aea790683..1e0e326f7 160000 --- a/.ci +++ b/.ci @@ -1 +1 @@ -Subproject commit aea79068391fe407fcb817c036bc54f26342bdaa +Subproject commit 1e0e326f74ffac4154ce80b5d41c410c754cf5d8 diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index 945fea713..acd8b63cb 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -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 From eea361bf5e3971124c2cc82bd595cebff722b0fd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 4 Apr 2021 13:32:17 -0700 Subject: [PATCH 04/42] Com: Allow runtime bypass of freeListLib By environment or iocsh variable. --- modules/libcom/src/freeList/freeList.h | 2 + modules/libcom/src/freeList/freeListLib.c | 58 ++++++++++++++++------- modules/libcom/src/iocsh/libComRegister.c | 12 +++-- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/modules/libcom/src/freeList/freeList.h b/modules/libcom/src/freeList/freeList.h index 3cf978cb6..a06d8c4db 100644 --- a/modules/libcom/src/freeList/freeList.h +++ b/modules/libcom/src/freeList/freeList.h @@ -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); diff --git a/modules/libcom/src/freeList/freeListLib.c b/modules/libcom/src/freeList/freeListLib.c index 3dfecb5ee..ad0321b26 100644 --- a/modules/libcom/src/freeList/freeListLib.c +++ b/modules/libcom/src/freeList/freeListLib.c @@ -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) diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index f09d5ca48..f089af68d 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -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 @@ -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); } From 1ab474638df506e8c252a62b35fd0ec91cec8804 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Tue, 10 May 2022 14:27:31 +0200 Subject: [PATCH 05/42] Initial test for averaging --- modules/database/test/std/rec/compressTest.c | 91 ++++++++++++++++++- modules/database/test/std/rec/compressTest.db | 9 +- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index d57b6b0a8..33eabca05 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -13,6 +13,7 @@ #include "epicsMath.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) @@ -100,9 +101,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 +231,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 +347,90 @@ void testLIFOCirc(void) testdbCleanup(); } +void +writeToWaveform(DBADDR *addr, long count, ...) { + va_list args; + long i; + double values[count]; + + va_start(args, count); + for (i=0; i< count; i++) { + values[i] = va_arg(args, double); + } + va_end(args); + + testOk1(dbPut(addr, DBF_DOUBLE, values, count)==0); +} + +void +testNto1Average(void) { + double buf; + long nReq; + 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); + + if (dbNameToAddr("wf", &wfaddr)) + testAbort("Failed to get 'wf'"); + if (dbNameToAddr("comp", &caddr)) + testAbort("Failed to get 'comp'"); + + testDiag("Test incomplete input data"); + + dbScanLock(wfaddr.precord); + writeToWaveform(&wfaddr, 3, 1., 2., 3.); + dbScanUnlock(wfaddr.precord); + + 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, 0., 0.01); + + dbScanUnlock(caddr.precord); + + testDiag("Test complete input data"); + + dbScanLock(wfaddr.precord); + writeToWaveform(&wfaddr, 4, 1., 2., 3., 4.); + dbScanUnlock(wfaddr.precord); + + 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); + + testIocShutdownOk(); + + testdbCleanup(); +} + MAIN(compressTest) { - testPlan(116); + testPlan(120); testFIFOCirc(); testLIFOCirc(); + testNto1Average(); return testDone(); } diff --git a/modules/database/test/std/rec/compressTest.db b/modules/database/test/std/rec/compressTest.db index 59fc620ba..9a60d1ab3 100644 --- a/modules/database/test/std/rec/compressTest.db +++ b/modules/database/test/std/rec/compressTest.db @@ -1,7 +1,12 @@ -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(BALG,"$(BALG)") field(NSAM,"$(NSAM)") + field(N, "$(N=1)") } From 3ab22818da0826ffa1f77286e3fa4e74bf12d457 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Tue, 10 May 2022 16:48:44 +0200 Subject: [PATCH 06/42] Added failing test for partially filled buffer average --- .../src/std/rec/compressRecord.dbd.pod | 10 ++- modules/database/test/std/rec/compressTest.c | 68 +++++++++++++------ modules/database/test/std/rec/compressTest.db | 3 +- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod index 87ef67c00..f9040329b 100644 --- a/modules/database/src/std/rec/compressRecord.dbd.pod +++ b/modules/database/src/std/rec/compressRecord.dbd.pod @@ -393,7 +393,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) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 33eabca05..092b4308e 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -17,6 +17,8 @@ #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 *); @@ -34,8 +36,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); @@ -67,8 +68,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); @@ -359,13 +359,15 @@ writeToWaveform(DBADDR *addr, long count, ...) { } va_end(args); + dbScanLock(addr->precord); testOk1(dbPut(addr, DBF_DOUBLE, values, count)==0); + dbScanUnlock(addr->precord); } void testNto1Average(void) { - double buf; - long nReq; + double buf = 0.0; + long nReq = 1; DBADDR wfaddr, caddr; testDiag("Test Average"); @@ -382,55 +384,81 @@ testNto1Average(void) { testIocInitOk(); eltc(1); - if (dbNameToAddr("wf", &wfaddr)) - testAbort("Failed to get 'wf'"); - if (dbNameToAddr("comp", &caddr)) - testAbort("Failed to get 'comp'"); + fetchRecordOrDie("wf", wfaddr); + fetchRecordOrDie("comp", caddr); testDiag("Test incomplete input data"); - dbScanLock(wfaddr.precord); writeToWaveform(&wfaddr, 3, 1., 2., 3.); - dbScanUnlock(wfaddr.precord); dbScanLock(caddr.precord); dbProcess(caddr.precord); - - nReq = 1; 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"); - dbScanLock(wfaddr.precord); writeToWaveform(&wfaddr, 4, 1., 2., 3., 4.); - dbScanUnlock(wfaddr.precord); 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); testIocShutdownOk(); + testdbCleanup(); +} +void +testNto1AveragePartial(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,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); + + testIocShutdownOk(); testdbCleanup(); } MAIN(compressTest) { - testPlan(120); + testPlan(123); testFIFOCirc(); testLIFOCirc(); testNto1Average(); + testNto1AveragePartial(); return testDone(); } diff --git a/modules/database/test/std/rec/compressTest.db b/modules/database/test/std/rec/compressTest.db index 9a60d1ab3..91f7c16ce 100644 --- a/modules/database/test/std/rec/compressTest.db +++ b/modules/database/test/std/rec/compressTest.db @@ -1,11 +1,12 @@ record(ai, "ai") {} record(waveform, "wf") { field(FTVL, "DOUBLE") - field(NELM, "4") + field(NELM, "$(N=1)") } record(compress, "comp") { field(INP, "$(INP) NPP") field(ALG, "$(ALG)") + field(PBUF,"$(PARTIAL=NO)") field(BALG,"$(BALG)") field(NSAM,"$(NSAM)") field(N, "$(N=1)") From 1dc34a02e2265690253a174d3b91a9b070038c9c Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Wed, 11 May 2022 09:45:39 +0200 Subject: [PATCH 07/42] Add test path for single input data --- modules/database/test/std/rec/compressTest.c | 27 +++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 092b4308e..259c9dca4 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -413,6 +413,20 @@ testNto1Average(void) { 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(); } @@ -428,7 +442,7 @@ testNto1AveragePartial(void) { 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"); + testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=1,N=4,PARTIAL=YES"); eltc(0); testIocInitOk(); @@ -449,6 +463,17 @@ testNto1AveragePartial(void) { testDEq(buf, 2.0, 0.01); dbScanUnlock(caddr.precord); + + 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(); } From 84f47716917b47d68ace3f3c63b64059ece56e58 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Wed, 11 May 2022 12:57:25 +0200 Subject: [PATCH 08/42] Single input data test passes --- modules/database/src/std/rec/compressRecord.c | 10 ++++++---- modules/database/test/std/rec/compressTest.c | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/database/src/std/rec/compressRecord.c b/modules/database/src/std/rec/compressRecord.c index d7da7f9b6..7a6e83b4f 100644 --- a/modules/database/src/std/rec/compressRecord.c +++ b/modules/database/src/std/rec/compressRecord.c @@ -28,6 +28,8 @@ #include "dbEvent.h" #include "dbFldTypes.h" #include "errMdef.h" +#include "menuYesNo.h" +#include "menuAlarmStat.h" #include "special.h" #include "recSup.h" #include "recGbl.h" @@ -166,9 +168,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 +274,7 @@ static int array_average(compressRecord *prec, prec->inx = 0; return 0; } - + static int compress_scalar(struct compressRecord *prec,double *psource) { double value = *psource; @@ -302,7 +304,7 @@ static int compress_scalar(struct compressRecord *prec,double *psource) break; } inx++; - if (inx >= prec->n) { + if (inx >= prec->n || prec->pbuf == menuYesNoYES) { put_value(prec,pdest,1); prec->inx = 0; return 0; diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 259c9dca4..8d286ab44 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -437,7 +437,7 @@ testNto1AveragePartial(void) { long nReq = 1; DBADDR wfaddr, caddr; - testDiag("Test Average"); + testDiag("Test Average, Partial"); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); @@ -480,7 +480,7 @@ testNto1AveragePartial(void) { MAIN(compressTest) { - testPlan(123); + testPlan(127); testFIFOCirc(); testLIFOCirc(); testNto1Average(); From b54d4b9a24ee8f7223e0c0f3fc5eb3f23d29c382 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Wed, 11 May 2022 13:16:07 +0200 Subject: [PATCH 09/42] Added test for low value --- modules/database/test/std/rec/compressTest.c | 76 +++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 8d286ab44..4cc0357ec 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -11,6 +11,7 @@ #include "errlog.h" #include "dbAccess.h" #include "epicsMath.h" +#include "menuYesNo.h" #include "aiRecord.h" #include "waveformRecord.h" @@ -463,6 +464,7 @@ testNto1AveragePartial(void) { testDEq(buf, 2.0, 0.01); dbScanUnlock(caddr.precord); + testDiag("Test single entry from wf record"); writeToWaveform(&wfaddr, 1, 6.); @@ -478,12 +480,84 @@ testNto1AveragePartial(void) { 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"); + + testDEq(buf, 1.0, 0.01); + dbScanUnlock(caddr.precord); + + testDiag("Test partial data with PBUF set to YES"); + + ((compressRecord *)caddr.precord)->pbuf = menuYesNoYES; + + dbScanLock(caddr.precord); + 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(); +} + MAIN(compressTest) { - testPlan(127); + testPlan(134); testFIFOCirc(); testLIFOCirc(); testNto1Average(); testNto1AveragePartial(); + testNto1LowValue(); return testDone(); } From bf4a4c6b785f0581c9bfb36bca9752f530d6cbf6 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Wed, 11 May 2022 13:33:29 +0200 Subject: [PATCH 10/42] Added failing test for partial ai average --- modules/database/test/std/rec/compressTest.c | 79 +++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 4cc0357ec..3943ee5d8 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -551,13 +551,90 @@ testNto1LowValue(void) { testdbCleanup(); } +void +testAIPartialAverage(void) { + double buf = 0.0; + long nReq = 1; + DBADDR aiaddr, caddr; + + testDiag("Test 'N to 1 Low Value'"); + + 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); + + buf = 1.; + dbScanLock(aiaddr.precord); + dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); + 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, 1., 0.01); + + buf = 2.; + dbScanLock(aiaddr.precord); + dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); + dbScanUnlock(aiaddr.precord); + + dbScanLock(caddr.precord); + dbProcess(caddr.precord); + if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL)) + testAbort("dbGet failed on compress record"); + + testDEq(buf, 1.5, 0.01); + dbScanUnlock(caddr.precord); + + buf = 3.; + dbScanLock(aiaddr.precord); + dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); + dbScanUnlock(aiaddr.precord); + + 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.01); + dbScanUnlock(caddr.precord); + + buf = 4.; + dbScanLock(aiaddr.precord); + dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); + dbScanUnlock(aiaddr.precord); + + dbScanLock(caddr.precord); + dbProcess(caddr.precord); + if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL)) + testAbort("dbGet failed on compress record"); + + testDEq(buf, 2.5, 0.01); + dbScanUnlock(caddr.precord); + + testIocShutdownOk(); + testdbCleanup(); +} + MAIN(compressTest) { - testPlan(134); + testPlan(138); testFIFOCirc(); testLIFOCirc(); testNto1Average(); testNto1AveragePartial(); + testAIPartialAverage(); testNto1LowValue(); return testDone(); } From 11a4bed9aa6007f3c8617e2eaeac155b30f02dae Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Wed, 11 May 2022 14:47:45 +0200 Subject: [PATCH 11/42] compress_scalar for average works correctly now --- modules/database/src/std/rec/compressRecord.c | 12 +--- modules/database/test/std/rec/compressTest.c | 71 +++++-------------- modules/database/test/std/rec/compressTest.db | 2 +- 3 files changed, 22 insertions(+), 63 deletions(-) diff --git a/modules/database/src/std/rec/compressRecord.c b/modules/database/src/std/rec/compressRecord.c index 7a6e83b4f..b96bcc4aa 100644 --- a/modules/database/src/std/rec/compressRecord.c +++ b/modules/database/src/std/rec/compressRecord.c @@ -294,19 +294,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 || prec->pbuf == menuYesNoYES) { + 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; diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 3943ee5d8..3a9a4a8d2 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -361,7 +361,7 @@ writeToWaveform(DBADDR *addr, long count, ...) { va_end(args); dbScanLock(addr->precord); - testOk1(dbPut(addr, DBF_DOUBLE, values, count)==0); + testOk1(dbPut(addr, DBR_DOUBLE, values, count)==0); dbScanUnlock(addr->precord); } @@ -443,7 +443,7 @@ testNto1AveragePartial(void) { 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,PARTIAL=YES"); + testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=1,N=4,PBUF=YES"); eltc(0); testIocInitOk(); @@ -553,8 +553,11 @@ testNto1LowValue(void) { void testAIPartialAverage(void) { - double buf = 0.0; + double buf = 0.; + double data[4] = {1., 2., 3., 4.}; + double expected[4] = {1., 1.5, 2., 2.5}; long nReq = 1; + int i; DBADDR aiaddr, caddr; testDiag("Test 'N to 1 Low Value'"); @@ -571,57 +574,19 @@ testAIPartialAverage(void) { fetchRecordOrDie("ai", aiaddr); fetchRecordOrDie("comp", caddr); - buf = 1.; - dbScanLock(aiaddr.precord); - dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); - dbScanUnlock(aiaddr.precord); + for (i = 0; i < 4; i++) { + dbScanLock(aiaddr.precord); + testOk1(dbPut(&aiaddr, DBR_DOUBLE, &data[i], 1) == 0); + 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); + 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, 1., 0.01); - - buf = 2.; - dbScanLock(aiaddr.precord); - dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); - dbScanUnlock(aiaddr.precord); - - dbScanLock(caddr.precord); - dbProcess(caddr.precord); - if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL)) - testAbort("dbGet failed on compress record"); - - testDEq(buf, 1.5, 0.01); - dbScanUnlock(caddr.precord); - - buf = 3.; - dbScanLock(aiaddr.precord); - dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); - dbScanUnlock(aiaddr.precord); - - 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.01); - dbScanUnlock(caddr.precord); - - buf = 4.; - dbScanLock(aiaddr.precord); - dbPut(&aiaddr, DBF_FLOAT, &buf, nReq); - dbScanUnlock(aiaddr.precord); - - dbScanLock(caddr.precord); - dbProcess(caddr.precord); - if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL)) - testAbort("dbGet failed on compress record"); - - testDEq(buf, 2.5, 0.01); - dbScanUnlock(caddr.precord); + testDEq(buf, expected[i], 0.01); + } testIocShutdownOk(); testdbCleanup(); @@ -629,7 +594,7 @@ testAIPartialAverage(void) { MAIN(compressTest) { - testPlan(138); + testPlan(142); testFIFOCirc(); testLIFOCirc(); testNto1Average(); diff --git a/modules/database/test/std/rec/compressTest.db b/modules/database/test/std/rec/compressTest.db index 91f7c16ce..2249171f9 100644 --- a/modules/database/test/std/rec/compressTest.db +++ b/modules/database/test/std/rec/compressTest.db @@ -6,7 +6,7 @@ record(waveform, "wf") { record(compress, "comp") { field(INP, "$(INP) NPP") field(ALG, "$(ALG)") - field(PBUF,"$(PARTIAL=NO)") + field(PBUF,"$(PBUF=NO)") field(BALG,"$(BALG)") field(NSAM,"$(NSAM)") field(N, "$(N=1)") From dec23501e106e83a3dd4206775b4bc2a7443fe84 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Wed, 11 May 2022 15:25:23 +0200 Subject: [PATCH 12/42] Added test for array_average --- modules/database/src/std/rec/compressRecord.c | 1 - modules/database/test/std/rec/compressTest.c | 36 ++++++++++++++++++- modules/database/test/std/rec/compressTest.db | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/modules/database/src/std/rec/compressRecord.c b/modules/database/src/std/rec/compressRecord.c index b96bcc4aa..b6fafb4ab 100644 --- a/modules/database/src/std/rec/compressRecord.c +++ b/modules/database/src/std/rec/compressRecord.c @@ -29,7 +29,6 @@ #include "dbFldTypes.h" #include "errMdef.h" #include "menuYesNo.h" -#include "menuAlarmStat.h" #include "special.h" #include "recSup.h" #include "recGbl.h" diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 3a9a4a8d2..0636ddeb9 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -365,6 +365,38 @@ writeToWaveform(DBADDR *addr, long count, ...) { dbScanUnlock(addr->precord); } +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; @@ -531,6 +563,7 @@ testNto1LowValue(void) { 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); dbScanUnlock(caddr.precord); @@ -594,9 +627,10 @@ testAIPartialAverage(void) { MAIN(compressTest) { - testPlan(142); + testPlan(145); testFIFOCirc(); testLIFOCirc(); + testArrayAverage(); testNto1Average(); testNto1AveragePartial(); testAIPartialAverage(); diff --git a/modules/database/test/std/rec/compressTest.db b/modules/database/test/std/rec/compressTest.db index 2249171f9..168bad03b 100644 --- a/modules/database/test/std/rec/compressTest.db +++ b/modules/database/test/std/rec/compressTest.db @@ -1,7 +1,7 @@ record(ai, "ai") {} record(waveform, "wf") { field(FTVL, "DOUBLE") - field(NELM, "$(N=1)") + field(NELM, "4") } record(compress, "comp") { field(INP, "$(INP) NPP") From 373e5440aca61be90ca2c3bbc62a33ae8839bfb4 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Thu, 12 May 2022 08:25:47 +0200 Subject: [PATCH 13/42] General cleanup --- modules/database/test/std/rec/compressTest.c | 49 ++++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 0636ddeb9..220297a0b 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -87,6 +87,23 @@ 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[count]; + + 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); +} + static void testFIFOCirc(void) { @@ -349,25 +366,9 @@ void testLIFOCirc(void) } void -writeToWaveform(DBADDR *addr, long count, ...) { - va_list args; - long i; - double values[count]; - - va_start(args, count); - for (i=0; i< count; i++) { - values[i] = va_arg(args, double); - } - va_end(args); - - dbScanLock(addr->precord); - testOk1(dbPut(addr, DBR_DOUBLE, values, count)==0); - dbScanUnlock(addr->precord); -} - -void testArrayAverage(void) { +testArrayAverage(void) { DBADDR wfaddr, caddr; - + testDiag("Test Array Average"); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); @@ -565,13 +566,11 @@ testNto1LowValue(void) { // We confirm that this hasn't changed i.e. the dbProcess above did nothing testDEq(buf, 1.0, 0.01); - dbScanUnlock(caddr.precord); testDiag("Test partial data with PBUF set to YES"); ((compressRecord *)caddr.precord)->pbuf = menuYesNoYES; - dbScanLock(caddr.precord); dbProcess(caddr.precord); if (dbGet(&caddr, DBR_DOUBLE, &buf, NULL, &nReq, NULL)) testAbort("dbGet failed on compress record"); @@ -585,7 +584,7 @@ testNto1LowValue(void) { } void -testAIPartialAverage(void) { +testAIAveragePartial(void) { double buf = 0.; double data[4] = {1., 2., 3., 4.}; double expected[4] = {1., 1.5, 2., 2.5}; @@ -593,7 +592,7 @@ testAIPartialAverage(void) { int i; DBADDR aiaddr, caddr; - testDiag("Test 'N to 1 Low Value'"); + testDiag("Test 'N to 1 Average' with analog in, PBUF=YES"); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL); @@ -609,7 +608,7 @@ testAIPartialAverage(void) { for (i = 0; i < 4; i++) { dbScanLock(aiaddr.precord); - testOk1(dbPut(&aiaddr, DBR_DOUBLE, &data[i], 1) == 0); + dbPut(&aiaddr, DBR_DOUBLE, &data[i], 1); dbScanUnlock(aiaddr.precord); dbScanLock(caddr.precord); @@ -627,13 +626,13 @@ testAIPartialAverage(void) { MAIN(compressTest) { - testPlan(145); + testPlan(131); testFIFOCirc(); testLIFOCirc(); testArrayAverage(); testNto1Average(); testNto1AveragePartial(); - testAIPartialAverage(); + testAIAveragePartial(); testNto1LowValue(); return testDone(); } From d66e90a0165ff49b7b09c3dd6cf6ccdb8c2a978c Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Thu, 12 May 2022 09:45:48 +0200 Subject: [PATCH 14/42] Fixing 'error C2057: expected constant expression' error --- modules/database/test/std/rec/compressTest.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 220297a0b..98eb1bab7 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -5,6 +5,9 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +#include + +#include "cantProceed.h" #include "dbUnitTest.h" #include "testMain.h" #include "dbLock.h" @@ -91,7 +94,7 @@ void writeToWaveform(DBADDR *addr, long count, ...) { va_list args; long i; - double values[count]; + double *values = (double *)callocMustSucceed(count, sizeof(double), "writeToWaveform"); va_start(args, count); for (i=0; i< count; i++) { @@ -102,6 +105,7 @@ writeToWaveform(DBADDR *addr, long count, ...) { dbScanLock(addr->precord); dbPut(addr, DBR_DOUBLE, values, count); dbScanUnlock(addr->precord); + free(values); } static From 84d96173750a8bce42d37e8bd3f409f519187f57 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Thu, 12 May 2022 10:57:37 +0200 Subject: [PATCH 15/42] Added one more put/process to go over the buffer length Note that it is not really a circular buffer in this case, but a full reset of the buffer to the beginning. This matches the documentation, but it seems valuable to add an explicit test for this case. --- modules/database/test/std/rec/compressTest.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/database/test/std/rec/compressTest.c b/modules/database/test/std/rec/compressTest.c index 98eb1bab7..ed5f31e63 100644 --- a/modules/database/test/std/rec/compressTest.c +++ b/modules/database/test/std/rec/compressTest.c @@ -590,8 +590,12 @@ testNto1LowValue(void) { void testAIAveragePartial(void) { double buf = 0.; - double data[4] = {1., 2., 3., 4.}; - double expected[4] = {1., 1.5, 2., 2.5}; + 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; @@ -610,7 +614,7 @@ testAIAveragePartial(void) { fetchRecordOrDie("ai", aiaddr); fetchRecordOrDie("comp", caddr); - for (i = 0; i < 4; i++) { + for (i = 0; i < 5; i++) { dbScanLock(aiaddr.precord); dbPut(&aiaddr, DBR_DOUBLE, &data[i], 1); dbScanUnlock(aiaddr.precord); @@ -630,7 +634,7 @@ testAIAveragePartial(void) { MAIN(compressTest) { - testPlan(131); + testPlan(132); testFIFOCirc(); testLIFOCirc(); testArrayAverage(); From 579c125b01433ad8d5c16571fb3fbb408c233695 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Thu, 12 May 2022 10:58:53 +0200 Subject: [PATCH 16/42] Updated documentation --- .../src/std/rec/compressRecord.dbd.pod | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod index f9040329b..9323a35cb 100644 --- a/modules/database/src/std/rec/compressRecord.dbd.pod +++ b/modules/database/src/std/rec/compressRecord.dbd.pod @@ -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,18 @@ 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. + The compression record keeps NSAM data samples. The field N determines the number of elements to compress into each result. From b963a4564e3d473fc24be39c3758aee230d11314 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Mon, 16 May 2022 08:55:51 +0200 Subject: [PATCH 17/42] Added commend about PBUF having no effect on 'Average' algorithm --- modules/database/src/std/rec/compressRecord.dbd.pod | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod index 9323a35cb..20c52f496 100644 --- a/modules/database/src/std/rec/compressRecord.dbd.pod +++ b/modules/database/src/std/rec/compressRecord.dbd.pod @@ -175,6 +175,11 @@ 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. From e5ad12e638629ad814c1eb158e742951c6347db1 Mon Sep 17 00:00:00 2001 From: Simon Rose Date: Mon, 6 Mar 2023 14:50:28 +0100 Subject: [PATCH 18/42] Updated RELEASE_NOTES --- documentation/RELEASE_NOTES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index e4d23e753..327b3d7d9 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -15,7 +15,11 @@ should also be read to understand what has changed since earlier releases. ## Changes made on the 7.0 branch since 7.0.7 - +### `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 From e22d74310bc57c0252f4a00a6b8634b0a9ed92aa Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Jul 2022 10:31:17 -0700 Subject: [PATCH 19/42] RTEMS: ensure epicsThreadMustJoin() short-circuits --- modules/libcom/src/osi/os/RTEMS-score/osdThread.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c index ac157f445..110951445 100644 --- a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c +++ b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c @@ -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); { From f902d7000600f8a7f03274e21eb853828f8ee5f2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Jul 2022 10:10:27 -0700 Subject: [PATCH 20/42] switch callback to epicsThreadCreateOpt() --- modules/database/src/ioc/db/callback.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index d58b8fc6f..d1b8f74ef 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -301,13 +301,16 @@ void callbackInit(void) callbackQueue[i].threadsConfigured = callbackThreadsDefault; for (j = 0; j < callbackQueue[i].threadsConfigured; j++) { + epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; + opts.joinable = 0; + 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]); + tid = epicsThreadCreateOpt(threadName, + (EPICSTHREADFUNC)callbackTask, &priorityValue[i], &opts); if (tid == 0) { cantProceed("Failed to spawn callback thread %s\n", threadName); } else { From a9ade9669ac27ee982b432e3a4e3e9fdb751284f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Jul 2022 10:23:38 -0700 Subject: [PATCH 21/42] switch dbScan to epicsThreadCreateOpt() --- modules/database/src/ioc/db/dbScan.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c index f3e354e78..281b90ed7 100644 --- a/modules/database/src/ioc/db/dbScan.c +++ b/modules/database/src/ioc/db/dbScan.c @@ -761,14 +761,16 @@ void scanOnceQueueShow(const int reset) static void initOnce(void) { + epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; + opts.joinable = 0; + 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 +934,16 @@ static void spawnPeriodic(int ind) { periodic_scan_list *ppsl = papPeriodic[ind]; char taskName[20]; + epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; + opts.joinable = 0; + 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); } From 2ff44cb38649b201ea62a69028ecc48f924fc92d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Jul 2022 10:25:35 -0700 Subject: [PATCH 22/42] callback join threads --- modules/database/src/ioc/db/callback.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index d1b8f74ef..556da37b9 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -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; jthreadsConfigured; 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,19 +304,24 @@ 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 = 0; + 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 = epicsThreadCreateOpt(threadName, + callbackQueue[i].threads[j] = tid = epicsThreadCreateOpt(threadName, (EPICSTHREADFUNC)callbackTask, &priorityValue[i], &opts); if (tid == 0) { cantProceed("Failed to spawn callback thread %s\n", threadName); From bded79f14dc7bde32df42478adcafa2c5546a407 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Jul 2022 10:35:03 -0700 Subject: [PATCH 23/42] dbScan join threads --- modules/database/src/ioc/db/dbScan.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c index 281b90ed7..6e8799784 100644 --- a/modules/database/src/ioc/db/dbScan.c +++ b/modules/database/src/ioc/db/dbScan.c @@ -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) @@ -762,7 +766,7 @@ void scanOnceQueueShow(const int reset) static void initOnce(void) { epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; - opts.joinable = 0; + opts.joinable = 1; opts.priority = epicsThreadPriorityScanLow + nPeriodic; opts.stackSize = epicsThreadStackBig; if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { @@ -935,7 +939,7 @@ static void spawnPeriodic(int ind) periodic_scan_list *ppsl = papPeriodic[ind]; char taskName[20]; epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; - opts.joinable = 0; + opts.joinable = 1; opts.priority = epicsThreadPriorityScanLow + ind; opts.stackSize = epicsThreadStackBig; From f430389ee72f08fc1f24fd2960f69c1a14bbf9eb Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Jul 2022 10:47:31 -0700 Subject: [PATCH 24/42] iocShutdown(): Always stop worker threads --- modules/database/src/ioc/misc/iocInit.c | 14 +++++++------- modules/libcom/src/iocsh/initHooks.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/database/src/ioc/misc/iocInit.c b/modules/database/src/ioc/misc/iocInit.c index c26fd0e95..3be83adbe 100644 --- a/modules/database/src/ioc/misc/iocInit.c +++ b/modules/database/src/ioc/misc/iocInit.c @@ -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(); } diff --git a/modules/libcom/src/iocsh/initHooks.h b/modules/libcom/src/iocsh/initHooks.h index 15faf1384..9afc0090a 100644 --- a/modules/libcom/src/iocsh/initHooks.h +++ b/modules/libcom/src/iocsh/initHooks.h @@ -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() */ From 52cc68433fab94852955c6d7f5c84e9572ab6658 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 14 Sep 2022 11:20:27 -0700 Subject: [PATCH 25/42] COMMANDLINE_LIBRARY fallback to $(wildcard $(GNU_DIR) --- configure/toolchain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure/toolchain.c b/configure/toolchain.c index 2f76b475b..da4e8bb2c 100644 --- a/configure/toolchain.c +++ b/configure/toolchain.c @@ -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 From 832abbd3b1fcbf5d15f1beb7da1a4bf3d8a4f0b5 Mon Sep 17 00:00:00 2001 From: Brendan Chandler Date: Tue, 20 Dec 2022 10:19:02 -0600 Subject: [PATCH 26/42] Return an error if subrecord processing fails due to bad INP links If a sub record has an invalid INPx link, it was silently failing (and not running the proc function). This change plumbs in the error, so the put fails and the user knows something went wrong. --- modules/database/src/std/rec/subRecord.c | 2 +- modules/database/test/std/rec/Makefile | 10 ++++ modules/database/test/std/rec/subproctest.c | 55 ++++++++++++++++++++ modules/database/test/std/rec/subproctest.db | 4 ++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 modules/database/test/std/rec/subproctest.c create mode 100644 modules/database/test/std/rec/subproctest.db diff --git a/modules/database/src/std/rec/subRecord.c b/modules/database/src/std/rec/subRecord.c index 91f964123..1ef3af3de 100644 --- a/modules/database/src/std/rec/subRecord.c +++ b/modules/database/src/std/rec/subRecord.c @@ -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) diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index effbbd1cc..816164ebe 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -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 diff --git a/modules/database/test/std/rec/subproctest.c b/modules/database/test/std/rec/subproctest.c new file mode 100644 index 000000000..213a6e88d --- /dev/null +++ b/modules/database/test/std/rec/subproctest.c @@ -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 +#include +#include +#include +#include "registryFunction.h" +#include + +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(); +} diff --git a/modules/database/test/std/rec/subproctest.db b/modules/database/test/std/rec/subproctest.db new file mode 100644 index 000000000..126d3fea5 --- /dev/null +++ b/modules/database/test/std/rec/subproctest.db @@ -0,0 +1,4 @@ +record(sub, "InvalidINPARec") { + field(SNAM, "subproc") + field(INPA, "nonexistent") +} From 90ae51e8f2dd5ab0695e94bdde62da11f76cb0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Wed, 4 Jan 2023 09:32:23 +0100 Subject: [PATCH 27/42] MacOs: Use readline from MacPorts Commit b38ff09f6e2465a1085ac718beb994628c5b7430 and commit d9ca8a70f05bc61a6e4dfbe1518da8ce4f515651 introduced the TAB completion in iocsh. Commit 1f75813a4d4571a9236a9268a11b8806a5878d1d enabled it for MacOs having readline installed via HomeBrew. This commit enables it for MacPorts. --- configure/os/CONFIG_SITE.darwinCommon.darwinCommon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure/os/CONFIG_SITE.darwinCommon.darwinCommon b/configure/os/CONFIG_SITE.darwinCommon.darwinCommon index 6cc9a9de0..d45422401 100644 --- a/configure/os/CONFIG_SITE.darwinCommon.darwinCommon +++ b/configure/os/CONFIG_SITE.darwinCommon.darwinCommon @@ -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 From e1c1bb8b1bacce82c58e133fd34182e5166b38f0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 22 Jan 2023 13:02:00 -0800 Subject: [PATCH 28/42] dbEvent: correct eventsRemaining Only pass eventsRemaining when no queued events have been canceled. Also possible race bt accessing ev_que with locking. --- modules/database/src/ioc/db/dbEvent.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 5891c0afe..4fd4fde06 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -955,6 +955,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 +978,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 +1011,7 @@ 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 ); } LOCKEVQUE (ev_que); From b6626e4f60697d577097e335ce79e1ecbce3fee6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 22 Jan 2023 13:28:10 -0800 Subject: [PATCH 29/42] dbEvent: try to detect possible "stall" event_read() should not return if the last callback was delivered with eventsRemaining!=0 --- modules/database/src/ioc/db/dbEvent.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 4fd4fde06..d5c930dfc 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -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 ); @@ -1012,6 +1014,7 @@ static int event_read ( struct event_que *ev_que ) /* Issue user callback */ ( *user_sub ) ( pevent->user_arg, pevent->chan, eventsRemaining, pfl ); + notifiedRemaining = eventsRemaining; } LOCKEVQUE (ev_que); @@ -1038,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; From bcdeeed20699176c0ef0850a192f7cbaae6a75de Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 7 Mar 2023 14:43:18 +0100 Subject: [PATCH 30/42] fix missing newline at end of file --- modules/libcom/src/freeList/freeList.h | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/libcom/src/freeList/freeList.h b/modules/libcom/src/freeList/freeList.h index a06d8c4db..c8b4680b9 100644 --- a/modules/libcom/src/freeList/freeList.h +++ b/modules/libcom/src/freeList/freeList.h @@ -43,4 +43,3 @@ LIBCOM_API size_t epicsStdCall freeListItemsAvail(void *pvt); #endif #endif /*INCfreeListh*/ - \ No newline at end of file From 172597e0e6348308ae8c24c7a1c200bdd0e0226c Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 7 Mar 2023 15:16:19 +0100 Subject: [PATCH 31/42] avoid accessing dbr_text[type] when type is out of range --- modules/ca/src/client/test_event.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ca/src/client/test_event.cpp b/modules/ca/src/client/test_event.cpp index 1d285ed12..6db6a4a56 100644 --- a/modules/ca/src/client/test_event.cpp +++ b/modules/ca/src/client/test_event.cpp @@ -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] ); From e862f0e95fe79b8ccd6b692176d6e5b790a60abf Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 7 Mar 2023 15:30:00 +0100 Subject: [PATCH 32/42] fix warning "if clause does not guard..." --- modules/database/src/ioc/db/dbUnitTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index 805031bc2..2c7efd8a9 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -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"); From a9fd57a865a32088c6457e86281d2ef050380d8f Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 7 Mar 2023 15:46:21 +0000 Subject: [PATCH 33/42] editorconfig: add initial version forcing final newlines See #337 --- .editorconfig | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..b6a8712f6 --- /dev/null +++ b/.editorconfig @@ -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 From c7a769e5da9ce7253e58a321e2fd7e0da1ba8cf3 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 7 Mar 2023 16:55:29 +0000 Subject: [PATCH 34/42] editorconfig: add workflow to check if config is upheld --- .github/workflows/check-editorconfig.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/check-editorconfig.yml diff --git a/.github/workflows/check-editorconfig.yml b/.github/workflows/check-editorconfig.yml new file mode 100644 index 000000000..07f6d6c94 --- /dev/null +++ b/.github/workflows/check-editorconfig.yml @@ -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 From 0b01fb20db0102d045a0daba24453ea6f37c8a96 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 7 Mar 2023 17:25:11 +0100 Subject: [PATCH 35/42] make buffer large enough for any argument value --- modules/libcom/src/flex/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libcom/src/flex/misc.c b/modules/libcom/src/flex/misc.c index a1c94667a..7b79fca3b 100644 --- a/modules/libcom/src/flex/misc.c +++ b/modules/libcom/src/flex/misc.c @@ -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 ) { From 3e51491628096642b26db7ca34999673463de44f Mon Sep 17 00:00:00 2001 From: Henrique Silva Date: Tue, 7 Mar 2023 17:36:24 +0100 Subject: [PATCH 36/42] Fix generalTimeReport interest_level argument type Setting to iocshArgInt actually makes the function verbosity to be able to be controlled by the value --- modules/libcom/src/iocsh/libComRegister.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index f089af68d..079bdc7ac 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -448,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" From 531a769007a1cb3f89ccfcc8edffbc3ba7692e5f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 20 Jul 2022 11:54:58 -0700 Subject: [PATCH 37/42] fix rtems_ne2kpci_driver_attach prototype --- modules/libcom/RTEMS/rtems_netconfig.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/libcom/RTEMS/rtems_netconfig.c b/modules/libcom/RTEMS/rtems_netconfig.c index 6272d81a6..d9267ccaa 100644 --- a/modules/libcom/RTEMS/rtems_netconfig.c +++ b/modules/libcom/RTEMS/rtems_netconfig.c @@ -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 Date: Sat, 25 Feb 2023 19:14:15 -0800 Subject: [PATCH 38/42] dbdToRecordtypeH use offsetof() quiets UB sanitizer --- modules/database/src/tools/dbdToRecordtypeH.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/tools/dbdToRecordtypeH.pl b/modules/database/src/tools/dbdToRecordtypeH.pl index 0bb3839fe..bfb14aa2e 100644 --- a/modules/database/src/tools/dbdToRecordtypeH.pl +++ b/modules/database/src/tools/dbdToRecordtypeH.pl @@ -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); From d3f93746a84d4bd49c87a3f01e90143b7cc3e896 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 26 Feb 2023 11:52:50 -0800 Subject: [PATCH 39/42] 1<<31 upsets ubsan --- modules/database/src/std/rec/mbboDirectRecord.c | 2 +- modules/database/test/std/rec/mbbioDirectTest.c | 2 +- modules/libcom/src/yacc/closure.c | 4 ++-- modules/libcom/src/yacc/warshall.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/database/src/std/rec/mbboDirectRecord.c b/modules/database/src/std/rec/mbboDirectRecord.c index c41cc6d7a..6d3347abb 100644 --- a/modules/database/src/std/rec/mbboDirectRecord.c +++ b/modules/database/src/std/rec/mbboDirectRecord.c @@ -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 diff --git a/modules/database/test/std/rec/mbbioDirectTest.c b/modules/database/test/std/rec/mbbioDirectTest.c index 304217d37..ed2c5c39d 100644 --- a/modules/database/test/std/rec/mbbioDirectTest.c +++ b/modules/database/test/std/rec/mbbioDirectTest.c @@ -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); diff --git a/modules/libcom/src/yacc/closure.c b/modules/libcom/src/yacc/closure.c index 548b93889..5d7c0f5d4 100644 --- a/modules/libcom/src/yacc/closure.c +++ b/modules/libcom/src/yacc/closure.c @@ -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) diff --git a/modules/libcom/src/yacc/warshall.c b/modules/libcom/src/yacc/warshall.c index 186fdbce9..1362e49cb 100644 --- a/modules/libcom/src/yacc/warshall.c +++ b/modules/libcom/src/yacc/warshall.c @@ -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; From 3b484f58d3f12fa0fb9d15fc09f0f654569f97c8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 6 Mar 2023 14:43:31 +0000 Subject: [PATCH 40/42] for links, treat "" the same as unset. --- modules/database/src/ioc/db/dbConstLink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/database/src/ioc/db/dbConstLink.c b/modules/database/src/ioc/db/dbConstLink.c index ec2d3184f..46ffd92b2 100644 --- a/modules/database/src/ioc/db/dbConstLink.c +++ b/modules/database/src/ioc/db/dbConstLink.c @@ -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 */ From 151256533f04b8061717b0582310793115910d35 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 9 Mar 2023 09:49:32 +0000 Subject: [PATCH 41/42] renamed hidden pdbbase in dbd parser avoid confusion with non-static pdbbase global --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 109 +++++++++--------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 719ffc9db..0fd250826 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -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); From 5eff3803a83fbc3ea855b63841f5359c09830ade Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 10 Mar 2023 12:02:29 +0000 Subject: [PATCH 42/42] update release notes --- documentation/RELEASE_NOTES.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 327b3d7d9..c8c3d327d 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -15,6 +15,33 @@ should also be read to understand what has changed since earlier releases. ## Changes made on the 7.0 branch since 7.0.7 +### 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