This is Unofficial EPICS BASE Doxygen Site
selRecord.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 /* selRecord.c - Record Support Routines for Select records */
11 /*
12  * Original Author: Bob Dalesio
13  * Date: 6-2-89
14  */
15 
16 #include <stddef.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include "dbDefs.h"
23 #include "epicsPrint.h"
24 #include "epicsMath.h"
25 #include "alarm.h"
26 #include "dbAccess.h"
27 #include "dbEvent.h"
28 #include "dbFldTypes.h"
29 #include "errMdef.h"
30 #include "recSup.h"
31 #include "recGbl.h"
32 
33 #define GEN_SIZE_OFFSET
34 #include "selRecord.h"
35 #undef GEN_SIZE_OFFSET
36 #include "epicsExport.h"
37 
38 /* Create RSET - Record Support Entry Table*/
39 #define report NULL
40 #define initialize NULL
41 static long init_record(struct dbCommon *, int);
42 static long process(struct dbCommon *);
43 #define special NULL
44 #define get_value NULL
45 #define cvt_dbaddr NULL
46 #define get_array_info NULL
47 #define put_array_info NULL
48 static long get_units(DBADDR *, char *);
49 static long get_precision(const DBADDR *, long *);
50 #define get_enum_str NULL
51 #define get_enum_strs NULL
52 #define put_enum_str NULL
53 static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
54 static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
55 static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
56 
58  RSETNUMBER,
59  report,
60  initialize,
62  process,
63  special,
64  get_value,
65  cvt_dbaddr,
68  get_units,
76 };
77 epicsExportAddress(rset,selRSET);
78 
79 #define SEL_MAX 12
80 
81 static void checkAlarms(selRecord *);
82 static void do_sel(selRecord *);
83 static int fetch_values(selRecord *);
84 static void monitor(selRecord *);
85 
86 
87 static long init_record(struct dbCommon *pcommon, int pass)
88 {
89  struct selRecord *prec = (struct selRecord *)pcommon;
90  struct link *plink;
91  int i;
92  double *pvalue;
93 
94  if (pass==0)
95  return 0;
96 
97  /* get seln initial value if nvl is a constant*/
98  recGblInitConstantLink(&prec->nvl, DBF_USHORT, &prec->seln);
99 
100  plink = &prec->inpa;
101  pvalue = &prec->a;
102  for (i=0; i<SEL_MAX; i++, plink++, pvalue++) {
103  *pvalue = epicsNAN;
104  recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
105  }
106  return 0;
107 }
108 
109 static long process(struct dbCommon *pcommon)
110 {
111  struct selRecord *prec = (struct selRecord *)pcommon;
112  prec->pact = TRUE;
113  if ( RTN_SUCCESS(fetch_values(prec)) ) {
114  do_sel(prec);
115  }
116 
117  recGblGetTimeStamp(prec);
118  /* check for alarms */
119  checkAlarms(prec);
120 
121 
122  /* check event list */
123  monitor(prec);
124 
125  /* process the forward scan link record */
126  recGblFwdLink(prec);
127 
128  prec->pact=FALSE;
129  return(0);
130 }
131 
132 
133 #define indexof(field) selRecord##field
134 
135 static long get_units(DBADDR *paddr, char *units)
136 {
137  selRecord *prec=(selRecord *)paddr->precord;
138 
139  if(paddr->pfldDes->field_type == DBF_DOUBLE) {
140  strncpy(units,prec->egu,DB_UNITS_SIZE);
141  }
142  return(0);
143 }
144 
145 static long get_precision(const DBADDR *paddr, long *precision)
146 {
147  selRecord *prec=(selRecord *)paddr->precord;
148  double *pvalue,*plvalue;
149  int i;
150 
151  *precision = prec->prec;
152  if(paddr->pfield==(void *)&prec->val){
153  return(0);
154  }
155  pvalue = &prec->a;
156  plvalue = &prec->la;
157  for(i=0; i<SEL_MAX; i++, pvalue++, plvalue++) {
158  if(paddr->pfield==(void *)&pvalue
159  || paddr->pfield==(void *)&plvalue){
160  return(0);
161  }
162  }
163  recGblGetPrec(paddr,precision);
164  return(0);
165 }
166 
167 
168 static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
169 {
170  selRecord *prec=(selRecord *)paddr->precord;
171  int index = dbGetFieldIndex(paddr);
172 
173  switch (index) {
174  case indexof(VAL):
175  case indexof(HIHI):
176  case indexof(HIGH):
177  case indexof(LOW):
178  case indexof(LOLO):
179  case indexof(LALM):
180  case indexof(ALST):
181  case indexof(MLST):
182 #ifdef __GNUC__
183  case indexof(A) ... indexof(L):
184  case indexof(LA) ... indexof(LL):
185  break;
186  default:
187 #else
188  break;
189  default:
190  if((index >= indexof(A) && index <= indexof(L))
191  || (index >= indexof(LA) && index <= indexof(LL)))
192  break;
193 #endif
194  recGblGetGraphicDouble(paddr,pgd);
195  return(0);
196  }
197  pgd->upper_disp_limit = prec->hopr;
198  pgd->lower_disp_limit = prec->lopr;
199  return(0);
200 }
201 
202 static long get_control_double(struct dbAddr *paddr, struct dbr_ctrlDouble *pcd)
203 {
204  selRecord *prec=(selRecord *)paddr->precord;
205  int index = dbGetFieldIndex(paddr);
206 
207  switch (index) {
208  case indexof(VAL):
209  case indexof(HIHI):
210  case indexof(HIGH):
211  case indexof(LOW):
212  case indexof(LOLO):
213  case indexof(LALM):
214  case indexof(ALST):
215  case indexof(MLST):
216 #ifdef __GNUC__
217  case indexof(A) ... indexof(L):
218  case indexof(LA) ... indexof(LL):
219  break;
220  default:
221 #else
222  break;
223  default:
224  if((index >= indexof(A) && index <= indexof(L))
225  || (index >= indexof(LA) && index <= indexof(LL)))
226  break;
227 #endif
228  recGblGetControlDouble(paddr,pcd);
229  return(0);
230  }
231  pcd->upper_ctrl_limit = prec->hopr;
232  pcd->lower_ctrl_limit = prec->lopr;
233  return(0);
234 }
235 
236 static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
237 {
238  selRecord *prec=(selRecord *)paddr->precord;
239 
240  if(dbGetFieldIndex(paddr) == indexof(VAL)) {
241  pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
242  pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
243  pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
244  pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
245  } else recGblGetAlarmDouble(paddr,pad);
246  return(0);
247 }
248 
249 static void checkAlarms(selRecord *prec)
250 {
251  double val, hyst, lalm;
252  double alev;
253  epicsEnum16 asev;
254 
255  if (prec->udf) {
256  recGblSetSevr(prec, UDF_ALARM, prec->udfs);
257  return;
258  }
259 
260  val = prec->val;
261  hyst = prec->hyst;
262  lalm = prec->lalm;
263 
264  /* alarm condition hihi */
265  asev = prec->hhsv;
266  alev = prec->hihi;
267  if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
268  if (recGblSetSevr(prec, HIHI_ALARM, asev))
269  prec->lalm = alev;
270  return;
271  }
272 
273  /* alarm condition lolo */
274  asev = prec->llsv;
275  alev = prec->lolo;
276  if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
277  if (recGblSetSevr(prec, LOLO_ALARM, asev))
278  prec->lalm = alev;
279  return;
280  }
281 
282  /* alarm condition high */
283  asev = prec->hsv;
284  alev = prec->high;
285  if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
286  if (recGblSetSevr(prec, HIGH_ALARM, asev))
287  prec->lalm = alev;
288  return;
289  }
290 
291  /* alarm condition low */
292  asev = prec->lsv;
293  alev = prec->low;
294  if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
295  if (recGblSetSevr(prec, LOW_ALARM, asev))
296  prec->lalm = alev;
297  return;
298  }
299 
300  /* we get here only if val is out of alarm by at least hyst */
301  prec->lalm = val;
302  return;
303 }
304 
305 static void monitor(selRecord *prec)
306 {
307  unsigned monitor_mask;
308  double *pnew;
309  double *pprev;
310  int i;
311 
312  monitor_mask = recGblResetAlarms(prec);
313 
314  /* check for value change */
315  recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
316 
317  /* check for archive change */
318  recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
319 
320  /* send out monitors connected to the value field */
321  if (monitor_mask)
322  db_post_events(prec, &prec->val, monitor_mask);
323 
324  monitor_mask |= DBE_VALUE|DBE_LOG;
325 
326  /* trigger monitors of the SELN field */
327  if (prec->nlst != prec->seln) {
328  prec->nlst = prec->seln;
329  db_post_events(prec, &prec->seln, monitor_mask);
330  }
331 
332  /* check all input fields for changes, even if VAL hasn't changed */
333  for(i=0, pnew=&prec->a, pprev=&prec->la; i<SEL_MAX; i++, pnew++, pprev++) {
334  if(*pnew != *pprev) {
335  db_post_events(prec, pnew, monitor_mask);
336  *pprev = *pnew;
337  }
338  }
339  return;
340 }
341 
342 static void do_sel(selRecord *prec)
343 {
344  double *pvalue;
345  double order[SEL_MAX];
346  unsigned short count;
347  unsigned short i,j;
348  double val;
349 
350  /* selection mechanism */
351  pvalue = &prec->a;
352  switch (prec->selm){
353  case (selSELM_Specified):
354  if (prec->seln >= SEL_MAX) {
355  recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
356  return;
357  }
358  val = *(pvalue+prec->seln);
359  break;
360  case (selSELM_High_Signal):
361  val = -epicsINF;
362  for (i = 0; i < SEL_MAX; i++,pvalue++){
363  if (!isnan(*pvalue) && val < *pvalue) {
364  val = *pvalue;
365  prec->seln = i;
366  }
367  }
368  break;
369  case (selSELM_Low_Signal):
370  val = epicsINF;
371  for (i = 0; i < SEL_MAX; i++,pvalue++){
372  if (!isnan(*pvalue) && val > *pvalue) {
373  val = *pvalue;
374  prec->seln = i;
375  }
376  }
377  break;
378  case (selSELM_Median_Signal):
379  count = 0;
380  order[0] = epicsNAN;
381  for (i = 0; i < SEL_MAX; i++,pvalue++){
382  if (!isnan(*pvalue)){
383  /* Insertion sort */
384  j = count;
385  while ((j > 0) && (order[j-1] > *pvalue)){
386  order[j] = order[j-1];
387  j--;
388  }
389  order[j] = *pvalue;
390  count++;
391  }
392  }
393  prec->seln = count;
394  val = order[count / 2];
395  break;
396  default:
397  recGblSetSevr(prec,CALC_ALARM,INVALID_ALARM);
398  return;
399  }
400  prec->val = val;
401  prec->udf = isnan(prec->val);
402  return;
403 }
404 
405 /*
406  * FETCH_VALUES
407  *
408  * fetch the values for the variables from which to select
409  */
410 static int fetch_values(selRecord *prec)
411 {
412  struct link *plink;
413  double *pvalue;
414  int i;
415  long status;
416 
417  plink = &prec->inpa;
418  pvalue = &prec->a;
419  /* If mechanism is selSELM_Specified, only get the selected input*/
420  if(prec->selm == selSELM_Specified) {
421  /* fetch the select index */
422  status=dbGetLink(&(prec->nvl),DBR_USHORT,&(prec->seln),0,0);
423  if (!RTN_SUCCESS(status) || (prec->seln >= SEL_MAX))
424  return(status);
425 
426  plink += prec->seln;
427  pvalue += prec->seln;
428 
429  status=dbGetLink(plink,DBR_DOUBLE, pvalue,0,0);
430  return(status);
431  }
432  /* fetch all inputs*/
433  for(i=0; i<SEL_MAX; i++, plink++, pvalue++) {
434  status=dbGetLink(plink,DBR_DOUBLE, pvalue,0,0);
435  }
436  return(status);
437 }
#define HIHI_ALARM
Definition: alarm.h:94
#define RSETNUMBER
Definition: recSup.h:92
#define FALSE
Definition: dbDefs.h:32
#define put_array_info
Definition: selRecord.c:47
unsigned * LA
Definition: lalr.c:22
pvd::Status status
#define indexof(field)
Definition: selRecord.c:133
#define RTN_SUCCESS(STATUS)
Definition: errMdef.h:18
int i
Definition: scan.c:967
#define DBR_USHORT
Definition: dbFldTypes.h:80
epicsExportAddress(rset, selRSET)
#define report
Definition: selRecord.c:39
#define init_record
#define SOFT_ALARM
Definition: alarm.h:106
Miscellaneous macro definitions.
#define DBE_ARCHIVE
Definition: caeventmask.h:39
#define get_control_double
Definition: biRecord.c:58
#define DBE_VALUE
Definition: caeventmask.h:38
#define HIGH_ALARM
Definition: alarm.h:95
#define CALC_ALARM
Definition: alarm.h:103
#define cvt_dbaddr
Definition: selRecord.c:45
rset selRSET
Definition: selRecord.c:57
#define isnan(x)
Definition: epicsMath.h:21
#define DBE_LOG
Definition: caeventmask.h:40
#define get_units
Definition: biRecord.c:52
#define DBR_DOUBLE
Definition: db_access.h:76
#define get_enum_str
Definition: selRecord.c:50
float epicsNAN
Definition: epicsMath.cpp:35
#define put_enum_str
Definition: selRecord.c:52
epicsUInt16 epicsEnum16
Definition: epicsTypes.h:47
#define get_precision
Definition: biRecord.c:53
#define get_array_info
Definition: selRecord.c:46
#define TRUE
Definition: dbDefs.h:27
#define special
Definition: selRecord.c:43
#define get_enum_strs
Definition: selRecord.c:51
#define get_value
Definition: selRecord.c:44
Definition: recSup.h:67
int prec
Definition: reader.c:29
#define initialize
Definition: selRecord.c:40
#define INVALID_ALARM
Definition: alarm.h:53
#define LOLO_ALARM
Definition: alarm.h:96
#define LOW_ALARM
Definition: alarm.h:97
#define get_graphic_double
Definition: biRecord.c:57
#define SEL_MAX
Definition: selRecord.c:79
#define get_alarm_double
Definition: aaiRecord.c:69
#define UDF_ALARM
Definition: alarm.h:108
float epicsINF
Definition: epicsMath.cpp:36
Exporting IOC objects.