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