Skip to content

Commit

Permalink
Fix issue with compress record
Browse files Browse the repository at this point in the history
The handling of N-to-M array compression was broken with the addition
of the partial buffer option, which broke the bounds check that was
being used.

Note that this also makes the partial buffer option more consistent;
if, for example, you have
```
record(compress, foo) {
  field(ALG, Average)
  field(INP, {const: [1,2,3]})
  field(NSAM, 2)
  field(N, 2)
  field(PBUF, YES)
}
```
then this will now behave as expected on both of the samples.
  • Loading branch information
simon-ess committed Mar 14, 2024
1 parent 5dfc6ca commit b2aacfd
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 42 deletions.
78 changes: 37 additions & 41 deletions modules/database/src/std/rec/compressRecord.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,15 @@ static int compare(const void *arg1, const void *arg2)
else return 1;
}

#define min(a, b) ((a) < (b) ? (a) : (b))

static int compress_array(compressRecord *prec,
double *psource, int no_elements)
{
epicsInt32 i,j;
epicsInt32 j;
epicsInt32 n, nnew;
epicsInt32 nsam = prec->nsam;
epicsUInt32 samples_written = 0;
double value;

/* skip out of limit data */
Expand All @@ -167,61 +170,54 @@ static int compress_array(compressRecord *prec,
}
if (prec->n <= 0)
prec->n = 1;
if (no_elements < prec->n && prec->pbuf != menuYesNoYES)
return 1; /*dont do anything*/
n = no_elements;
n = prec->n;

/* determine number of samples to take */
if (no_elements < nsam * n)
nnew = (no_elements / n);
else nnew = nsam;
nnew = min(no_elements, nsam * n);

/* 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++) {
while (nnew > 0)
{
if (nnew < n && prec->pbuf != menuYesNoYES)
break;

n = min(n, nnew);
switch (prec->alg)
{
case compressALG_N_to_1_Low_Value:
value = *psource++;
for (j = 1; j < n; j++, 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++){
break;
case compressALG_N_to_1_High_Value:
value = *psource++;
for (j = 1; j < n; j++, 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++)
break;
case compressALG_N_to_1_Average:
value = *psource++;
for (j = 1; 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) {
}
value = value / n;
break;
case compressALG_N_to_1_Median:
/* note: sorts source array (OK; it's a work pointer) */
qsort(psource, n, sizeof(double), compare);
value = psource[n / 2];
put_value(prec, &value, 1);
psource += n;
break;
}
break;
nnew -= n;
put_value(prec, &value, 1);
samples_written++;
}
return 0;
return (samples_written > 0);
}

static int array_average(compressRecord *prec,
Expand Down
66 changes: 65 additions & 1 deletion modules/database/test/std/rec/compressTest.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,38 @@ testNto1Average(void) {
testdbCleanup();
}

void testNto2Average(void) {
DBADDR wfaddr, caddr;

testDiag("Test N to 1 Average, NSAM=2, N=2");

testdbPrepare();

testdbReadDatabase("recTestIoc.dbd", NULL, NULL);

recTestIoc_registerRecordDeviceDriver(pdbbase);

testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=2,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);

checkArrD("comp", 2, 1.5, 3.5, 0, 0);
dbScanUnlock(caddr.precord);

testIocShutdownOk();
testdbCleanup();
}

void
testNto1AveragePartial(void) {
double buf = 0.0;
Expand Down Expand Up @@ -517,6 +549,36 @@ testNto1AveragePartial(void) {
testdbCleanup();
}

void
testNtoMPartial(void) {
DBADDR wfaddr, caddr;

testDiag("Test Average, N to M, Partial");

testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
recTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("compressTest.db", NULL, "INP=wf,ALG=N to 1 Average,BALG=FIFO Buffer,NSAM=2,N=3,PBUF=YES");

eltc(0);
testIocInitOk();
eltc(1);

fetchRecordOrDie("wf", wfaddr);
fetchRecordOrDie("comp", caddr);

writeToWaveform(&wfaddr, 4, 1., 2., 3., 4.);

dbScanLock(caddr.precord);
dbProcess(caddr.precord);

checkArrD("comp", 2, 2.0, 4.0, 0, 0);
dbScanUnlock(caddr.precord);

testIocShutdownOk();
testdbCleanup();
}

void
testNto1LowValue(void) {
double buf = 0.0;
Expand Down Expand Up @@ -634,12 +696,14 @@ testAIAveragePartial(void) {

MAIN(compressTest)
{
testPlan(132);
testPlan(134);
testFIFOCirc();
testLIFOCirc();
testArrayAverage();
testNto1Average();
testNto2Average();
testNto1AveragePartial();
testNtoMPartial();
testAIAveragePartial();
testNto1LowValue();
return testDone();
Expand Down

0 comments on commit b2aacfd

Please sign in to comment.