This is Unofficial EPICS BASE Doxygen Site
seqRecord.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2012 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  * Author: John Winans
12  * Date: 09-21-92
13  */
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include "alarm.h"
19 #include "callback.h"
20 #include "dbAccess.h"
21 #include "dbEvent.h"
22 #include "epicsTypes.h"
23 #include "link.h"
24 #include "recSup.h"
25 #include "recGbl.h"
26 
27 #define GEN_SIZE_OFFSET
28 #include "seqRecord.h"
29 #undef GEN_SIZE_OFFSET
30 #include "epicsExport.h"
31 
32 static void processNextLink(seqRecord *prec);
33 static long asyncFinish(seqRecord *prec);
34 static void processCallback(epicsCallback *arg);
35 
36 /* Create RSET - Record Support Entry Table*/
37 #define report NULL
38 #define initialize NULL
39 static long init_record(struct dbCommon *prec, int pass);
40 static long process(struct dbCommon *prec);
41 #define special NULL
42 #define get_value NULL
43 #define cvt_dbaddr NULL
44 #define get_array_info NULL
45 #define put_array_info NULL
46 static long get_units(DBADDR *, char *);
47 static long get_precision(const DBADDR *paddr, long *);
48 #define get_enum_str NULL
49 #define get_enum_strs NULL
50 #define put_enum_str NULL
51 static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
52 static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
53 static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
54 
56  RSETNUMBER,
57  report,
58  initialize,
60  process,
61  special,
62  get_value,
63  cvt_dbaddr,
66  get_units,
74 };
75 epicsExportAddress(rset, seqRSET);
76 
79 
80 double seqDLYlimit = 100000;
82 
83 
84 /* Total number of link-groups */
85 #define NUM_LINKS 16
86 
87 /* Each link-group looks like this */
88 typedef struct linkGrp {
89  double dly; /* Delay in seconds */
90  DBLINK dol; /* Input link */
91  double dov; /* Value storage */
92  DBLINK lnk; /* Output link */
93 } linkGrp;
94 
95 /* The list of link-groups for processing */
96 typedef struct seqRecPvt {
97  epicsCallback callback;
98  seqRecord *prec;
99  linkGrp *grps[NUM_LINKS + 1]; /* List of link-groups */
100  int index; /* Where we are now */
101 } seqRecPvt;
102 
103 
104 static long init_record(struct dbCommon *pcommon, int pass)
105 {
106  struct seqRecord *prec = (struct seqRecord *)pcommon;
107  int index;
108  linkGrp *grp;
109  seqRecPvt *pseqRecPvt;
110 
111  if (pass == 0)
112  return 0;
113 
114  pseqRecPvt = (seqRecPvt *)calloc(1, sizeof(seqRecPvt));
115  pseqRecPvt->prec = prec;
116  callbackSetCallback(processCallback, &pseqRecPvt->callback);
117  callbackSetUser(pseqRecPvt, &pseqRecPvt->callback);
118  prec->dpvt = pseqRecPvt;
119 
120  recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln);
121 
122  grp = (linkGrp *) &prec->dly0;
123  for (index = 0; index < NUM_LINKS; index++, grp++) {
124  recGblInitConstantLink(&grp->dol, DBF_DOUBLE, &grp->dov);
125  }
126 
127  prec->oldn = prec->seln;
128 
129  return 0;
130 }
131 
132 static long process(struct dbCommon *pcommon)
133 {
134  struct seqRecord *prec = (struct seqRecord *)pcommon;
135  seqRecPvt *pcb = (seqRecPvt *) prec->dpvt;
136  linkGrp *pgrp;
137  epicsUInt16 lmask;
138  int i;
139 
140  if (prec->pact)
141  return asyncFinish(prec);
142  prec->pact = TRUE;
143 
144  /* Set callback from PRIO */
145  callbackSetPriority(prec->prio, &pcb->callback);
146 
147  if (prec->selm == seqSELM_All)
148  lmask = (1 << NUM_LINKS) - 1;
149  else {
150  /* Get SELN value */
151  dbGetLink(&prec->sell, DBR_USHORT, &prec->seln, 0, 0);
152 
153  if (prec->selm == seqSELM_Specified) {
154  int grpn = prec->seln + prec->offs;
155  if (grpn < 0 || grpn >= NUM_LINKS) {
156  recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
157  return asyncFinish(prec);
158  }
159 
160  lmask = 1 << grpn;
161  }
162  else if (prec->selm == seqSELM_Mask) {
163  int shft = prec->shft;
164  if (shft < -15 || shft > 15) {
165  /* Shifting by more than the number of bits in the
166  * value produces undefined behavior in C */
167  recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
168  return asyncFinish(prec);
169  }
170  lmask = (shft >= 0) ? prec->seln >> shft : prec->seln << -shft;
171  }
172  else {
173  recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
174  return asyncFinish(prec);
175  }
176  }
177 
178  /* Figure out which groups are to be processed */
179  pcb->index = 0;
180  pgrp = (linkGrp *) &prec->dly0;
181  for (i = 0; lmask; lmask >>= 1) {
182  if ((lmask & 1) &&
183  (!dbLinkIsConstant(&pgrp->lnk) ||
184  !dbLinkIsConstant(&pgrp->dol))) {
185  pcb->grps[i++] = pgrp;
186  }
187  pgrp++;
188  }
189  pcb->grps[i] = NULL; /* mark the end of the list */
190 
191  if (!i)
192  return asyncFinish(prec);
193 
194  /* Start processing link groups (we have at least one) */
195  processNextLink(prec);
196 
197  return 0;
198 }
199 
200 static void processNextLink(seqRecord *prec)
201 {
202  seqRecPvt *pcb = (seqRecPvt *) prec->dpvt;
203  linkGrp *pgrp = pcb->grps[pcb->index];
204 
205  if (pgrp == NULL) {
206  /* None left, finish up. */
207  prec->rset->process((dbCommon *)prec);
208  return;
209  }
210 
211  /* Always use the callback task to avoid recursion */
212  if (pgrp->dly > 0.0)
213  callbackRequestDelayed(&pcb->callback, pgrp->dly);
214  else
215  callbackRequest(&pcb->callback);
216 }
217 
218 static long asyncFinish(seqRecord *prec)
219 {
220  epicsUInt16 events;
221 
222  prec->udf = FALSE;
223  recGblGetTimeStamp(prec);
224 
225  /* post monitors */
226  events = recGblResetAlarms(prec);
227  if (events)
228  db_post_events(prec, &prec->val, events);
229  if (prec->seln != prec->oldn) {
230  db_post_events(prec, &prec->seln, events | DBE_VALUE | DBE_LOG);
231  prec->oldn = prec->seln;
232  }
233 
234  /* process the forward scan link record */
235  recGblFwdLink(prec);
236 
237  prec->pact = FALSE;
238  return 0;
239 }
240 
241 
242 static void processCallback(epicsCallback *arg)
243 {
244  seqRecPvt *pcb;
245  seqRecord *prec;
246  linkGrp *pgrp;
247  double odov;
248 
249  callbackGetUser(pcb, arg);
250  prec = pcb->prec;
251  dbScanLock((struct dbCommon *)prec);
252 
253  pgrp = pcb->grps[pcb->index];
254 
255  /* Save the old value */
256  odov = pgrp->dov;
257 
258  dbGetLink(&pgrp->dol, DBR_DOUBLE, &pgrp->dov, 0, 0);
259 
260  recGblGetTimeStamp(prec);
261 
262  /* Dump the value to the destination field */
263  dbPutLink(&pgrp->lnk, DBR_DOUBLE, &pgrp->dov, 1);
264 
265  if (odov != pgrp->dov) {
266  db_post_events(prec, &pgrp->dov, DBE_VALUE | DBE_LOG);
267  }
268 
269  /* Start the next link-group */
270  pcb->index++;
271  processNextLink(prec);
272 
273  dbScanUnlock((struct dbCommon *)prec);
274 }
275 
276 
277 #define indexof(field) seqRecord##field
278 #define get_dol(prec, fieldOffset) \
279  &((linkGrp *) &prec->dly0)[fieldOffset >> 2].dol
280 
281 static long get_units(DBADDR *paddr, char *units)
282 {
283  seqRecord *prec = (seqRecord *) paddr->precord;
284  int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
285 
286  if (fieldOffset >= 0)
287  switch (fieldOffset & 2) {
288  case 0: /* DLYn */
289  strcpy(units, "s");
290  break;
291  case 2: /* DOn */
292  dbGetUnits(get_dol(prec, fieldOffset),
293  units, DB_UNITS_SIZE);
294  }
295  return 0;
296 }
297 
298 static long get_precision(const DBADDR *paddr, long *pprecision)
299 {
300  seqRecord *prec = (seqRecord *) paddr->precord;
301  int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
302  short precision;
303 
304  if (fieldOffset >= 0)
305  switch (fieldOffset & 2) {
306  case 0: /* DLYn */
307  *pprecision = seqDLYprecision;
308  return 0;
309  case 2: /* DOn */
310  if (dbGetPrecision(get_dol(prec, fieldOffset), &precision) == 0) {
311  *pprecision = precision;
312  return 0;
313  }
314  }
315  *pprecision = prec->prec;
316  recGblGetPrec(paddr, pprecision);
317  return 0;
318 }
319 
320 static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
321 {
322  seqRecord *prec = (seqRecord *) paddr->precord;
323  int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
324 
325  if (fieldOffset >= 0)
326  switch (fieldOffset & 2) {
327  case 0: /* DLYn */
328  pgd->lower_disp_limit = 0.0;
329  pgd->lower_disp_limit = 10.0;
330  return 0;
331  case 2: /* DOn */
332  dbGetGraphicLimits(get_dol(prec, fieldOffset),
333  &pgd->lower_disp_limit,
334  &pgd->upper_disp_limit);
335  return 0;
336  }
337  recGblGetGraphicDouble(paddr, pgd);
338  return 0;
339 }
340 
341 static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
342 {
343  int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
344 
345  if (fieldOffset >= 0 && (fieldOffset & 2) == 0) { /* DLYn */
346  pcd->lower_ctrl_limit = 0.0;
347  pcd->upper_ctrl_limit = seqDLYlimit;
348  }
349  else
350  recGblGetControlDouble(paddr, pcd);
351  return 0;
352 }
353 
354 static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
355 {
356  seqRecord *prec = (seqRecord *) paddr->precord;
357  int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
358 
359  if (fieldOffset >= 0 && (fieldOffset & 2) == 2) /* DOn */
360  dbGetAlarmLimits(get_dol(prec, fieldOffset),
361  &pad->lower_alarm_limit, &pad->lower_warning_limit,
362  &pad->upper_warning_limit, &pad->upper_alarm_limit);
363  else
364  recGblGetAlarmDouble(paddr, pad);
365  return 0;
366 }
epicsExportAddress(rset, seqRSET)
#define RSETNUMBER
Definition: recSup.h:92
#define indexof(field)
Definition: seqRecord.c:277
#define FALSE
Definition: dbDefs.h:32
#define get_dol(prec, fieldOffset)
Definition: seqRecord.c:278
int i
Definition: scan.c:967
#define DBR_USHORT
Definition: dbFldTypes.h:80
unsigned short epicsUInt16
Definition: epicsTypes.h:41
#define cvt_dbaddr
Definition: seqRecord.c:43
#define init_record
double dov
Definition: seqRecord.c:91
#define get_enum_strs
Definition: seqRecord.c:49
#define SOFT_ALARM
Definition: alarm.h:106
#define NULL
Definition: catime.c:38
struct seqRecPvt seqRecPvt
DBLINK dol
Definition: seqRecord.c:90
linkGrp * grps[NUM_LINKS+1]
Definition: seqRecord.c:99
#define get_enum_str
Definition: seqRecord.c:48
epicsCallback callback
Definition: seqRecord.c:97
#define get_control_double
Definition: biRecord.c:58
#define DBE_VALUE
Definition: caeventmask.h:38
rset seqRSET
Definition: seqRecord.c:55
seqRecord * prec
Definition: seqRecord.c:98
#define get_array_info
Definition: seqRecord.c:44
#define DBE_LOG
Definition: caeventmask.h:40
#define get_units
Definition: biRecord.c:52
#define DBR_DOUBLE
Definition: db_access.h:76
#define initialize
Definition: seqRecord.c:38
#define report
Definition: seqRecord.c:37
#define put_array_info
Definition: seqRecord.c:45
int index
Definition: seqRecord.c:100
#define get_precision
Definition: biRecord.c:53
int seqDLYprecision
Definition: seqRecord.c:77
#define TRUE
Definition: dbDefs.h:27
#define put_enum_str
Definition: seqRecord.c:50
if(yy_init)
Definition: scan.c:972
Definition: recSup.h:67
#define NUM_LINKS
Definition: seqRecord.c:85
DBLINK lnk
Definition: seqRecord.c:92
int prec
Definition: reader.c:29
#define INVALID_ALARM
Definition: alarm.h:53
#define special
Definition: seqRecord.c:41
#define get_graphic_double
Definition: biRecord.c:57
#define get_value
Definition: seqRecord.c:42
struct linkGrp linkGrp
#define get_alarm_double
Definition: aaiRecord.c:69
double seqDLYlimit
Definition: seqRecord.c:80
double dly
Definition: seqRecord.c:89
Exporting IOC objects.