This is Unofficial EPICS BASE Doxygen Site
aSubRecord.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * EPICS BASE is distributed subject to a Software License Agreement found
5 * in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7 /*
8  * Record Support Routines for the Array Subroutine Record type,
9  * derived from Andy Foster's genSub record, with some features
10  * removed and asynchronous support added.
11  *
12  * Original Author: Andy Foster
13  * Revised by: Andrew Johnson
14  *
15  */
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "alarm.h"
22 #include "cantProceed.h"
23 #include "dbDefs.h"
24 #include "dbEvent.h"
25 #include "dbAccess.h"
26 #include "dbFldTypes.h"
27 #include "dbStaticLib.h"
28 #include "errMdef.h"
29 #include "errlog.h"
30 #include "recSup.h"
31 #include "devSup.h"
32 #include "special.h"
33 #include "registryFunction.h"
34 #include "recGbl.h"
35 #define GEN_SIZE_OFFSET
36 #include "aSubRecord.h"
37 #undef GEN_SIZE_OFFSET
38 #include "epicsExport.h"
39 
40 
41 typedef long (*GENFUNCPTR)(struct aSubRecord *);
42 
43 /* Create RSET - Record Support Entry Table*/
44 
45 #define report NULL
46 #define initialize NULL
47 static long init_record(struct dbCommon *, int);
48 static long process(struct dbCommon *);
49 static long special(DBADDR *, int);
50 #define get_value NULL
51 static long cvt_dbaddr(DBADDR *);
52 static long get_array_info(DBADDR *, long *, long *);
53 static long put_array_info(DBADDR *, long );
54 static long get_units(DBADDR *, char *);
55 static long get_precision(const DBADDR *, long *);
56 #define get_enum_str NULL
57 #define get_enum_strs NULL
58 #define put_enum_str NULL
59 static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
60 static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
61 static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
62 
64  RSETNUMBER,
65  report,
66  initialize,
68  process,
69  special,
70  get_value,
71  cvt_dbaddr,
74  get_units,
82 };
83 epicsExportAddress(rset, aSubRSET);
84 
85 static long initFields(epicsEnum16 *pft, epicsUInt32 *pno, epicsUInt32 *pne,
86  epicsUInt32 *pon, const char **fldnames, void **pval, void **povl);
87 static long fetch_values(aSubRecord *prec);
88 static void monitor(aSubRecord *);
89 static long do_sub(aSubRecord *);
90 
91 #define NUM_ARGS 21
92 
93 /* These are the names of the Input fields */
94 static const char *Ifldnames[] = {
95  "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
96  "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U"
97 };
98 
99 /* These are the names of the Output fields */
100 static const char *Ofldnames[] = {
101  "VALA", "VALB", "VALC", "VALD", "VALE", "VALF", "VALG",
102  "VALH", "VALI", "VALJ", "VALK", "VALL", "VALM", "VALN",
103  "VALO", "VALP", "VALQ", "VALR", "VALS", "VALT", "VALU"
104 };
105 
106 
107 static long init_record(struct dbCommon *pcommon, int pass)
108 {
109  struct aSubRecord *prec = (struct aSubRecord *)pcommon;
110  STATIC_ASSERT(sizeof(prec->onam)==sizeof(prec->snam));
111  GENFUNCPTR pfunc;
112  int i;
113 
114  if (pass == 0) {
115  /* Allocate memory for arrays */
116  initFields(&prec->fta, &prec->noa, &prec->nea, NULL,
117  Ifldnames, &prec->a, NULL);
118  initFields(&prec->ftva, &prec->nova, &prec->neva, &prec->onva,
119  Ofldnames, &prec->vala, &prec->ovla);
120  return 0;
121  }
122 
123  /* Initialize the Subroutine Name Link */
124  recGblInitConstantLink(&prec->subl, DBF_STRING, prec->snam);
125 
126  /* Initialize Input Links */
127  for (i = 0; i < NUM_ARGS; i++) {
128  struct link *plink = &(&prec->inpa)[i];
129  short dbr = (&prec->fta)[i];
130  long n = (&prec->noa)[i];
131 
132  dbLoadLinkArray(plink, dbr, (&prec->a)[i], &n);
133  if (n > 0)
134  (&prec->nea)[i] = n;
135  }
136 
137  /* Call the user initialization routine if there is one */
138  if (prec->inam[0] != 0) {
139  pfunc = (GENFUNCPTR)registryFunctionFind(prec->inam);
140  if (pfunc) {
141  pfunc(prec);
142  } else {
143  recGblRecordError(S_db_BadSub, (void *)prec,
144  "aSubRecord::init_record - INAM subr not found");
145  return S_db_BadSub;
146  }
147  }
148 
149  if (prec->lflg == aSubLFLG_IGNORE &&
150  prec->snam[0] != 0) {
151  pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam);
152  if (pfunc)
153  prec->sadr = pfunc;
154  else {
155  recGblRecordError(S_db_BadSub, (void *)prec,
156  "aSubRecord::init_record - SNAM subr not found");
157  return S_db_BadSub;
158  }
159  }
160  strcpy(prec->onam, prec->snam);
161  prec->oval = prec->val;
162  for (i = 0; i < NUM_ARGS; i++) {
163  epicsUInt32 nev = (&prec->neva)[i];
164 
165  (&prec->onva)[i] = nev;
166  if (nev)
167  memcpy((&prec->ovla)[i], (&prec->vala)[i],
168  dbValueSize((&prec->ftva)[i]) * nev);
169  }
170  return 0;
171 }
172 
173 
174 static long initFields(epicsEnum16 *pft, epicsUInt32 *pno, epicsUInt32 *pne,
175  epicsUInt32 *pon, const char **fldnames, void **pval, void **povl)
176 {
177  int i;
178  long status = 0;
179 
180  for (i = 0; i < NUM_ARGS; i++, pft++, pno++, pne++, pval++) {
181  epicsUInt32 num;
182  epicsUInt32 flen;
183 
184  if (*pft > DBF_ENUM)
185  *pft = DBF_CHAR;
186 
187  if (*pno == 0)
188  *pno = 1;
189 
190  flen = dbValueSize(*pft);
191  num = *pno * flen;
192  *pval = callocMustSucceed(*pno, flen, "aSubRecord::init_record");
193 
194  *pne = *pno;
195 
196  if (povl) {
197  if (num)
198  *povl = callocMustSucceed(*pno, flen,
199  "aSubRecord::init_record");
200  povl++;
201  *pon++ = *pne;
202  }
203  }
204  return status;
205 }
206 
207 
208 static long process(struct dbCommon *pcommon)
209 {
210  struct aSubRecord *prec = (struct aSubRecord *)pcommon;
211  int pact = prec->pact;
212  long status = 0;
213 
214  if (!pact) {
215  prec->pact = TRUE;
216  status = fetch_values(prec);
217  prec->pact = FALSE;
218  }
219 
220  if (!status) {
221  status = do_sub(prec);
222  prec->val = status;
223  }
224 
225  if (!pact && prec->pact)
226  return 0;
227 
228  prec->pact = TRUE;
229 
230  /* Push the output link values */
231  if (!status) {
232  int i;
233 
234  for (i = 0; i < NUM_ARGS; i++)
235  dbPutLink(&(&prec->outa)[i], (&prec->ftva)[i], (&prec->vala)[i],
236  (&prec->neva)[i]);
237  }
238 
239  recGblGetTimeStamp(prec);
240  monitor(prec);
241  recGblFwdLink(prec);
242  prec->pact = FALSE;
243 
244  return 0;
245 }
246 
247 static long fetch_values(aSubRecord *prec)
248 {
249  long status;
250  int i;
251 
252  if (prec->lflg == aSubLFLG_READ) {
253  /* Get the Subroutine Name and look it up if changed */
254  status = dbGetLink(&prec->subl, DBR_STRING, prec->snam, 0, 0);
255  if (status)
256  return status;
257 
258  if (prec->snam[0] != 0 &&
259  strcmp(prec->snam, prec->onam)) {
260  GENFUNCPTR pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam);
261 
262  if (!pfunc)
263  return S_db_BadSub;
264 
265  if (prec->sadr!=pfunc && prec->cadr) {
266  prec->cadr(prec);
267  prec->cadr = NULL;
268  }
269 
270  prec->sadr = pfunc;
271  strcpy(prec->onam, prec->snam);
272  }
273  }
274 
275  /* Get the input link values */
276  for (i = 0; i < NUM_ARGS; i++) {
277  long nRequest = (&prec->noa)[i];
278  status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,
279  &nRequest);
280  if (nRequest > 0)
281  (&prec->nea)[i] = nRequest;
282  if (status)
283  return status;
284  }
285  return 0;
286 }
287 
288 #define indexof(field) aSubRecord##field
289 
290 static long get_inlinkNumber(int fieldIndex) {
291  if (fieldIndex >= indexof(A) && fieldIndex <= indexof(U))
292  return fieldIndex - indexof(A);
293  return -1;
294 }
295 
296 static long get_outlinkNumber(int fieldIndex) {
297  if (fieldIndex >= indexof(VALA) && fieldIndex <= indexof(VALU))
298  return fieldIndex - indexof(VALA);
299  return -1;
300 }
301 
302 static long get_units(DBADDR *paddr, char *units)
303 {
304  aSubRecord *prec = (aSubRecord *)paddr->precord;
305  int linkNumber;
306 
307  linkNumber = get_inlinkNumber(dbGetFieldIndex(paddr));
308  if (linkNumber >= 0) {
309  dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE);
310  return 0;
311  }
312  linkNumber = get_outlinkNumber(dbGetFieldIndex(paddr));
313  if (linkNumber >= 0) {
314  dbGetUnits(&prec->outa + linkNumber, units, DB_UNITS_SIZE);
315  }
316  return 0;
317 }
318 
319 static long get_precision(const DBADDR *paddr, long *pprecision)
320 {
321  aSubRecord *prec = (aSubRecord *)paddr->precord;
322  int fieldIndex = dbGetFieldIndex(paddr);
323  int linkNumber;
324 
325  *pprecision = prec->prec;
326  linkNumber = get_inlinkNumber(fieldIndex);
327  if (linkNumber >= 0) {
328  short precision;
329 
330  if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0)
331  *pprecision = precision;
332  return 0;
333  }
334 
335  linkNumber = get_outlinkNumber(fieldIndex);
336  if (linkNumber >= 0) {
337  short precision;
338 
339  if (dbGetPrecision(&prec->outa + linkNumber, &precision) == 0)
340  *pprecision = precision;
341  } else
342  recGblGetPrec(paddr, pprecision);
343  return 0;
344 }
345 
346 static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
347 {
348  aSubRecord *prec = (aSubRecord *)paddr->precord;
349  int fieldIndex = dbGetFieldIndex(paddr);
350  int linkNumber;
351 
352  linkNumber = get_inlinkNumber(fieldIndex);
353  if (linkNumber >= 0) {
354  dbGetGraphicLimits(&prec->inpa + linkNumber,
355  &pgd->lower_disp_limit,
356  &pgd->upper_disp_limit);
357  return 0;
358  }
359  linkNumber = get_outlinkNumber(fieldIndex);
360  if (linkNumber >= 0) {
361  dbGetGraphicLimits(&prec->outa + linkNumber,
362  &pgd->lower_disp_limit,
363  &pgd->upper_disp_limit);
364  }
365  return 0;
366 }
367 
368 static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
369 {
370  recGblGetControlDouble(paddr,pcd);
371  return 0;
372 }
373 
374 static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
375 {
376  aSubRecord *prec = (aSubRecord *)paddr->precord;
377  int fieldIndex = dbGetFieldIndex(paddr);
378  int linkNumber;
379 
380  linkNumber = get_inlinkNumber(fieldIndex);
381  if (linkNumber >= 0) {
382  dbGetAlarmLimits(&prec->inpa + linkNumber,
383  &pad->lower_alarm_limit,
384  &pad->lower_warning_limit,
385  &pad->upper_warning_limit,
386  &pad->upper_alarm_limit);
387  return 0;
388  }
389  linkNumber = get_outlinkNumber(fieldIndex);
390  if (linkNumber >= 0) {
391  dbGetAlarmLimits(&prec->outa + linkNumber,
392  &pad->lower_alarm_limit,
393  &pad->lower_warning_limit,
394  &pad->upper_warning_limit,
395  &pad->upper_alarm_limit);
396  return 0;
397  }
398  recGblGetAlarmDouble(paddr, pad);
399  return 0;
400 }
401 
402 static void monitor(aSubRecord *prec)
403 {
404  int i;
405  unsigned short monitor_mask;
406 
407  monitor_mask = recGblResetAlarms(prec) | DBE_VALUE | DBE_LOG;
408 
409  /* Post events for VAL field */
410  if (prec->val != prec->oval) {
411  db_post_events(prec, &prec->val, monitor_mask);
412  prec->oval = prec->val;
413  }
414 
415  /* Event posting on VAL arrays depends on the setting of prec->eflg */
416  switch (prec->eflg) {
417  case aSubEFLG_NEVER:
418  break;
419  case aSubEFLG_ON_CHANGE:
420  for (i = 0; i < NUM_ARGS; i++) {
421  void *povl = (&prec->ovla)[i];
422  void *pval = (&prec->vala)[i];
423  epicsUInt32 *ponv = &(&prec->onva)[i];
424  epicsUInt32 *pnev = &(&prec->neva)[i];
425  epicsUInt32 onv = *ponv; /* Num Elements in OVLx */
426  epicsUInt32 nev = *pnev; /* Num Elements in VALx */
427  epicsUInt32 alen = dbValueSize((&prec->ftva)[i]) * nev;
428 
429  if (nev != onv || memcmp(povl, pval, alen)) {
430  memcpy(povl, pval, alen);
431  db_post_events(prec, pval, monitor_mask);
432  if (nev != onv) {
433  *ponv = nev;
434  db_post_events(prec, pnev, monitor_mask);
435  }
436  }
437  }
438  break;
439  case aSubEFLG_ALWAYS:
440  for (i = 0; i < NUM_ARGS; i++) {
441  db_post_events(prec, (&prec->vala)[i], monitor_mask);
442  db_post_events(prec, &(&prec->neva)[i], monitor_mask);
443  }
444  break;
445  }
446  return;
447 }
448 
449 
450 static long do_sub(aSubRecord *prec)
451 {
452  GENFUNCPTR pfunc = prec->sadr;
453  long status;
454 
455  if (prec->snam[0] == 0)
456  return 0;
457 
458  if (pfunc == NULL) {
459  recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM);
460  return S_db_BadSub;
461  }
462  status = pfunc(prec);
463  if (status < 0)
464  recGblSetSevr(prec, SOFT_ALARM, prec->brsv);
465  else
466  prec->udf = FALSE;
467 
468  return status;
469 }
470 
471 
472 static long cvt_dbaddr(DBADDR *paddr)
473 {
474  aSubRecord *prec = (aSubRecord *)paddr->precord;
475  int fieldIndex = dbGetFieldIndex(paddr);
476 
477  if (fieldIndex >= aSubRecordA &&
478  fieldIndex <= aSubRecordU) {
479  int offset = fieldIndex - aSubRecordA;
480 
481  paddr->pfield = (&prec->a )[offset];
482  paddr->no_elements = (&prec->noa)[offset];
483  paddr->field_type = (&prec->fta)[offset];
484  }
485  else if (fieldIndex >= aSubRecordVALA &&
486  fieldIndex <= aSubRecordVALU) {
487  int offset = fieldIndex - aSubRecordVALA;
488 
489  paddr->pfield = (&prec->vala)[offset];
490  paddr->no_elements = (&prec->nova)[offset];
491  paddr->field_type = (&prec->ftva)[offset];
492  }
493  else {
494  errlogPrintf("aSubRecord::cvt_dbaddr called for %s.%s\n",
495  prec->name, paddr->pfldDes->name);
496  return 0;
497  }
498  paddr->dbr_field_type = paddr->field_type;
499  paddr->field_size = dbValueSize(paddr->field_type);
500  return 0;
501 }
502 
503 
504 static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
505 {
506  aSubRecord *prec = (aSubRecord *)paddr->precord;
507  int fieldIndex = dbGetFieldIndex(paddr);
508 
509  if (fieldIndex >= aSubRecordA &&
510  fieldIndex <= aSubRecordU) {
511  *no_elements = (&prec->nea)[fieldIndex - aSubRecordA];
512  }
513  else if (fieldIndex >= aSubRecordVALA &&
514  fieldIndex <= aSubRecordVALU) {
515  *no_elements = (&prec->neva)[fieldIndex - aSubRecordVALA];
516  }
517  else {
518  errlogPrintf("aSubRecord::get_array_info called for %s.%s\n",
519  prec->name, paddr->pfldDes->name);
520  }
521  *offset = 0;
522 
523  return 0;
524 }
525 
526 
527 static long put_array_info(DBADDR *paddr, long nNew)
528 {
529  aSubRecord *prec = (aSubRecord *)paddr->precord;
530  int fieldIndex = dbGetFieldIndex(paddr);
531 
532  if (fieldIndex >= aSubRecordA &&
533  fieldIndex <= aSubRecordU) {
534  (&prec->nea)[fieldIndex - aSubRecordA] = nNew;
535  }
536  else if (fieldIndex >= aSubRecordVALA &&
537  fieldIndex <= aSubRecordVALU) {
538  (&prec->neva)[fieldIndex - aSubRecordVALA] = nNew;
539  }
540  else {
541  errlogPrintf("aSubRecord::put_array_info called for %s.%s\n",
542  prec->name, paddr->pfldDes->name);
543  }
544  return 0;
545 }
546 
547 
548 static long special(DBADDR *paddr, int after)
549 {
550  aSubRecord *prec = (aSubRecord *)paddr->precord;
551  long status = 0;
552 
553  if (after &&
554  prec->lflg == aSubLFLG_IGNORE) {
555  GENFUNCPTR pfunc;
556  if (prec->snam[0] == 0)
557  pfunc = 0;
558  else {
559  pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam);
560  if (!pfunc) {
561  status = S_db_BadSub;
562  recGblRecordError(status, (void *)prec, prec->snam);
563  }
564  }
565 
566  if (prec->sadr != pfunc && prec->cadr) {
567  prec->cadr(prec);
568  prec->cadr = NULL;
569  }
570 
571  prec->sadr = pfunc;
572  }
573  return status;
574 }
#define RSETNUMBER
Definition: recSup.h:92
#define DBR_STRING
Definition: db_access.h:69
#define NUM_ARGS
Definition: aSubRecord.c:91
#define FALSE
Definition: dbDefs.h:32
pvd::Status status
int i
Definition: scan.c:967
rset aSubRSET
Definition: aSubRecord.c:63
#define init_record
epicsExportAddress(rset, aSubRSET)
#define indexof(field)
Definition: aSubRecord.c:288
#define SOFT_ALARM
Definition: alarm.h:106
#define NULL
Definition: catime.c:38
long(* GENFUNCPTR)(struct aSubRecord *)
Definition: aSubRecord.c:41
unsigned int epicsUInt32
Definition: epicsTypes.h:43
Miscellaneous macro definitions.
#define get_control_double
Definition: biRecord.c:58
#define DBE_VALUE
Definition: caeventmask.h:38
#define get_array_info
Definition: aiRecord.c:56
Device support routines.
#define initialize
Definition: aSubRecord.c:46
#define report
Definition: aSubRecord.c:45
#define STATIC_ASSERT(expr)
Declare a condition that should be true at compile-time.
Definition: epicsAssert.h:86
#define DBE_LOG
Definition: caeventmask.h:40
#define get_units
Definition: biRecord.c:52
#define get_enum_str
Definition: aSubRecord.c:56
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
Definition: cantProceed.c:22
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
#define put_enum_str
Definition: aSubRecord.c:58
epicsUInt16 epicsEnum16
Definition: epicsTypes.h:47
#define BAD_SUB_ALARM
Definition: alarm.h:107
#define get_precision
Definition: biRecord.c:53
#define put_array_info
Definition: aiRecord.c:57
#define TRUE
Definition: dbDefs.h:27
epicsShareFunc REGISTRYFUNCTION registryFunctionFind(const char *name)
if(yy_init)
Definition: scan.c:972
Definition: recSup.h:67
Routines for code that can&#39;t continue or return after an error.
#define get_value
Definition: aSubRecord.c:50
int prec
Definition: reader.c:29
#define INVALID_ALARM
Definition: alarm.h:53
#define get_graphic_double
Definition: biRecord.c:57
#define cvt_dbaddr
Definition: aiRecord.c:55
#define special
Definition: dfanoutRecord.c:50
#define get_alarm_double
Definition: aaiRecord.c:69
#define get_enum_strs
Definition: aSubRecord.c:57
Exporting IOC objects.