Merged compress branch
This commit is contained in:
@@ -20,6 +20,12 @@
|
||||
|
||||
-->
|
||||
|
||||
<h3>compressRecord buffering order</h3>
|
||||
|
||||
<p>The compressRecord has a new field <tt>BALG</tt> 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.</p>
|
||||
|
||||
<h3>Valgrind Instrumentation</h3>
|
||||
|
||||
<p>Valgrind is a software debugging suite provided by many Linux distributions.
|
||||
|
||||
+35
-23
@@ -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, "",
|
||||
|
||||
+210
-177
@@ -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_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;
|
||||
/* 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; i<nuse; i++) *psum++ = 0;
|
||||
}else{
|
||||
for (i = 0; i < nnow; i++)
|
||||
*psum++ += *psource++;
|
||||
}
|
||||
/* add in the new waveform */
|
||||
if (inx == 0) {
|
||||
for (i = 0; i < nnow; i++)
|
||||
*psum++ = *psource++;
|
||||
for (i = nnow; i < nuse; i++)
|
||||
*psum++ = 0;
|
||||
} else {
|
||||
for (i = 0; i < nnow; i++)
|
||||
*psum++ += *psource++;
|
||||
}
|
||||
|
||||
/* 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/((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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<nReq && i<elen; i++) {
|
||||
match &= fabs(buf[i]-expect[i])<0.01;
|
||||
}
|
||||
testOk(match, "dbGet(\"%s\") matches", pv);
|
||||
if (elen!=nReq)
|
||||
testDiag("lengths don't match %ld != %ld", elen, nReq);
|
||||
for (i=0; i<nReq && i<elen; i++) {
|
||||
if (fabs(buf[i]-expect[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<nReq && i<elen; i++) {
|
||||
match &= buf[i]==expect[i];
|
||||
}
|
||||
testOk(match, "dbGet(\"%s\") matches", pv);
|
||||
if (elen!=nReq)
|
||||
testDiag("lengths don't match %ld != %ld", elen, nReq);
|
||||
for (i=0; i<nReq && i<elen; i++) {
|
||||
if(buf[i]!=expect[i])
|
||||
testDiag("[%ld] -> %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();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
record(ai, "val") {}
|
||||
record(compress, "comp") {
|
||||
field(INP, "val NPP")
|
||||
field(ALG, "$(ALG)")
|
||||
field(BALG,"$(BALG)")
|
||||
field(NSAM,"$(NSAM)")
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user