This is Unofficial EPICS BASE Doxygen Site
compressRecord.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 
10 /*
11  * Original Author: Bob Dalesio
12  * Date: 7-14-89
13  */
14 
15 #include <stddef.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <math.h>
21 
22 #include "dbDefs.h"
23 #include "epicsPrint.h"
24 #include "alarm.h"
25 #include "dbStaticLib.h"
26 #include "dbAccess.h"
27 #include "dbEvent.h"
28 #include "dbFldTypes.h"
29 #include "errMdef.h"
30 #include "special.h"
31 #include "recSup.h"
32 #include "recGbl.h"
33 
34 #define GEN_SIZE_OFFSET
35 #include "compressRecord.h"
36 #undef GEN_SIZE_OFFSET
37 #include "epicsExport.h"
38 
39 #define indexof(field) compressRecord##field
40 
41 /* Create RSET - Record Support Entry Table*/
42 #define report NULL
43 #define initialize NULL
44 static long init_record(struct dbCommon *, int);
45 static long process(struct dbCommon *);
46 static long special(DBADDR *, int);
47 #define get_value NULL
48 static long cvt_dbaddr(DBADDR *);
49 static long get_array_info(DBADDR *, long *, long *);
50 static long put_array_info(DBADDR *, long);
51 static long get_units(DBADDR *, char *);
52 static long get_precision(const DBADDR *, long *);
53 #define get_enum_str NULL
54 #define get_enum_strs NULL
55 #define put_enum_str NULL
56 static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
57 static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
58 #define get_alarm_double NULL
59 
61  RSETNUMBER,
62  report,
63  initialize,
65  process,
66  special,
67  get_value,
68  cvt_dbaddr,
71  get_units,
79 };
80 epicsExportAddress(rset,compressRSET);
81 
82 
83 static void reset(compressRecord *prec)
84 {
85  prec->nuse = 0;
86  prec->off = 0;
87  prec->inx = 0;
88  prec->cvb = 0.0;
89  prec->res = 0;
90  /* allocate memory for the summing buffer for conversions requiring it */
91  if (prec->alg == compressALG_Average && prec->sptr == NULL) {
92  prec->sptr = calloc(prec->nsam, sizeof(double));
93  }
94 
95  if (prec->bptr && prec->nsam)
96  memset(prec->bptr, 0, prec->nsam * sizeof(double));
97 }
98 
99 static void monitor(compressRecord *prec)
100 {
101  unsigned short alarm_mask = recGblResetAlarms(prec);
102  unsigned short monitor_mask = alarm_mask | DBE_LOG | DBE_VALUE;
103 
104  if (alarm_mask || prec->nuse != prec->ouse) {
105  db_post_events(prec, &prec->nuse, monitor_mask);
106  prec->ouse = prec->nuse;
107  }
108  db_post_events(prec, prec->bptr, monitor_mask);
109 }
110 
111 static void put_value(compressRecord *prec, double *psource, int n)
112 {
113  int fifo = (prec->balg == bufferingALG_FIFO);
114  epicsUInt32 offset = prec->off;
115  epicsUInt32 nuse = prec->nuse;
116  epicsUInt32 nsam = prec->nsam;
117 
118  nuse += n;
119  if (nuse > nsam)
120  nuse = nsam;
121 
122  while (n--) {
123  /* for LIFO, pre-decrement modulo nsam */
124  if (!fifo)
125  offset = (offset + nsam - 1) % nsam;
126 
127  prec->bptr[offset] = *psource++;
128 
129  /* for FIFO, post-increment modulo nsam */
130  if (fifo)
131  offset = (offset + 1) % nsam;
132  }
133 
134  prec->off = offset;
135  prec->nuse = nuse;
136 }
137 
138 
139 /* qsort comparison function (for median calculation) */
140 static int compare(const void *arg1, const void *arg2)
141 {
142  double a = *(double *)arg1;
143  double b = *(double *)arg2;
144 
145  if ( a < b ) return -1;
146  else if ( a == b ) return 0;
147  else return 1;
148 }
149 
150 static int compress_array(compressRecord *prec,
151  double *psource, int no_elements)
152 {
153  epicsInt32 i,j;
154  epicsInt32 n, nnew;
155  epicsInt32 nsam = prec->nsam;
156  double value;
157 
158  /* skip out of limit data */
159  if (prec->ilil < prec->ihil) {
160  while (((*psource < prec->ilil) || (*psource > prec->ihil))
161  && (no_elements > 0)) {
162  no_elements--;
163  psource++;
164  }
165  }
166  if (prec->n <= 0)
167  prec->n = 1;
168  n = prec->n;
169  if (no_elements < n)
170  return 1; /*dont do anything*/
171 
172  /* determine number of samples to take */
173  if (no_elements < nsam * n)
174  nnew = (no_elements / n);
175  else nnew = nsam;
176 
177  /* compress according to specified algorithm */
178  switch (prec->alg){
179  case compressALG_N_to_1_Low_Value:
180  /* compress N to 1 keeping the lowest value */
181  for (i = 0; i < nnew; i++) {
182  value = *psource++;
183  for (j = 1; j < n; j++, psource++) {
184  if (value > *psource)
185  value = *psource;
186  }
187  put_value(prec, &value, 1);
188  }
189  break;
190  case compressALG_N_to_1_High_Value:
191  /* compress N to 1 keeping the highest value */
192  for (i = 0; i < nnew; i++){
193  value = *psource++;
194  for (j = 1; j < n; j++, psource++) {
195  if (value < *psource)
196  value = *psource;
197  }
198  put_value(prec, &value, 1);
199  }
200  break;
201  case compressALG_N_to_1_Average:
202  /* compress N to 1 keeping the average value */
203  for (i = 0; i < nnew; i++) {
204  value = 0;
205  for (j = 0; j < n; j++, psource++)
206  value += *psource;
207  value /= n;
208  put_value(prec, &value, 1);
209  }
210  break;
211 
212  case compressALG_N_to_1_Median:
213  /* compress N to 1 keeping the median value */
214  /* note: sorts source array (OK; it's a work pointer) */
215  for (i = 0; i < nnew; i++, psource += nnew) {
216  qsort(psource, n, sizeof(double), compare);
217  value = psource[n / 2];
218  put_value(prec, &value, 1);
219  }
220  break;
221  }
222  return 0;
223 }
224 
225 static int array_average(compressRecord *prec,
226  double *psource, epicsInt32 no_elements)
227 {
228  epicsInt32 i;
229  epicsInt32 nnow;
230  epicsInt32 nsam=prec->nsam;
231  double *psum;
232  double multiplier;
233  epicsInt32 inx = prec->inx;
234  epicsInt32 nuse, n;
235 
236  nuse = nsam;
237  if (nuse > no_elements)
238  nuse = no_elements;
239  nnow = nuse;
240  if (nnow > no_elements)
241  nnow=no_elements;
242  psum = (double *)prec->sptr;
243 
244  /* add in the new waveform */
245  if (inx == 0) {
246  for (i = 0; i < nnow; i++)
247  *psum++ = *psource++;
248  for (i = nnow; i < nuse; i++)
249  *psum++ = 0;
250  } else {
251  for (i = 0; i < nnow; i++)
252  *psum++ += *psource++;
253  }
254 
255  /* do we need to calculate the result */
256  inx++;
257  if (prec->n <= 0)
258  prec->n = 1;
259  n = prec->n;
260  if (inx < n) {
261  prec->inx = inx;
262  return 1;
263  }
264  if (n > 1) {
265  psum = (double *)prec->sptr;
266  multiplier = 1.0 / n;
267  for (i = 0; i < nuse; i++, psum++)
268  *psum = *psum * multiplier;
269  }
270  put_value(prec, prec->sptr, nuse);
271  prec->inx = 0;
272  return 0;
273 }
274 
275 static int compress_scalar(struct compressRecord *prec,double *psource)
276 {
277  double value = *psource;
278  double *pdest=&prec->cvb;
279  epicsInt32 inx = prec->inx;
280 
281  /* compress according to specified algorithm */
282  switch (prec->alg) {
283  case (compressALG_N_to_1_Low_Value):
284  if ((value < *pdest) || (inx == 0))
285  *pdest = value;
286  break;
287  case (compressALG_N_to_1_High_Value):
288  if ((value > *pdest) || (inx == 0))
289  *pdest = value;
290  break;
291  /* for scalars, Median not implemented => use average */
292  case (compressALG_N_to_1_Average):
293  case (compressALG_N_to_1_Median):
294  if (inx == 0)
295  *pdest = value;
296  else {
297  *pdest += value;
298  if (inx + 1 >= prec->n)
299  *pdest = *pdest / (inx + 1);
300  }
301  break;
302  }
303  inx++;
304  if (inx >= prec->n) {
305  put_value(prec,pdest,1);
306  prec->inx = 0;
307  return 0;
308  } else {
309  prec->inx = inx;
310  return 1;
311  }
312 }
313 
314 /*Beginning of record support routines*/
315 static long init_record(struct dbCommon *pcommon, int pass)
316 {
317  struct compressRecord *prec = (struct compressRecord *)pcommon;
318  if (pass == 0) {
319  if (prec->nsam < 1)
320  prec->nsam = 1;
321  prec->bptr = calloc(prec->nsam, sizeof(double));
322  reset(prec);
323  }
324 
325  return 0;
326 }
327 
328 static long process(struct dbCommon *pcommon)
329 {
330  struct compressRecord *prec = (struct compressRecord *)pcommon;
331  long status = 0;
332  long nelements = 0;
333  int alg = prec->alg;
334 
335  prec->pact = TRUE;
336  if (!dbIsLinkConnected(&prec->inp) ||
337  dbGetNelements(&prec->inp, &nelements) ||
338  nelements <= 0) {
339  recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
340  }
341  else {
342  if (!prec->wptr || nelements != prec->inpn) {
343  if (prec->wptr) {
344  free(prec->wptr);
345  reset(prec);
346  }
347  prec->wptr = dbCalloc(nelements, sizeof(double));
348  prec->inpn = nelements;
349  }
350  status = dbGetLink(&prec->inp, DBF_DOUBLE, prec->wptr, 0, &nelements);
351  if (status || nelements <= 0) {
352  recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
353  status = 0;
354  }
355  else if (alg == compressALG_Average) {
356  status = array_average(prec, prec->wptr, nelements);
357  }
358  else if (alg == compressALG_Circular_Buffer) {
359  put_value(prec, prec->wptr, nelements);
360  status = 0;
361  }
362  else if (nelements > 1) {
363  status = compress_array(prec, prec->wptr, nelements);
364  }
365  else if (nelements == 1){
366  status = compress_scalar(prec, prec->wptr);
367  }
368  else
369  status = 1;
370  }
371 
372  /* check event list */
373  if (status != 1) {
374  prec->udf = FALSE;
375  recGblGetTimeStamp(prec);
376  monitor(prec);
377  /* process the forward scan link record */
378  recGblFwdLink(prec);
379  }
380 
381  prec->pact = FALSE;
382  return 0;
383 }
384 
385 static long special(DBADDR *paddr, int after)
386 {
387  compressRecord *prec = (compressRecord *) paddr->precord;
388  int special_type = paddr->special;
389 
390  if (!after)
391  return 0;
392 
393  if (special_type == SPC_RESET) {
394  reset(prec);
395  return 0;
396  }
397 
398  recGblDbaddrError(S_db_badChoice, paddr, "compress: special");
399  return S_db_badChoice;
400 }
401 
402 static long cvt_dbaddr(DBADDR *paddr)
403 {
404  compressRecord *prec = (compressRecord *) paddr->precord;
405 
406  paddr->pfield = prec->bptr;
407  paddr->no_elements = prec->nsam;
408  paddr->field_type = DBF_DOUBLE;
409  paddr->field_size = sizeof(double);
410  paddr->dbr_field_type = DBF_DOUBLE;
411 
412  if (prec->balg == bufferingALG_LIFO)
413  paddr->special = SPC_NOMOD;
414  return 0;
415 }
416 
417 static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
418 {
419  /* offset indicates the next element which would be written.
420  * In FIFO mode offset-1 is the last valid element
421  * In LIFO mode offset is the first valid element
422  * (*offset) should be set to the index of the first valid element
423  */
424  compressRecord *prec = (compressRecord *) paddr->precord;
425  epicsUInt32 off = prec->off;
426  epicsUInt32 nuse = prec->nuse;
427 
428  if (prec->balg == bufferingALG_FIFO) {
429  epicsUInt32 nsam = prec->nsam;
430 
431  off = (off + nsam - nuse) % nsam;
432  }
433 
434  *no_elements = nuse;
435  *offset = off;
436  return 0;
437 }
438 
439 static long put_array_info(DBADDR *paddr, long nNew)
440 {
441  compressRecord *prec = (compressRecord *) paddr->precord;
442  epicsUInt32 nuse = prec->nuse;
443 
444  if (prec->balg == bufferingALG_FIFO)
445  prec->off = (prec->off + nNew) % prec->nsam;
446  prec->nuse += nNew;
447  if (prec->nuse > prec->nsam)
448  prec->nuse = prec->nsam;
449 
450  if (nuse != prec->nuse)
451  db_post_events(prec, &prec->nuse, DBE_VALUE | DBE_LOG);
452  return 0;
453 }
454 
455 static long get_units(DBADDR *paddr, char *units)
456 {
457  compressRecord *prec = (compressRecord *) paddr->precord;
458 
459  if (paddr->pfldDes->field_type == DBF_DOUBLE ||
460  dbGetFieldIndex(paddr) == indexof(VAL)) {
461  strncpy(units, prec->egu, DB_UNITS_SIZE);
462  }
463  return 0;
464 }
465 
466 static long get_precision(const DBADDR *paddr, long *precision)
467 {
468  compressRecord *prec = (compressRecord *) paddr->precord;
469 
470  *precision = prec->prec;
471  if (dbGetFieldIndex(paddr) != indexof(VAL))
472  recGblGetPrec(paddr,precision);
473  return 0;
474 }
475 
476 static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
477 {
478  compressRecord *prec = (compressRecord *) paddr->precord;
479 
480  switch (dbGetFieldIndex(paddr)) {
481  case indexof(VAL):
482  case indexof(IHIL):
483  case indexof(ILIL):
484  pgd->upper_disp_limit = prec->hopr;
485  pgd->lower_disp_limit = prec->lopr;
486  break;
487  default:
488  recGblGetGraphicDouble(paddr,pgd);
489  }
490  return 0;
491 }
492 
493 static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
494 {
495  compressRecord *prec = (compressRecord *) paddr->precord;
496 
497  switch (dbGetFieldIndex(paddr)) {
498  case indexof(VAL):
499  case indexof(IHIL):
500  case indexof(ILIL):
501  pcd->upper_ctrl_limit = prec->hopr;
502  pcd->lower_ctrl_limit = prec->lopr;
503  break;
504  default:
505  recGblGetControlDouble(paddr, pcd);
506  }
507  return 0;
508 }
#define RSETNUMBER
Definition: recSup.h:92
Definition: link.h:174
#define FALSE
Definition: dbDefs.h:32
#define put_enum_str
pvd::Status status
int i
Definition: scan.c:967
#define SPC_NOMOD
Definition: special.h:26
#define init_record
#define NULL
Definition: catime.c:38
unsigned int epicsUInt32
Definition: epicsTypes.h:43
Miscellaneous macro definitions.
rset compressRSET
#define get_control_double
Definition: biRecord.c:58
#define SPC_RESET
Definition: special.h:35
#define DBE_VALUE
Definition: caeventmask.h:38
#define get_array_info
Definition: aiRecord.c:56
#define initialize
#define DBE_LOG
Definition: caeventmask.h:40
#define get_units
Definition: biRecord.c:52
#define get_enum_str
#define indexof(field)
#define LINK_ALARM
Definition: alarm.h:105
#define get_precision
Definition: biRecord.c:53
#define put_array_info
Definition: aiRecord.c:57
#define TRUE
Definition: dbDefs.h:27
#define get_enum_strs
#define dbCalloc(nobj, size)
Definition: dbStaticLib.h:228
#define get_alarm_double
#define report
if(yy_init)
Definition: scan.c:972
Definition: recSup.h:67
epicsExportAddress(rset, compressRSET)
int prec
Definition: reader.c:29
#define INVALID_ALARM
Definition: alarm.h:53
#define get_graphic_double
Definition: biRecord.c:57
#define get_value
#define cvt_dbaddr
Definition: aiRecord.c:55
#define special
Definition: dfanoutRecord.c:50
int epicsInt32
Definition: epicsTypes.h:42
Exporting IOC objects.