diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index a66c0ad30..5ba14b373 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,12 @@ --> +

compressRecord buffering order

+ +

The compressRecord has a new field BALG which can select between +FIFO (append) and LIFO (prepend) ordering for insertion of new elements. FIFO +ordering is the default, matching the behviour of previous versions.

+

Valgrind Instrumentation

Valgrind is a software debugging suite provided by many Linux distributions. diff --git a/src/std/filters/arr.c b/src/std/filters/arr.c index 801e69277..9e183ac15 100644 --- a/src/std/filters/arr.c +++ b/src/std/filters/arr.c @@ -33,8 +33,7 @@ typedef struct myStruct { static void *myStructFreeList; -static const -chfPluginArgDef opts[] = { +static const chfPluginArgDef opts[] = { chfInt32 (myStruct, start, "s", 0, 1), chfInt32 (myStruct, incr, "i", 0, 1), chfInt32 (myStruct, end, "e", 0, 1), @@ -67,13 +66,16 @@ static int parse_ok(void *pvt) return 0; } -static void freeArray(db_field_log *pfl) { +static void freeArray(db_field_log *pfl) +{ if (pfl->type == dbfl_type_ref) { freeListFree(pfl->u.r.pvt, pfl->u.r.field); } } -static long wrapArrayIndices(long *start, const long increment, long *end, const long no_elements) { +static long wrapArrayIndices(long *start, const long increment, long *end, + const long no_elements) +{ long len = 0; if (*start < 0) *start = no_elements + *start; @@ -88,7 +90,8 @@ static long wrapArrayIndices(long *start, const long increment, long *end, const return len; } -static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) +{ myStruct *my = (myStruct*) pvt; struct dbCommon *prec; struct rset *prset; @@ -97,13 +100,16 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { long nTarget = 0; long offset = 0; long nSource = chan->addr.no_elements; + long capacity = nSource; + void *pdst; - /* Only array data */ - if (pfl->type == dbfl_type_val) { - return pfl; + switch (pfl->type) { + case dbfl_type_val: + /* Only filter arrays */ + break; - /* Extract from record */ - } else if (pfl->type == dbfl_type_rec) { + case dbfl_type_rec: + /* Extract from record */ if (chan->addr.special == SPC_DBADDR && nSource > 1 && (prset = dbGetRset(&chan->addr)) && @@ -122,32 +128,36 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { pfl->field_size = chan->addr.field_size; pfl->no_elements = nTarget; if (nTarget) { - void *pdst = freeListCalloc(my->arrayFreeList); + pdst = freeListCalloc(my->arrayFreeList); if (pdst) { pfl->u.r.dtor = freeArray; pfl->u.r.pvt = my->arrayFreeList; offset = (offset + start) % chan->addr.no_elements; - dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); + dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity, + offset, my->incr); pfl->u.r.field = pdst; } } dbScanUnlock(prec); chan->addr.pfield = pfieldsave; } + break; /* Extract from buffer */ - } else if (pfl->type == dbfl_type_ref) { - void *psrc = pfl->u.r.field; - void *pdst = NULL; - + case dbfl_type_ref: + pdst = NULL; nSource = pfl->no_elements; nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); pfl->no_elements = nTarget; - if (nTarget) { /* Copy the data out */ + if (nTarget) { + /* Copy the data out */ + void *psrc = pfl->u.r.field; + pdst = freeListCalloc(my->arrayFreeList); - if (!pdst) return pfl; + if (!pdst) break; offset = start; - dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, nTarget, nSource, offset, my->incr); + dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, + nTarget, nSource, offset, my->incr); } if (pfl->u.r.dtor) pfl->u.r.dtor(pfl); if (nTarget) { @@ -155,21 +165,22 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { pfl->u.r.pvt = my->arrayFreeList; pfl->u.r.field = pdst; } + break; } return pfl; } static void channelRegisterPost(dbChannel *chan, void *pvt, - chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*) pvt; long start = my->start; long end = my->end; long max = 0; - if (probe->no_elements <= 1) return; /* array data only */ + if (probe->no_elements <= 1) return; /* array data only */ - max = wrapArrayIndices(&start, my->incr, &end, probe->no_elements); /* wrap indices into array */ + max = wrapArrayIndices(&start, my->incr, &end, probe->no_elements); if (max) { if (!my->arrayFreeList) freeListInitPvt(&my->arrayFreeList, max * probe->field_size, 2); @@ -180,7 +191,8 @@ static void channelRegisterPost(dbChannel *chan, void *pvt, *arg_out = pvt; } -static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +static void channel_report(dbChannel *chan, void *pvt, int level, + const unsigned short indent) { myStruct *my = (myStruct*) pvt; printf("%*sArray (arr): start=%d, incr=%d, end=%d\n", indent, "", diff --git a/src/std/rec/compressRecord.c b/src/std/rec/compressRecord.c index 0ecfbd4e5..c6bdbd869 100644 --- a/src/std/rec/compressRecord.c +++ b/src/std/rec/compressRecord.c @@ -89,9 +89,12 @@ static void reset(compressRecord *prec) prec->cvb = 0.0; prec->res = 0; /* allocate memory for the summing buffer for conversions requiring it */ - if (prec->alg == compressALG_Average && prec->sptr == 0){ + if (prec->alg == compressALG_Average && prec->sptr == NULL) { prec->sptr = calloc(prec->nsam, sizeof(double)); } + + if (prec->bptr && prec->nsam) + memset(prec->bptr, 0, prec->nsam * sizeof(double)); } static void monitor(compressRecord *prec) @@ -108,29 +111,32 @@ static void monitor(compressRecord *prec) static void put_value(compressRecord *prec, double *psource, int n) { - epicsInt32 offset = prec->off; - epicsInt32 nuse = prec->nuse; - epicsInt32 nsam = prec->nsam; - double *pdest = prec->bptr + offset; - int i; + int fifo = (prec->balg == bufferingALG_FIFO); + epicsUInt32 offset = prec->off; + epicsUInt32 nuse = prec->nuse; + epicsUInt32 nsam = prec->nsam; - for (i = 0; i < n; i++) { - *pdest = *psource++; - if (++offset >= nsam) { - pdest = prec->bptr; - offset = 0; - } - else - pdest++; - } nuse += n; if (nuse > nsam) nuse = nsam; + + while (n--) { + /* for LIFO, decrement before */ + if (!fifo) + offset = (offset - 1) % nsam; + + prec->bptr[offset] = *psource++; + + /* for FIFO, increment after */ + if (fifo) + offset = (offset + 1) % nsam; + } + prec->off = offset; prec->nuse = nuse; - return; } - + + /* qsort comparison function (for median calculation) */ static int compare(const void *arg1, const void *arg2) { @@ -145,157 +151,167 @@ static int compare(const void *arg1, const void *arg2) static int compress_array(compressRecord *prec, double *psource, int no_elements) { - epicsInt32 i,j; - epicsInt32 nnew; - epicsInt32 nsam=prec->nsam; - double value; - epicsInt32 n; + epicsInt32 i,j; + epicsInt32 n, nnew; + epicsInt32 nsam = prec->nsam; + double value; - /* skip out of limit data */ - if (prec->ilil < prec->ihil){ - while (((*psource < prec->ilil) || (*psource > prec->ihil)) - && (no_elements > 0)){ - no_elements--; - psource++; - } - } - if(prec->n <= 0) prec->n = 1; - n = prec->n; - if(no_elementsalg){ - case (compressALG_N_to_1_Low_Value): - /* compress N to 1 keeping the lowest value */ - for (i = 0; i < nnew; i++){ - value = *psource++; - for (j = 1; j < n; j++, psource++){ - if (value > *psource) value = *psource; - } - put_value(prec,&value,1); - } - break; - case (compressALG_N_to_1_High_Value): - /* compress N to 1 keeping the highest value */ - for (i = 0; i < nnew; i++){ - value = *psource++; - for (j = 1; j < n; j++, psource++){ - if (value < *psource) value = *psource; - } - put_value(prec,&value,1); - } - break; - case (compressALG_N_to_1_Average): - /* compress N to 1 keeping the average value */ - for (i = 0; i < nnew; i++){ - value = 0; - for (j = 0; j < n; j++, psource++) - value += *psource; - value /= n; - put_value(prec,&value,1); - } - break; - case (compressALG_N_to_1_Median): - /* compress N to 1 keeping the median value */ - /* note: sorts source array (OK; it's a work pointer) */ - for (i = 0; i < nnew; i++, psource+=nnew){ - qsort(psource,n,sizeof(double),compare); - value=psource[n/2]; - put_value(prec,&value,1); - } - break; + /* skip out of limit data */ + if (prec->ilil < prec->ihil) { + while (((*psource < prec->ilil) || (*psource > prec->ihil)) + && (no_elements > 0)) { + no_elements--; + psource++; } - return(0); + } + if (prec->n <= 0) + prec->n = 1; + n = prec->n; + if (no_elements < n) + return 1; /*dont do anything*/ + + /* determine number of samples to take */ + if (no_elements < nsam * n) + nnew = (no_elements / n); + else nnew = nsam; + + /* compress according to specified algorithm */ + switch (prec->alg){ + case compressALG_N_to_1_Low_Value: + /* compress N to 1 keeping the lowest value */ + for (i = 0; i < nnew; i++) { + value = *psource++; + for (j = 1; j < n; j++, psource++) { + if (value > *psource) + value = *psource; + } + put_value(prec, &value, 1); + } + break; + case compressALG_N_to_1_High_Value: + /* compress N to 1 keeping the highest value */ + for (i = 0; i < nnew; i++){ + value = *psource++; + for (j = 1; j < n; j++, psource++) { + if (value < *psource) + value = *psource; + } + put_value(prec, &value, 1); + } + break; + case compressALG_N_to_1_Average: + /* compress N to 1 keeping the average value */ + for (i = 0; i < nnew; i++) { + value = 0; + for (j = 0; j < n; j++, psource++) + value += *psource; + value /= n; + put_value(prec, &value, 1); + } + break; + + case compressALG_N_to_1_Median: + /* compress N to 1 keeping the median value */ + /* note: sorts source array (OK; it's a work pointer) */ + for (i = 0; i < nnew; i++, psource += nnew) { + qsort(psource, n, sizeof(double), compare); + value = psource[n / 2]; + put_value(prec, &value, 1); + } + break; + } + return 0; } - + static int array_average(compressRecord *prec, - double *psource,epicsInt32 no_elements) + double *psource, epicsInt32 no_elements) { - epicsInt32 i; - epicsInt32 nnow; - epicsInt32 nsam=prec->nsam; - double *psum; - double multiplier; - epicsInt32 inx=prec->inx; - epicsInt32 nuse,n; + epicsInt32 i; + epicsInt32 nnow; + epicsInt32 nsam=prec->nsam; + double *psum; + double multiplier; + epicsInt32 inx = prec->inx; + epicsInt32 nuse, n; - nuse = nsam; - if(nuse>no_elements) nuse = no_elements; - nnow=nuse; - if(nnow>no_elements) nnow=no_elements; - psum = (double *)prec->sptr; + nuse = nsam; + if (nuse > no_elements) + nuse = no_elements; + nnow = nuse; + if (nnow > no_elements) + nnow=no_elements; + psum = (double *)prec->sptr; - /* add in the new waveform */ - if (inx == 0){ - for (i = 0; i < nnow; i++) - *psum++ = *psource++; - for(i=nnow; in<=0)prec->n=1; - n = prec->n; - if (inxinx = inx; - return(1); - } - if(n>1) { - psum = (double *)prec->sptr; - multiplier = 1.0/((double)n); - for (i = 0; i < nuse; i++, psum++) - *psum = *psum * multiplier; - } - put_value(prec,prec->sptr,nuse); - prec->inx = 0; - return(0); + /* do we need to calculate the result */ + inx++; + if (prec->n <= 0) + prec->n = 1; + n = prec->n; + if (inx < n) { + prec->inx = inx; + return 1; + } + if (n > 1) { + psum = (double *)prec->sptr; + multiplier = 1.0 / n; + for (i = 0; i < nuse; i++, psum++) + *psum = *psum * multiplier; + } + put_value(prec, prec->sptr, nuse); + prec->inx = 0; + return 0; } static int compress_scalar(struct compressRecord *prec,double *psource) { - double value = *psource; - double *pdest=&prec->cvb; - epicsInt32 inx = prec->inx; + double value = *psource; + double *pdest=&prec->cvb; + epicsInt32 inx = prec->inx; - /* compress according to specified algorithm */ - switch (prec->alg){ - case (compressALG_N_to_1_Low_Value): - if ((value < *pdest) || (inx == 0)) - *pdest = value; - break; - case (compressALG_N_to_1_High_Value): - if ((value > *pdest) || (inx == 0)) - *pdest = value; - break; - /* 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); - } - break; - } - inx++; - if(inx>=prec->n) { - put_value(prec,pdest,1); - prec->inx = 0; - return(0); - } else { - prec->inx = inx; - return(1); - } + /* compress according to specified algorithm */ + switch (prec->alg) { + case (compressALG_N_to_1_Low_Value): + if ((value < *pdest) || (inx == 0)) + *pdest = value; + break; + case (compressALG_N_to_1_High_Value): + if ((value > *pdest) || (inx == 0)) + *pdest = value; + break; + /* 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); + } + break; + } + inx++; + if (inx >= prec->n) { + put_value(prec,pdest,1); + prec->inx = 0; + return 0; + } else { + prec->inx = inx; + return 1; + } } - + /*Beginning of record support routines*/ static long init_record(compressRecord *prec, int pass) { @@ -305,6 +321,7 @@ static long init_record(compressRecord *prec, int pass) prec->bptr = calloc(prec->nsam, sizeof(double)); reset(prec); } + return 0; } @@ -350,6 +367,7 @@ static long process(compressRecord *prec) else status = 1; } + /* check event list */ if (status != 1) { prec->udf = FALSE; @@ -358,10 +376,11 @@ static long process(compressRecord *prec) /* process the forward scan link record */ recGblFwdLink(prec); } + prec->pact = FALSE; return 0; } - + static long special(DBADDR *paddr, int after) { compressRecord *prec = (compressRecord *) paddr->precord; @@ -388,18 +407,31 @@ static long cvt_dbaddr(DBADDR *paddr) paddr->field_type = DBF_DOUBLE; paddr->field_size = sizeof(double); paddr->dbr_field_type = DBF_DOUBLE; + + if (prec->balg == bufferingALG_LIFO) + paddr->special = SPC_NOMOD; return 0; } static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) { + /* offset indicates the next element which would be written. + * In FIFO mode offset-1 is the last valid element + * In LIFO mode offset is the first valid element + * (*offset) should be set to the index of the first valid element + */ compressRecord *prec = (compressRecord *) paddr->precord; + epicsUInt32 off = prec->off; + epicsUInt32 nuse = prec->nuse; + epicsUInt32 nsam = prec->nsam; + + *no_elements = nuse; + if (prec->balg == bufferingALG_FIFO) { + *offset = (off - nuse) % nsam; + } else { + *offset = off; + } - *no_elements = prec->nuse; - if (prec->nuse == prec->nsam) - *offset = prec->off; - else - *offset = 0; return 0; } @@ -407,7 +439,8 @@ static long put_array_info(DBADDR *paddr, long nNew) { compressRecord *prec = (compressRecord *) paddr->precord; - prec->off = (prec->off + nNew) % prec->nsam; + if (prec->balg == bufferingALG_FIFO) + prec->off = (prec->off + nNew) % prec->nsam; prec->nuse += nNew; if (prec->nuse > prec->nsam) prec->nuse = prec->nsam; @@ -440,14 +473,14 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) compressRecord *prec = (compressRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { - case indexof(VAL): - case indexof(IHIL): - case indexof(ILIL): - pgd->upper_disp_limit = prec->hopr; - pgd->lower_disp_limit = prec->lopr; - break; - default: - recGblGetGraphicDouble(paddr,pgd); + case indexof(VAL): + case indexof(IHIL): + case indexof(ILIL): + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + break; + default: + recGblGetGraphicDouble(paddr,pgd); } return 0; } @@ -457,14 +490,14 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) compressRecord *prec = (compressRecord *) paddr->precord; switch (dbGetFieldIndex(paddr)) { - case indexof(VAL): - case indexof(IHIL): - case indexof(ILIL): - pcd->upper_ctrl_limit = prec->hopr; - pcd->lower_ctrl_limit = prec->lopr; - break; - default: - recGblGetControlDouble(paddr, pcd); + case indexof(VAL): + case indexof(IHIL): + case indexof(ILIL): + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + break; + default: + recGblGetControlDouble(paddr, pcd); } return 0; } diff --git a/src/std/rec/compressRecord.dbd.pod b/src/std/rec/compressRecord.dbd.pod index 97b84a9a0..93d1e63bf 100644 --- a/src/std/rec/compressRecord.dbd.pod +++ b/src/std/rec/compressRecord.dbd.pod @@ -9,19 +9,34 @@ =title Compress Record (compress) -... +The data compression record is used to collect and compress data from arrays. +When the INP field references a data array field, it immediately compresses the +entire array into an element of an array using one of several algorithms, +overwriting the previous element. If the INP field obtains its value from a +scalar-value field, the compression record will collect a new sample each time +the record is processed and add it to the compressed data array as a circular +buffer. + +The INP link can also specify a constant; however, if this is the case, the +compression algorithms are ignored, and the record support routines merely +return after checking the FLNK field. =head2 Record-specific Menus =head3 Menu compressALG -The ALG field which uses this menu controls the compression algorithm used. - -... +The ALG field which uses this menu controls the compression algorithm used by +the record. =menu compressALG -... +=head3 Menu bufferingALG + +The BALG field which uses this menu controls whether new values are inserted at +the beginning or the end of the VAL array. + +=menu bufferingALG + =head2 Parameter Fields @@ -41,6 +56,10 @@ menu(compressALG) { choice(compressALG_Circular_Buffer,"Circular Buffer") choice(compressALG_N_to_1_Median,"N to 1 Median") } +menu(bufferingALG) { + choice(bufferingALG_FIFO, "FIFO Buffer") + choice(bufferingALG_LIFO, "LIFO Buffer") +} recordtype(compress) { =fields VAL @@ -76,6 +95,13 @@ recordtype(compress) { interest(1) menu(compressALG) } + field(BALG,DBF_MENU) { + prompt("Buffering Algorithm") + promptgroup(GUI_ALARMS) + special(SPC_RESET) + interest(1) + menu(bufferingALG) + } field(NSAM,DBF_ULONG) { prompt("Number of Values") promptgroup(GUI_COMPRESS) diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 5e7a8fb29..f5a60a751 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -38,6 +38,13 @@ testHarness_SRCS += linkRetargetLinkTest.c TESTFILES += ../linkRetargetLink.db TESTS += linkRetargetLinkTest +TESTPROD_HOST += compressTest +compressTest_SRCS += compressTest.c +compressTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += compressTest.c +TESTFILES += ../compressTest.db +TESTS += compressTest + TARGETS += $(COMMON_DIR)/asTestIoc.dbd asTestIoc_DBD += base.dbd asTestIoc_DBD += asTest.dbd diff --git a/src/std/rec/test/compressTest.c b/src/std/rec/test/compressTest.c new file mode 100644 index 000000000..99a937115 --- /dev/null +++ b/src/std/rec/test/compressTest.c @@ -0,0 +1,354 @@ +/*************************************************************************\ +* Copyright (c) 2016 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "dbLock.h" +#include "errlog.h" +#include "dbAccess.h" +#include "epicsMath.h" + +#include "aiRecord.h" +#include "compressRecord.h" + +#define testDEq(A,B,D) testOk(fabs((A)-(B))<(D), #A " (%f) ~= " #B " (%f)", A, B) + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static +void checkArrD(const char *pv, long elen, double a, double b, double c, double d) +{ + double buf[4]; + double expect[4]; + long nReq = NELEMENTS(buf), i; + unsigned match; + DBADDR addr; + + expect[0] = a; + expect[1] = b; + expect[2] = c; + expect[3] = d; + + if (dbNameToAddr(pv, &addr)) + testAbort("Unknown PV '%s'", pv); + + if (dbGet(&addr, DBR_DOUBLE, buf, NULL, &nReq, NULL)) + testAbort("Failed to get '%s'", pv); + + match = elen==nReq; + for (i=0; i=0.01) + testDiag("[%ld] -> %f != %f", i, expect[i], buf[i]); + } +} + +static +void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32 c, epicsInt32 d) +{ + epicsInt32 buf[4]; + epicsInt32 expect[4]; + long nReq = NELEMENTS(buf), i; + unsigned match; + DBADDR addr; + + expect[0] = a; + expect[1] = b; + expect[2] = c; + expect[3] = d; + + if (dbNameToAddr(pv, &addr)) + testAbort("Unknown PV '%s'", pv); + + if (dbGet(&addr, DBR_LONG, buf, NULL, &nReq, NULL)) + testAbort("Failed to get '%s'", pv); + + match = elen==nReq; + for (i=0; i %d != %d", i, (int)expect[i], (int)buf[i]); + } +} + +static +void testFIFOCirc(void) +{ + aiRecord *vrec; + compressRecord *crec; + double *cbuf; + + testDiag("Test FIFO"); + + testdbPrepare(); + + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4"); + + vrec = (aiRecord*)testdbRecordPtr("val"); + crec = (compressRecord*)testdbRecordPtr("comp"); + + eltc(0); + testIocInitOk(); + eltc(1); + + dbScanLock((dbCommon*)crec); + cbuf = crec->bptr; + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + + testDiag("Push 1.1"); + vrec->val = 1.1; + dbProcess((dbCommon*)crec); + + /* In FIFO mode the valid elements are + * cbuf[(off-nuse-1) % size] through cbuf[(off-1) % size] + */ + testOk1(crec->off==1); + testOk1(crec->inx==0); + testOk1(crec->nuse==1); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 0.0, 0.1); + testDEq(cbuf[2], 0.0, 0.1); + testDEq(cbuf[3], 0.0, 0.1); + checkArrD("comp", 1, 1.1, 0, 0, 0); + + testDiag("Push 2.1"); + vrec->val = 2.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==2); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 0.0, 0.1); + testDEq(cbuf[3], 0.0, 0.1); + checkArrD("comp", 2, 1.1, 2.1, 0, 0); + + testDiag("Push 3.1"); + vrec->val = 3.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==3); + testOk1(crec->inx==0); + testOk1(crec->nuse==3); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 0.0, 0.1); + checkArrD("comp", 3, 1.1, 2.1, 3.1, 0); + + testDiag("Push 4.1"); + vrec->val = 4.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 1.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 4.1, 0.1); + checkArrD("comp", 4, 1.1, 2.1, 3.1, 4.1); + + testDiag("Push 5.1"); + vrec->val = 5.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==1); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 5.1, 0.1); + testDEq(cbuf[1], 2.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 4.1, 0.1); + checkArrD("comp", 4, 2.1, 3.1, 4.1, 5.1); + + testDiag("Push 6.1"); + vrec->val = 6.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 5.1, 0.1); + testDEq(cbuf[1], 6.1, 0.1); + testDEq(cbuf[2], 3.1, 0.1); + testDEq(cbuf[3], 4.1, 0.1); + checkArrD("comp", 4, 3.1, 4.1, 5.1, 6.1); + + dbScanUnlock((dbCommon*)crec); + + testDiag("Reset"); + testdbPutFieldOk("comp.RES", DBF_LONG, 0); + + dbScanLock((dbCommon*)crec); + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + checkArrD("comp", 0, 0, 0, 0, 0); + dbScanUnlock((dbCommon*)crec); + + testIocShutdownOk(); + + testdbCleanup(); +} + +static +void testLIFOCirc(void) +{ + aiRecord *vrec; + compressRecord *crec; + double *cbuf; + + testDiag("Test LIFO"); + + testdbPrepare(); + + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("compressTest.db", NULL, + "ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4"); + + vrec = (aiRecord*)testdbRecordPtr("val"); + crec = (compressRecord*)testdbRecordPtr("comp"); + + eltc(0); + testIocInitOk(); + eltc(1); + + dbScanLock((dbCommon*)crec); + cbuf = crec->bptr; + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + + testDiag("Push 1.1"); + vrec->val = 1.1; + dbProcess((dbCommon*)crec); + + testDiag("off %u", crec->off); + testOk1(crec->off==3); + testOk1(crec->inx==0); + testOk1(crec->nuse==1); + testDEq(cbuf[0], 0.0, 0.1); + testDEq(cbuf[1], 0.0, 0.1); + testDEq(cbuf[2], 0.0, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 1, 1.1, 0, 0, 0); + + testDiag("Push 2.1"); + vrec->val = 2.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==2); + testDEq(cbuf[0], 0.0, 0.1); + testDEq(cbuf[1], 0.0, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 2, 2.1, 1.1, 0, 0); + checkArrI("comp", 2, 2, 1, 0, 0); + + testDiag("Push 3.1"); + vrec->val = 3.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==1); + testOk1(crec->inx==0); + testOk1(crec->nuse==3); + testDEq(cbuf[0], 0.0, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 3, 3.1, 2.1, 1.1, 0); + checkArrI("comp", 3, 3, 2, 1, 0); + + testDiag("Push 4.1"); + vrec->val = 4.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 4.1, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 1.1, 0.1); + checkArrD("comp", 4, 4.1, 3.1, 2.1, 1.1); + checkArrI("comp", 4, 4, 3, 2, 1); + + testDiag("Push 5.1"); + vrec->val = 5.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==3); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 4.1, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 2.1, 0.1); + testDEq(cbuf[3], 5.1, 0.1); + checkArrD("comp", 4, 5.1, 4.1, 3.1, 2.1); + checkArrI("comp", 4, 5, 4, 3, 2); + + testDiag("Push 6.1"); + vrec->val = 6.1; + dbProcess((dbCommon*)crec); + + testOk1(crec->off==2); + testOk1(crec->inx==0); + testOk1(crec->nuse==4); + testDEq(cbuf[0], 4.1, 0.1); + testDEq(cbuf[1], 3.1, 0.1); + testDEq(cbuf[2], 6.1, 0.1); + testDEq(cbuf[3], 5.1, 0.1); + checkArrD("comp", 4, 6.1, 5.1, 4.1, 3.1); + + dbScanUnlock((dbCommon*)crec); + + testDiag("Reset"); + testdbPutFieldOk("comp.RES", DBF_LONG, 0); + + dbScanLock((dbCommon*)crec); + testOk1(crec->off==0); + testOk1(crec->inx==0); + testOk1(crec->nuse==0); + checkArrD("comp", 0, 0, 0, 0, 0); + dbScanUnlock((dbCommon*)crec); + + testIocShutdownOk(); + + testdbCleanup(); +} + +MAIN(compressTest) +{ + testPlan(116); + testFIFOCirc(); + testLIFOCirc(); + return testDone(); +} diff --git a/src/std/rec/test/compressTest.db b/src/std/rec/test/compressTest.db new file mode 100644 index 000000000..59fc620ba --- /dev/null +++ b/src/std/rec/test/compressTest.db @@ -0,0 +1,7 @@ +record(ai, "val") {} +record(compress, "comp") { + field(INP, "val NPP") + field(ALG, "$(ALG)") + field(BALG,"$(BALG)") + field(NSAM,"$(NSAM)") +} diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c index 3476d2138..5612917cc 100644 --- a/src/std/rec/test/epicsRunRecordTests.c +++ b/src/std/rec/test/epicsRunRecordTests.c @@ -13,6 +13,7 @@ #include "epicsExit.h" int analogMonitorTest(void); +int compressTest(void); int arrayOpTest(void); int asTest(void); int linkRetargetLinkTest(void); @@ -23,6 +24,8 @@ void epicsRunRecordTests(void) runTest(analogMonitorTest); + runTest(compressTest); + runTest(arrayOpTest); runTest(asTest);