Merge lp:~epics-core/epics-base/compress into lp:~epics-core/epics-base/3.16

Proposed by mdavidsaver
Status: Merged
Merged at revision: 12715
Proposed branch: lp:~epics-core/epics-base/compress
Merge into: lp:~epics-core/epics-base/3.16
Diff against target: 680 lines (+459/-37)
9 files modified
documentation/RELEASE_NOTES.html (+6/-0)
src/ioc/db/dbAccess.c (+5/-5)
src/std/filters/arr.c (+2/-2)
src/std/rec/compressRecord.c (+43/-24)
src/std/rec/compressRecord.dbd.pod (+35/-6)
src/std/rec/test/Makefile (+7/-0)
src/std/rec/test/compressTest.c (+351/-0)
src/std/rec/test/compressTest.db (+7/-0)
src/std/rec/test/epicsRunRecordTests.c (+3/-0)
To merge this branch: bzr merge lp:~epics-core/epics-base/compress
Reviewer Review Type Date Requested Status
Andrew Johnson Approve
mdavidsaver Approve
Ralph Lange Pending
Review via email: mp+282534@code.launchpad.net

This proposal supersedes a proposal from 2014-08-28.

Description of the change

From Kukhee Kim

- Implement LIFO behavior in compress record
- Prepare new field BALG (Buffering Algorithm) to allow to choose FIFO and LIFO

From Michael Davidsaver

I've rebased against the current 3.16 branch and added unit testing of (only) circular buffer mode with FIFO and LIFO ordering.

In the process of writing the test I found that before the buffer is filled (nuse<nsam) the first nsam-nuse elements are visible as zeros. I didn't like this and changed the handling of OFF in compressRecord and in of no_elements in dbGet to avoid it.

So the changes which need the most attention are to dbGet() and the arr filter. These changes switch from wrapping around based on the number of valid elements set by rset::get_array_info to wrapping using the max field size. The actual number of elements copied out is still bounded by the # of valid elements.

This should only be a change where offset>0 and the buffer is not full. Given that this didn't work quite right before (could wrap one element early) I'm guessing that no existing code intentionally depends on this.

To post a comment you must log in.
Revision history for this message
Kim, Kukhee (khkim) wrote : Posted in a previous version of this proposal

Revision 12520 - 12523

Add epics thread id validation routine
Modify epicsThreadGetName to check up if the thread id is valid to avoid crash
Modify epicsThreadResume() command to avoid crash when user inputs an invalid thread id

Revision history for this message
mdavidsaver (mdavidsaver) wrote : Posted in a previous version of this proposal

What should be done about switching between FIFO and LIFO at runtime? At present LIFO -> FIFO clears the buffer. FIFO -> LIFO can't clear the buffer, but could zero it. Alternately, make the BALG field read only somehow.

Otherwise this works as I expect.

To save Andrew the trouble, please add to RELEASE_NOTES.html

review: Needs Fixing
Revision history for this message
mdavidsaver (mdavidsaver) wrote : Posted in a previous version of this proposal

Zeroing the buffer on reset seems sufficient.

As for epicsThreadValidateId(), it seems reasonable for the current implementation of epicsThreadResume(). IMO epicsThreadResume() is broken by design and should be removed, or epicsThread needs to be reimplemented around it. But this is the subject of another development.

Also, I'm commenting now because I've just noticed that there was activity on this branch. Since launchpad is sparing with email notifications it's a good idea to send a note to core-talk when pushing changes to a branch after a long period of inactivity.

review: Approve
Revision history for this message
Ralph Lange (ralph-lange) wrote : Posted in a previous version of this proposal

Should have some tests added, shouldn't it?

review: Needs Fixing
Revision history for this message
Andrew Johnson (anj) wrote : Posted in a previous version of this proposal

Results of AJ+MD+RL discussion about this today:
* This should be a 3.16 change (3.16.0.2 maybe), not 3.15.
* Needs Release Notes entry and POD documentation for the new field (AJ?).
* Remove the epicsThreadValidateId() changes, not ready.
* Automated tests would be nice given the complexity of the record, but we will accept the change without them.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Rebased only compressRecord changes against 3.16. Added unittest and a change to dbGet().

@AJ/RL I know I was the one pushing to wave the unittest requirement for this change, but on reflection I've changed my mind. Testing recordtype logic is now straightforward. Not doing so is just laziness. I'll save the waiver argument for rsrv interface binding, where unittesting isn't straightforward.

As for the change to dbGet()... I have to admit this was a case of scope creep.

review: Approve
lp:~epics-core/epics-base/compress updated
12708. By mdavidsaver

update release notes

Revision history for this message
Andrew Johnson (anj) wrote :

Merging after some minor source reformatting.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'documentation/RELEASE_NOTES.html'
2--- documentation/RELEASE_NOTES.html 2015-09-07 19:23:04 +0000
3+++ documentation/RELEASE_NOTES.html 2016-01-14 02:46:51 +0000
4@@ -20,6 +20,12 @@
5
6 -->
7
8+<h3>compressRecord buffering order</h3>
9+
10+<p>The compressRecord has a new field <em>BALG</em> which can select between
11+FIFO (append) and LIFO (prepend) ordering for insertion of new elements.
12+FIFO ordering is the default.</p>
13+
14 <h3>Database Multi-locking</h3>
15
16 <p>dbLock.c is re-written with an expanded API, and the removal of global mutex locks.</p>
17
18=== modified file 'src/ioc/db/dbAccess.c'
19--- src/ioc/db/dbAccess.c 2015-09-07 04:15:21 +0000
20+++ src/ioc/db/dbAccess.c 2016-01-14 02:46:51 +0000
21@@ -816,7 +816,7 @@
22 void *pfieldsave;
23 db_field_log *pfl = (db_field_log *)pflin;
24 short field_type;
25- long no_elements;
26+ long no_elements, max_elements;
27 long offset;
28 struct rset *prset;
29 long status = 0;
30@@ -828,10 +828,10 @@
31
32 if (!pfl || pfl->type == dbfl_type_rec) {
33 field_type = paddr->field_type;
34- no_elements = paddr->no_elements;
35+ max_elements = no_elements = paddr->no_elements;
36 } else {
37 field_type = pfl->field_type;
38- no_elements = pfl->no_elements;
39+ max_elements = no_elements = pfl->no_elements;
40 }
41
42 if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK)
43@@ -906,7 +906,7 @@
44 if (n <= 0) {
45 ;/*do nothing*/
46 } else if (!pfl || pfl->type == dbfl_type_rec) {
47- status = convert(paddr, pbuf, n, no_elements, offset);
48+ status = convert(paddr, pbuf, n, max_elements, offset);
49 } else {
50 DBADDR localAddr = *paddr; /* Structure copy */
51
52@@ -917,7 +917,7 @@
53 localAddr.pfield = (char *) &pfl->u.v.field;
54 else
55 localAddr.pfield = (char *) pfl->u.r.field;
56- status = convert(&localAddr, pbuf, n, no_elements, offset);
57+ status = convert(&localAddr, pbuf, n, max_elements, offset);
58 }
59 }
60 done:
61
62=== modified file 'src/std/filters/arr.c'
63--- src/std/filters/arr.c 2014-09-30 14:19:13 +0000
64+++ src/std/filters/arr.c 2016-01-14 02:46:51 +0000
65@@ -95,7 +95,7 @@
66 long end = my->end;
67 long nTarget = 0;
68 long offset = 0;
69- long nSource = chan->addr.no_elements;
70+ long nSource = chan->addr.no_elements, maxSource = nSource;
71
72 /* Only array data */
73 if (pfl->type == dbfl_type_val) {
74@@ -126,7 +126,7 @@
75 pfl->u.r.dtor = freeArray;
76 pfl->u.r.pvt = my->arrayFreeList;
77 offset = (offset + start) % chan->addr.no_elements;
78- dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr);
79+ dbExtractArrayFromRec(&chan->addr, pdst, nTarget, maxSource, offset, my->incr);
80 pfl->u.r.field = pdst;
81 }
82 }
83
84=== modified file 'src/std/rec/compressRecord.c'
85--- src/std/rec/compressRecord.c 2013-12-17 18:54:04 +0000
86+++ src/std/rec/compressRecord.c 2016-01-14 02:46:51 +0000
87@@ -92,6 +92,8 @@
88 if (prec->alg == compressALG_Average && prec->sptr == 0){
89 prec->sptr = calloc(prec->nsam, sizeof(double));
90 }
91+
92+ if(prec->bptr && prec->nsam) memset(prec->bptr, 0, prec->nsam * sizeof(double));
93 }
94
95 static void monitor(compressRecord *prec)
96@@ -108,29 +110,32 @@
97
98 static void put_value(compressRecord *prec, double *psource, int n)
99 {
100- epicsInt32 offset = prec->off;
101- epicsInt32 nuse = prec->nuse;
102- epicsInt32 nsam = prec->nsam;
103- double *pdest = prec->bptr + offset;
104- int i;
105+ int fifo = prec->balg==bufferingALG_FIFO;
106+ epicsUInt32 offset = prec->off;
107+ epicsUInt32 nuse = prec->nuse;
108+ epicsUInt32 nsam = prec->nsam;
109
110- for (i = 0; i < n; i++) {
111- *pdest = *psource++;
112- if (++offset >= nsam) {
113- pdest = prec->bptr;
114- offset = 0;
115- }
116- else
117- pdest++;
118- }
119 nuse += n;
120- if (nuse > nsam)
121+ if(nuse>nsam)
122 nuse = nsam;
123+
124+ while(n--) {
125+ /* for LIFO, decrement before */
126+ if(!fifo)
127+ offset = (offset-1)%nsam;
128+
129+ prec->bptr[offset] = *psource++;
130+
131+ /* for FIFO, increment after */
132+ if(fifo)
133+ offset = (offset+1)%nsam;
134+ }
135+
136 prec->off = offset;
137 prec->nuse = nuse;
138- return;
139 }
140-
141
142+
143+
144 /* qsort comparison function (for median calculation) */
145 static int compare(const void *arg1, const void *arg2)
146 {
147@@ -305,6 +310,7 @@
148 prec->bptr = calloc(prec->nsam, sizeof(double));
149 reset(prec);
150 }
151+
152 return 0;
153 }
154
155@@ -388,18 +394,30 @@
156 paddr->field_type = DBF_DOUBLE;
157 paddr->field_size = sizeof(double);
158 paddr->dbr_field_type = DBF_DOUBLE;
159+
160+ if(prec->balg == bufferingALG_LIFO) paddr->special = SPC_NOMOD;
161 return 0;
162 }
163
164 static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
165 {
166+ /* offset indicates the next element which would be written.
167+ * In FIFO mode offset-1 is the last valid element
168+ * In LIFO mode offset is the first valid element
169+ * (*offset) should be set to the index of the first valid element
170+ */
171 compressRecord *prec = (compressRecord *) paddr->precord;
172-
173- *no_elements = prec->nuse;
174- if (prec->nuse == prec->nsam)
175- *offset = prec->off;
176- else
177- *offset = 0;
178+ epicsUInt32 off = prec->off;
179+ epicsUInt32 nuse = prec->nuse;
180+ epicsUInt32 nsam = prec->nsam;
181+
182+ *no_elements = nuse;
183+ if(prec->balg == bufferingALG_FIFO) {
184+ *offset = (off-nuse)%nsam;
185+ } else {
186+ *offset = off;
187+ }
188+
189 return 0;
190 }
191
192@@ -407,7 +425,8 @@
193 {
194 compressRecord *prec = (compressRecord *) paddr->precord;
195
196- prec->off = (prec->off + nNew) % prec->nsam;
197+ if(prec->balg == bufferingALG_FIFO)
198+ prec->off = (prec->off + nNew) % prec->nsam;
199 prec->nuse += nNew;
200 if (prec->nuse > prec->nsam)
201 prec->nuse = prec->nsam;
202
203=== modified file 'src/std/rec/compressRecord.dbd.pod'
204--- src/std/rec/compressRecord.dbd.pod 2013-09-30 22:48:06 +0000
205+++ src/std/rec/compressRecord.dbd.pod 2016-01-14 02:46:51 +0000
206@@ -9,19 +9,37 @@
207
208 =title Compress Record (compress)
209
210-...
211+The data compression record is used to collect and compress data from arrays.
212+When the INP field references a data array field,
213+it immediately compresses the entire array into an element of an array using one of several algorithms,
214+overwriting the previous element. If the INP field obtains its value from a scalar-value field,
215+the compression record will collect a new sample each time the record is processed
216+and add it to the compressed data array as a circular buffer.
217+
218+The INP link can also specify a constant;
219+however, if this is the case, the compression algorithms are ignored,
220+and the record support routines merely return after checking the FLNK field.
221
222 =head2 Record-specific Menus
223
224 =head3 Menu compressALG
225
226+=menu compressALG
227+
228 The ALG field which uses this menu controls the compression algorithm used.
229
230-...
231-
232-=menu compressALG
233-
234-...
235+There are six possible algorithms which can be specified as follows:
236+
237+* Circular Buffer
238+* Average
239+* N to 1 Low Value
240+* N to 1 High Value
241+* N to 1 Average
242+* N to 1 Median
243+
244+=menu bufferingALG
245+
246+Selects FIFO (First in first out) or LIFO (Last in first out) insertion order.
247
248 =head2 Parameter Fields
249
250@@ -41,6 +59,10 @@
251 choice(compressALG_Circular_Buffer,"Circular Buffer")
252 choice(compressALG_N_to_1_Median,"N to 1 Median")
253 }
254+menu(bufferingALG) {
255+ choice(bufferingALG_FIFO, "FIFO Buffer")
256+ choice(bufferingALG_LIFO, "LIFO Buffer")
257+}
258 recordtype(compress) {
259
260 =fields VAL
261@@ -76,6 +98,13 @@
262 interest(1)
263 menu(compressALG)
264 }
265+ field(BALG,DBF_MENU) {
266+ prompt("Buffering Algorithm")
267+ promptgroup(GUI_ALARMS)
268+ special(SPC_RESET)
269+ interest(1)
270+ menu(bufferingALG)
271+ }
272 field(NSAM,DBF_ULONG) {
273 prompt("Number of Values")
274 promptgroup(GUI_COMPRESS)
275
276=== modified file 'src/std/rec/test/Makefile'
277--- src/std/rec/test/Makefile 2016-01-07 19:22:43 +0000
278+++ src/std/rec/test/Makefile 2016-01-14 02:46:51 +0000
279@@ -38,6 +38,13 @@
280 TESTFILES += ../linkRetargetLink.db
281 TESTS += linkRetargetLinkTest
282
283+TESTPROD_HOST += compressTest
284+compressTest_SRCS += compressTest.c
285+compressTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
286+testHarness_SRCS += compressTest.c
287+TESTFILES += ../compressTest.db
288+TESTS += compressTest
289+
290 TARGETS += $(COMMON_DIR)/asTestIoc.dbd
291 asTestIoc_DBD += base.dbd
292 asTestIoc_DBD += asTest.dbd
293
294=== added file 'src/std/rec/test/compressTest.c'
295--- src/std/rec/test/compressTest.c 1970-01-01 00:00:00 +0000
296+++ src/std/rec/test/compressTest.c 2016-01-14 02:46:51 +0000
297@@ -0,0 +1,351 @@
298+/*************************************************************************\
299+* Copyright (c) 2016 Michael Davidsaver
300+* EPICS BASE is distributed subject to a Software License Agreement found
301+* in file LICENSE that is included with this distribution.
302+\*************************************************************************/
303+
304+#include "dbUnitTest.h"
305+#include "testMain.h"
306+#include "dbLock.h"
307+#include "errlog.h"
308+#include "dbAccess.h"
309+#include "epicsMath.h"
310+
311+#include "aiRecord.h"
312+#include "compressRecord.h"
313+
314+#define testDEq(A,B,D) testOk(fabs((A)-(B))<(D), #A " (%f) ~= " #B " (%f)", A, B)
315+
316+void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
317+
318+static
319+void checkArrD(const char *pv, long elen, double a, double b, double c, double d)
320+{
321+ double buf[4];
322+ double expect[4];
323+ long nReq = NELEMENTS(buf), i;
324+ unsigned match;
325+ DBADDR addr;
326+
327+ expect[0] = a;
328+ expect[1] = b;
329+ expect[2] = c;
330+ expect[3] = d;
331+
332+ if(dbNameToAddr(pv, &addr))
333+ testAbort("Unknown PV '%s'", pv);
334+
335+ if(dbGet(&addr, DBR_DOUBLE, buf, NULL, &nReq, NULL))
336+ testAbort("Failed to get '%s'", pv);
337+
338+ match = elen==nReq;
339+ for(i=0; i<nReq && i<elen; i++) {
340+ match &= fabs(buf[i]-expect[i])<0.01;
341+ }
342+ testOk(match, "dbGet(\"%s\") matches", pv);
343+ if(elen!=nReq)
344+ testDiag("lengths don't match %ld != %ld", elen, nReq);
345+ for(i=0; i<nReq && i<elen; i++) {
346+ if(fabs(buf[i]-expect[i])>=0.01)
347+ testDiag("[%ld] -> %f != %f", i, expect[i], buf[i]);
348+ }
349+}
350+
351+static
352+void checkArrI(const char *pv, long elen, epicsInt32 a, epicsInt32 b, epicsInt32 c, epicsInt32 d)
353+{
354+ epicsInt32 buf[4];
355+ epicsInt32 expect[4];
356+ long nReq = NELEMENTS(buf), i;
357+ unsigned match;
358+ DBADDR addr;
359+
360+ expect[0] = a;
361+ expect[1] = b;
362+ expect[2] = c;
363+ expect[3] = d;
364+
365+ if(dbNameToAddr(pv, &addr))
366+ testAbort("Unknown PV '%s'", pv);
367+
368+ if(dbGet(&addr, DBR_LONG, buf, NULL, &nReq, NULL))
369+ testAbort("Failed to get '%s'", pv);
370+
371+ match = elen==nReq;
372+ for(i=0; i<nReq && i<elen; i++) {
373+ match &= buf[i]==expect[i];
374+ }
375+ testOk(match, "dbGet(\"%s\") matches", pv);
376+ if(elen!=nReq)
377+ testDiag("lengths don't match %ld != %ld", elen, nReq);
378+ for(i=0; i<nReq && i<elen; i++) {
379+ if(buf[i]!=expect[i])
380+ testDiag("[%ld] -> %d != %d", i, (int)expect[i], (int)buf[i]);
381+ }
382+}
383+
384+static
385+void testFIFOCirc(void)
386+{
387+ aiRecord *vrec;
388+ compressRecord *crec;
389+ double *cbuf;
390+
391+ testDiag("Test FIFO");
392+
393+ testdbPrepare();
394+
395+ testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
396+
397+ recTestIoc_registerRecordDeviceDriver(pdbbase);
398+
399+ testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=FIFO Buffer,NSAM=4");
400+
401+ vrec = (aiRecord*)testdbRecordPtr("val");
402+ crec = (compressRecord*)testdbRecordPtr("comp");
403+
404+ eltc(0);
405+ testIocInitOk();
406+ eltc(1);
407+
408+ dbScanLock((dbCommon*)crec);
409+ cbuf = crec->bptr;
410+
411+ testOk1(crec->off==0);
412+ testOk1(crec->inx==0);
413+ testOk1(crec->nuse==0);
414+
415+ testDiag("Push 1.1");
416+ vrec->val = 1.1;
417+ dbProcess((dbCommon*)crec);
418+
419+ /* In FIFO mode the valid elements a cbuf[(off-nuse-1)%size] through cbuf[(off-1)%size] */
420+ testOk1(crec->off==1);
421+ testOk1(crec->inx==0);
422+ testOk1(crec->nuse==1);
423+ testDEq(cbuf[0], 1.1, 0.1);
424+ testDEq(cbuf[1], 0.0, 0.1);
425+ testDEq(cbuf[2], 0.0, 0.1);
426+ testDEq(cbuf[3], 0.0, 0.1);
427+ checkArrD("comp", 1, 1.1, 0, 0, 0);
428+
429+ testDiag("Push 2.1");
430+ vrec->val = 2.1;
431+ dbProcess((dbCommon*)crec);
432+
433+ testOk1(crec->off==2);
434+ testOk1(crec->inx==0);
435+ testOk1(crec->nuse==2);
436+ testDEq(cbuf[0], 1.1, 0.1);
437+ testDEq(cbuf[1], 2.1, 0.1);
438+ testDEq(cbuf[2], 0.0, 0.1);
439+ testDEq(cbuf[3], 0.0, 0.1);
440+ checkArrD("comp", 2, 1.1, 2.1, 0, 0);
441+
442+ testDiag("Push 3.1");
443+ vrec->val = 3.1;
444+ dbProcess((dbCommon*)crec);
445+
446+ testOk1(crec->off==3);
447+ testOk1(crec->inx==0);
448+ testOk1(crec->nuse==3);
449+ testDEq(cbuf[0], 1.1, 0.1);
450+ testDEq(cbuf[1], 2.1, 0.1);
451+ testDEq(cbuf[2], 3.1, 0.1);
452+ testDEq(cbuf[3], 0.0, 0.1);
453+ checkArrD("comp", 3, 1.1, 2.1, 3.1, 0);
454+
455+ testDiag("Push 4.1");
456+ vrec->val = 4.1;
457+ dbProcess((dbCommon*)crec);
458+
459+ testOk1(crec->off==0);
460+ testOk1(crec->inx==0);
461+ testOk1(crec->nuse==4);
462+ testDEq(cbuf[0], 1.1, 0.1);
463+ testDEq(cbuf[1], 2.1, 0.1);
464+ testDEq(cbuf[2], 3.1, 0.1);
465+ testDEq(cbuf[3], 4.1, 0.1);
466+ checkArrD("comp", 4, 1.1, 2.1, 3.1, 4.1);
467+
468+ testDiag("Push 5.1");
469+ vrec->val = 5.1;
470+ dbProcess((dbCommon*)crec);
471+
472+ testOk1(crec->off==1);
473+ testOk1(crec->inx==0);
474+ testOk1(crec->nuse==4);
475+ testDEq(cbuf[0], 5.1, 0.1);
476+ testDEq(cbuf[1], 2.1, 0.1);
477+ testDEq(cbuf[2], 3.1, 0.1);
478+ testDEq(cbuf[3], 4.1, 0.1);
479+ checkArrD("comp", 4, 2.1, 3.1, 4.1, 5.1);
480+
481+ testDiag("Push 6.1");
482+ vrec->val = 6.1;
483+ dbProcess((dbCommon*)crec);
484+
485+ testOk1(crec->off==2);
486+ testOk1(crec->inx==0);
487+ testOk1(crec->nuse==4);
488+ testDEq(cbuf[0], 5.1, 0.1);
489+ testDEq(cbuf[1], 6.1, 0.1);
490+ testDEq(cbuf[2], 3.1, 0.1);
491+ testDEq(cbuf[3], 4.1, 0.1);
492+ checkArrD("comp", 4, 3.1, 4.1, 5.1, 6.1);
493+
494+ dbScanUnlock((dbCommon*)crec);
495+
496+ testDiag("Reset");
497+ testdbPutFieldOk("comp.RES", DBF_LONG, 0);
498+
499+ dbScanLock((dbCommon*)crec);
500+ testOk1(crec->off==0);
501+ testOk1(crec->inx==0);
502+ testOk1(crec->nuse==0);
503+ checkArrD("comp", 0, 0, 0, 0, 0);
504+ dbScanUnlock((dbCommon*)crec);
505+
506+ testIocShutdownOk();
507+
508+ testdbCleanup();
509+}
510+
511+static
512+void testLIFOCirc(void)
513+{
514+ aiRecord *vrec;
515+ compressRecord *crec;
516+ double *cbuf;
517+
518+ testDiag("Test LIFO");
519+
520+ testdbPrepare();
521+
522+ testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
523+
524+ recTestIoc_registerRecordDeviceDriver(pdbbase);
525+
526+ testdbReadDatabase("compressTest.db", NULL, "ALG=Circular Buffer,BALG=LIFO Buffer,NSAM=4");
527+
528+ vrec = (aiRecord*)testdbRecordPtr("val");
529+ crec = (compressRecord*)testdbRecordPtr("comp");
530+
531+ eltc(0);
532+ testIocInitOk();
533+ eltc(1);
534+
535+ dbScanLock((dbCommon*)crec);
536+ cbuf = crec->bptr;
537+
538+ testOk1(crec->off==0);
539+ testOk1(crec->inx==0);
540+ testOk1(crec->nuse==0);
541+
542+ testDiag("Push 1.1");
543+ vrec->val = 1.1;
544+ dbProcess((dbCommon*)crec);
545+
546+ testDiag("off %u", crec->off);
547+ testOk1(crec->off==3);
548+ testOk1(crec->inx==0);
549+ testOk1(crec->nuse==1);
550+ testDEq(cbuf[0], 0.0, 0.1);
551+ testDEq(cbuf[1], 0.0, 0.1);
552+ testDEq(cbuf[2], 0.0, 0.1);
553+ testDEq(cbuf[3], 1.1, 0.1);
554+ checkArrD("comp", 1, 1.1, 0, 0, 0);
555+
556+ testDiag("Push 2.1");
557+ vrec->val = 2.1;
558+ dbProcess((dbCommon*)crec);
559+
560+ testOk1(crec->off==2);
561+ testOk1(crec->inx==0);
562+ testOk1(crec->nuse==2);
563+ testDEq(cbuf[0], 0.0, 0.1);
564+ testDEq(cbuf[1], 0.0, 0.1);
565+ testDEq(cbuf[2], 2.1, 0.1);
566+ testDEq(cbuf[3], 1.1, 0.1);
567+ checkArrD("comp", 2, 2.1, 1.1, 0, 0);
568+ checkArrI("comp", 2, 2, 1, 0, 0);
569+
570+ testDiag("Push 3.1");
571+ vrec->val = 3.1;
572+ dbProcess((dbCommon*)crec);
573+
574+ testOk1(crec->off==1);
575+ testOk1(crec->inx==0);
576+ testOk1(crec->nuse==3);
577+ testDEq(cbuf[0], 0.0, 0.1);
578+ testDEq(cbuf[1], 3.1, 0.1);
579+ testDEq(cbuf[2], 2.1, 0.1);
580+ testDEq(cbuf[3], 1.1, 0.1);
581+ checkArrD("comp", 3, 3.1, 2.1, 1.1, 0);
582+ checkArrI("comp", 3, 3, 2, 1, 0);
583+
584+ testDiag("Push 4.1");
585+ vrec->val = 4.1;
586+ dbProcess((dbCommon*)crec);
587+
588+ testOk1(crec->off==0);
589+ testOk1(crec->inx==0);
590+ testOk1(crec->nuse==4);
591+ testDEq(cbuf[0], 4.1, 0.1);
592+ testDEq(cbuf[1], 3.1, 0.1);
593+ testDEq(cbuf[2], 2.1, 0.1);
594+ testDEq(cbuf[3], 1.1, 0.1);
595+ checkArrD("comp", 4, 4.1, 3.1, 2.1, 1.1);
596+ checkArrI("comp", 4, 4, 3, 2, 1);
597+
598+ testDiag("Push 5.1");
599+ vrec->val = 5.1;
600+ dbProcess((dbCommon*)crec);
601+
602+ testOk1(crec->off==3);
603+ testOk1(crec->inx==0);
604+ testOk1(crec->nuse==4);
605+ testDEq(cbuf[0], 4.1, 0.1);
606+ testDEq(cbuf[1], 3.1, 0.1);
607+ testDEq(cbuf[2], 2.1, 0.1);
608+ testDEq(cbuf[3], 5.1, 0.1);
609+ checkArrD("comp", 4, 5.1, 4.1, 3.1, 2.1);
610+ checkArrI("comp", 4, 5, 4, 3, 2);
611+
612+ testDiag("Push 6.1");
613+ vrec->val = 6.1;
614+ dbProcess((dbCommon*)crec);
615+
616+ testOk1(crec->off==2);
617+ testOk1(crec->inx==0);
618+ testOk1(crec->nuse==4);
619+ testDEq(cbuf[0], 4.1, 0.1);
620+ testDEq(cbuf[1], 3.1, 0.1);
621+ testDEq(cbuf[2], 6.1, 0.1);
622+ testDEq(cbuf[3], 5.1, 0.1);
623+ checkArrD("comp", 4, 6.1, 5.1, 4.1, 3.1);
624+
625+ dbScanUnlock((dbCommon*)crec);
626+
627+ testDiag("Reset");
628+ testdbPutFieldOk("comp.RES", DBF_LONG, 0);
629+
630+ dbScanLock((dbCommon*)crec);
631+ testOk1(crec->off==0);
632+ testOk1(crec->inx==0);
633+ testOk1(crec->nuse==0);
634+ checkArrD("comp", 0, 0, 0, 0, 0);
635+ dbScanUnlock((dbCommon*)crec);
636+
637+ testIocShutdownOk();
638+
639+ testdbCleanup();
640+}
641+
642+MAIN(compressTest)
643+{
644+ testPlan(116);
645+ testFIFOCirc();
646+ testLIFOCirc();
647+ return testDone();
648+}
649
650=== added file 'src/std/rec/test/compressTest.db'
651--- src/std/rec/test/compressTest.db 1970-01-01 00:00:00 +0000
652+++ src/std/rec/test/compressTest.db 2016-01-14 02:46:51 +0000
653@@ -0,0 +1,7 @@
654+record(ai, "val") {}
655+record(compress, "comp") {
656+ field(INP, "val NPP")
657+ field(ALG, "$(ALG)")
658+ field(BALG,"$(BALG)")
659+ field(NSAM,"$(NSAM)")
660+}
661
662=== modified file 'src/std/rec/test/epicsRunRecordTests.c'
663--- src/std/rec/test/epicsRunRecordTests.c 2015-08-27 16:13:49 +0000
664+++ src/std/rec/test/epicsRunRecordTests.c 2016-01-14 02:46:51 +0000
665@@ -13,6 +13,7 @@
666 #include "epicsExit.h"
667
668 int analogMonitorTest(void);
669+int compressTest(void);
670 int arrayOpTest(void);
671 int asTest(void);
672 int linkRetargetLinkTest(void);
673@@ -23,6 +24,8 @@
674
675 runTest(analogMonitorTest);
676
677+ runTest(compressTest);
678+
679 runTest(arrayOpTest);
680
681 runTest(asTest);

Subscribers

People subscribed via source and target branches