This is Unofficial EPICS BASE Doxygen Site
calcoutRecord.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 /* calcout.c - Record Support Routines for calc with output records */
11 /*
12  * Author : Ned Arnold
13  * Based on recCalc.c by Julie Sander and Bob Dalesio
14  * Date: 7-27-87
15  */
16 
17 #include <stddef.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <math.h>
23 
24 #include "alarm.h"
25 #include "dbDefs.h"
26 #include "dbAccess.h"
27 #include "dbEvent.h"
28 #include "dbLink.h"
29 #include "dbScan.h"
30 #include "cantProceed.h"
31 #include "epicsMath.h"
32 #include "errMdef.h"
33 #include "errlog.h"
34 #include "recSup.h"
35 #include "devSup.h"
36 #include "recGbl.h"
37 #include "special.h"
38 #include "callback.h"
39 #include "taskwd.h"
40 #include "menuIvoa.h"
41 
42 #define GEN_SIZE_OFFSET
43 #include "calcoutRecord.h"
44 #undef GEN_SIZE_OFFSET
45 #include "epicsExport.h"
46 
47 /* Create RSET - Record Support Entry Table*/
48 #define report NULL
49 #define initialize NULL
50 static long init_record(struct dbCommon *, int);
51 static long process(struct dbCommon *);
52 static long special(DBADDR *, int);
53 #define get_value NULL
54 #define cvt_dbaddr NULL
55 #define get_array_info NULL
56 #define put_array_info NULL
57 static long get_units(DBADDR *, char *);
58 static long get_precision(const DBADDR *, long *);
59 #define get_enum_str NULL
60 #define get_enum_strs NULL
61 #define put_enum_str NULL
62 static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
63 static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
64 static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
65 
67  RSETNUMBER,
68  report,
69  initialize,
71  process,
72  special,
73  get_value,
74  cvt_dbaddr,
77  get_units,
85 };
86 epicsExportAddress(rset, calcoutRSET);
87 
90 double calcoutODLYlimit = 100000;
92 
93 /* To provide feedback to the user as to the connection status of the
94  * links (.INxV and .OUTV), the following algorithm has been implemented ...
95  *
96  * A new PV_LINK is checked [in both init() and special()] to see if the
97  * target is local -- if so it is marked as such. If not, a checkLinkCb
98  * callback is scheduled to check the connection status later by calling
99  * dbIsLinkConnected(). Anytime there are unconnected CA_LINKs, another
100  * callback is scheduled. Once all connections are established, the CA_LINKs
101  * are checked whenever the record processes.
102  *
103  */
104 
105 #define NO_CA_LINKS 0
106 #define CA_LINKS_ALL_OK 1
107 #define CA_LINKS_NOT_OK 2
108 
109 typedef struct rpvtStruct {
110  epicsCallback doOutCb;
111  epicsCallback checkLinkCb;
112  short cbScheduled;
113  short caLinkStat; /* NO_CA_LINKS, CA_LINKS_ALL_OK, CA_LINKS_NOT_OK */
114 } rpvtStruct;
115 
116 static void checkAlarms(calcoutRecord *prec);
117 static void monitor(calcoutRecord *prec);
118 static int fetch_values(calcoutRecord *prec);
119 static void execOutput(calcoutRecord *prec);
120 static void checkLinks(calcoutRecord *prec);
121 static void checkLinksCallback(epicsCallback *arg);
122 static long writeValue(calcoutRecord *prec);
123 
125 
126 
127 static long init_record(struct dbCommon *pcommon, int pass)
128 {
129  struct calcoutRecord *prec = (struct calcoutRecord *)pcommon;
130  DBLINK *plink;
131  int i;
132  double *pvalue;
133  epicsEnum16 *plinkValid;
134  short error_number;
135  calcoutdset *pcalcoutDSET;
136  rpvtStruct *prpvt;
137 
138  if (pass == 0) {
139  prec->rpvt = (rpvtStruct *) callocMustSucceed(1, sizeof(rpvtStruct), "calcoutRecord");
140  return 0;
141  }
142 
143  if (!(pcalcoutDSET = (calcoutdset *)prec->dset)) {
144  recGblRecordError(S_dev_noDSET, (void *)prec, "calcout:init_record");
145  return S_dev_noDSET;
146  }
147 
148  /* must have write defined */
149  if ((pcalcoutDSET->common.number < 5) || (pcalcoutDSET->write ==NULL)) {
150  recGblRecordError(S_dev_missingSup, (void *)prec, "calcout:init_record");
151  return S_dev_missingSup;
152  }
153 
154  prpvt = prec->rpvt;
155  plink = &prec->inpa;
156  pvalue = &prec->a;
157  plinkValid = &prec->inav;
158 
159  for (i = 0; i <= CALCPERFORM_NARGS; i++, plink++, pvalue++, plinkValid++) {
160  /* Don't InitConstantLink the .OUT link */
161  if (i < CALCPERFORM_NARGS) {
162  recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
163  }
164 
165  if (dbLinkIsConstant(plink)) {
166  *plinkValid = calcoutINAV_CON;
167  }
168  else if (dbLinkIsVolatile(plink)) {
169  int conn = dbIsLinkConnected(plink);
170 
171  if (conn)
172  *plinkValid = calcoutINAV_EXT;
173  else {
174  /* Monitor for connection */
175  *plinkValid = calcoutINAV_EXT_NC;
176  prpvt->caLinkStat = CA_LINKS_NOT_OK;
177  }
178  }
179  else {
180  /* PV must reside on this ioc */
181  *plinkValid = calcoutINAV_LOC;
182 
183  if (!dbIsLinkConnected(plink)) {
184  errlogPrintf("calcout: %s.INP%c in no-vo disco state\n",
185  prec->name, i+'A');
186  }
187  }
188  }
189 
190  prec->clcv = postfix(prec->calc, prec->rpcl, &error_number);
191  if (prec->clcv){
192  recGblRecordError(S_db_badField, (void *)prec,
193  "calcout: init_record: Illegal CALC field");
194  errlogPrintf("%s.CALC: %s in expression \"%s\"\n",
195  prec->name, calcErrorStr(error_number), prec->calc);
196  }
197 
198  prec->oclv = postfix(prec->ocal, prec->orpc, &error_number);
199  if (prec->dopt == calcoutDOPT_Use_OVAL && prec->oclv){
200  recGblRecordError(S_db_badField, (void *)prec,
201  "calcout: init_record: Illegal OCAL field");
202  errlogPrintf("%s.OCAL: %s in expression \"%s\"\n",
203  prec->name, calcErrorStr(error_number), prec->ocal);
204  }
205 
206  prpvt = prec->rpvt;
207  callbackSetCallback(checkLinksCallback, &prpvt->checkLinkCb);
208  callbackSetPriority(0, &prpvt->checkLinkCb);
209  callbackSetUser(prec, &prpvt->checkLinkCb);
210  prpvt->cbScheduled = 0;
211 
212  prec->epvt = eventNameToHandle(prec->oevt);
213 
214  if (pcalcoutDSET->common.init_record) pcalcoutDSET->common.init_record(pcommon);
215  prec->pval = prec->val;
216  prec->mlst = prec->val;
217  prec->alst = prec->val;
218  prec->lalm = prec->val;
219  prec->povl = prec->oval;
220  return 0;
221 }
222 
223 static long process(struct dbCommon *pcommon)
224 {
225  struct calcoutRecord *prec = (struct calcoutRecord *)pcommon;
226  rpvtStruct *prpvt = prec->rpvt;
227  int doOutput;
228 
229  if (!prec->pact) {
230  prec->pact = TRUE;
231  /* if some links are CA, check connections */
232  if (prpvt->caLinkStat != NO_CA_LINKS) {
233  checkLinks(prec);
234  }
235  if (fetch_values(prec) == 0) {
236  if (calcPerform(&prec->a, &prec->val, prec->rpcl)) {
237  recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
238  } else {
239  prec->udf = isnan(prec->val);
240  }
241  }
242  checkAlarms(prec);
243  /* check for output link execution */
244  switch (prec->oopt) {
245  case calcoutOOPT_Every_Time:
246  doOutput = 1;
247  break;
248  case calcoutOOPT_On_Change:
249  doOutput = ! (fabs(prec->pval - prec->val) <= prec->mdel);
250  break;
251  case calcoutOOPT_Transition_To_Zero:
252  doOutput = ((prec->pval != 0.0) && (prec->val == 0.0));
253  break;
254  case calcoutOOPT_Transition_To_Non_zero:
255  doOutput = ((prec->pval == 0.0) && (prec->val != 0.0));
256  break;
257  case calcoutOOPT_When_Zero:
258  doOutput = (prec->val == 0.0);
259  break;
260  case calcoutOOPT_When_Non_zero:
261  doOutput = (prec->val != 0.0);
262  break;
263  default:
264  doOutput = 0;
265  break;
266  }
267  prec->pval = prec->val;
268  if (doOutput) {
269  if (prec->odly > 0.0) {
270  prec->dlya = 1;
271  recGblGetTimeStamp(prec);
272  db_post_events(prec, &prec->dlya, DBE_VALUE);
273  callbackRequestProcessCallbackDelayed(&prpvt->doOutCb,
274  prec->prio, prec, (double)prec->odly);
275  return 0;
276  } else {
277  prec->pact = FALSE;
278  execOutput(prec);
279  if (prec->pact) return 0;
280  prec->pact = TRUE;
281  }
282  }
283  recGblGetTimeStamp(prec);
284  } else { /* pact == TRUE */
285  if (prec->dlya) {
286  prec->dlya = 0;
287  recGblGetTimeStamp(prec);
288  db_post_events(prec, &prec->dlya, DBE_VALUE);
289  /* Make pact FALSE for asynchronous device support*/
290  prec->pact = FALSE;
291  execOutput(prec);
292  if (prec->pact) return 0;
293  prec->pact = TRUE;
294  } else {/*Device Support is asynchronous*/
295  writeValue(prec);
296  recGblGetTimeStamp(prec);
297  }
298  }
299  monitor(prec);
300  recGblFwdLink(prec);
301  prec->pact = FALSE;
302  return 0;
303 }
304 
305 static long special(DBADDR *paddr, int after)
306 {
307  calcoutRecord *prec = (calcoutRecord *)paddr->precord;
308  rpvtStruct *prpvt = prec->rpvt;
309  short error_number;
310  int fieldIndex = dbGetFieldIndex(paddr);
311  int lnkIndex;
312  DBLINK *plink;
313  double *pvalue;
314  epicsEnum16 *plinkValid;
315 
316  if (!after) return 0;
317  switch(fieldIndex) {
318  case(calcoutRecordCALC):
319  prec->clcv = postfix(prec->calc, prec->rpcl, &error_number);
320  if (prec->clcv){
321  recGblRecordError(S_db_badField, (void *)prec,
322  "calcout: special(): Illegal CALC field");
323  errlogPrintf("%s.CALC: %s in expression \"%s\"\n",
324  prec->name, calcErrorStr(error_number), prec->calc);
325  }
326  db_post_events(prec, &prec->clcv, DBE_VALUE);
327  return 0;
328 
329  case(calcoutRecordOCAL):
330  prec->oclv = postfix(prec->ocal, prec->orpc, &error_number);
331  if (prec->dopt == calcoutDOPT_Use_OVAL && prec->oclv){
332  recGblRecordError(S_db_badField, (void *)prec,
333  "calcout: special(): Illegal OCAL field");
334  errlogPrintf("%s.OCAL: %s in expression \"%s\"\n",
335  prec->name, calcErrorStr(error_number), prec->ocal);
336  }
337  db_post_events(prec, &prec->oclv, DBE_VALUE);
338  return 0;
339  case(calcoutRecordINPA):
340  case(calcoutRecordINPB):
341  case(calcoutRecordINPC):
342  case(calcoutRecordINPD):
343  case(calcoutRecordINPE):
344  case(calcoutRecordINPF):
345  case(calcoutRecordINPG):
346  case(calcoutRecordINPH):
347  case(calcoutRecordINPI):
348  case(calcoutRecordINPJ):
349  case(calcoutRecordINPK):
350  case(calcoutRecordINPL):
351  case(calcoutRecordOUT):
352  lnkIndex = fieldIndex - calcoutRecordINPA;
353  plink = &prec->inpa + lnkIndex;
354  pvalue = &prec->a + lnkIndex;
355  plinkValid = &prec->inav + lnkIndex;
356 
357  if (fieldIndex != calcoutRecordOUT)
358  recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
359 
360  if (dbLinkIsConstant(plink)) {
361  db_post_events(prec, pvalue, DBE_VALUE);
362  *plinkValid = calcoutINAV_CON;
363  } else if (dbLinkIsVolatile(plink)) {
364  int conn = dbIsLinkConnected(plink);
365 
366  if (conn)
367  *plinkValid = calcoutINAV_EXT;
368  else {
369  /* Monitor for connection */
370  *plinkValid = calcoutINAV_EXT_NC;
371  /* DO_CALLBACK, if not already scheduled */
372  if (!prpvt->cbScheduled) {
373  callbackRequestDelayed(&prpvt->checkLinkCb, .5);
374  prpvt->cbScheduled = 1;
375  prpvt->caLinkStat = CA_LINKS_NOT_OK;
376  }
377  }
378  }
379  else {
380  /* PV must reside on this ioc */
381  *plinkValid = calcoutINAV_LOC;
382 
383  if (!dbIsLinkConnected(plink)) {
384  errlogPrintf("calcout: %s.INP%c in no-vo diso state\n",
385  prec->name, lnkIndex);
386  }
387  }
388  db_post_events(prec, plinkValid, DBE_VALUE);
389  return 0;
390  case(calcoutRecordOEVT):
391  prec->epvt = eventNameToHandle(prec->oevt);
392  return 0;
393  default:
394  recGblDbaddrError(S_db_badChoice, paddr, "calc: special");
395  return(S_db_badChoice);
396  }
397 }
398 
399 #define indexof(field) calcoutRecord##field
400 
401 static long get_linkNumber(int fieldIndex) {
402  if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L))
403  return fieldIndex - indexof(A);
404  if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL))
405  return fieldIndex - indexof(LA);
406  return -1;
407 }
408 
409 static long get_units(DBADDR *paddr, char *units)
410 {
411  calcoutRecord *prec = (calcoutRecord *)paddr->precord;
412  int fieldIndex = dbGetFieldIndex(paddr);
413  int linkNumber;
414 
415  if(fieldIndex == indexof(ODLY)) {
416  strcpy(units, "s");
417  return 0;
418  }
419 
420  if(paddr->pfldDes->field_type == DBF_DOUBLE) {
421  linkNumber = get_linkNumber(dbGetFieldIndex(paddr));
422  if (linkNumber >= 0)
423  dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE);
424  else
425  strncpy(units,prec->egu,DB_UNITS_SIZE);
426  }
427  return 0;
428 }
429 
430 static long get_precision(const DBADDR *paddr, long *pprecision)
431 {
432  calcoutRecord *prec = (calcoutRecord *)paddr->precord;
433  int fieldIndex = dbGetFieldIndex(paddr);
434  int linkNumber;
435 
436  if (fieldIndex == indexof(ODLY)) {
437  *pprecision = calcoutODLYprecision;
438  return 0;
439  }
440 
441  *pprecision = prec->prec;
442  if (fieldIndex == indexof(VAL))
443  return 0;
444 
445  linkNumber = get_linkNumber(fieldIndex);
446  if (linkNumber >= 0) {
447  short precision;
448 
449  if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0)
450  *pprecision = precision;
451  } else
452  recGblGetPrec(paddr, pprecision);
453  return 0;
454 }
455 
456 static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
457 {
458  calcoutRecord *prec = (calcoutRecord *)paddr->precord;
459  int fieldIndex = dbGetFieldIndex(paddr);
460  int linkNumber;
461 
462  switch (fieldIndex) {
463  case indexof(VAL):
464  case indexof(HIHI):
465  case indexof(HIGH):
466  case indexof(LOW):
467  case indexof(LOLO):
468  case indexof(LALM):
469  case indexof(ALST):
470  case indexof(MLST):
471  pgd->lower_disp_limit = prec->lopr;
472  pgd->upper_disp_limit = prec->hopr;
473  break;
474  case indexof(ODLY):
475  recGblGetGraphicDouble(paddr,pgd);
476  pgd->lower_disp_limit = 0.0;
477  break;
478  default:
479  linkNumber = get_linkNumber(fieldIndex);
480  if (linkNumber >= 0) {
481  dbGetGraphicLimits(&prec->inpa + linkNumber,
482  &pgd->lower_disp_limit,
483  &pgd->upper_disp_limit);
484  } else
485  recGblGetGraphicDouble(paddr,pgd);
486  }
487  return 0;
488 }
489 
490 static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
491 {
492  calcoutRecord *prec = (calcoutRecord *)paddr->precord;
493 
494  switch (dbGetFieldIndex(paddr)) {
495  case indexof(VAL):
496  case indexof(HIHI):
497  case indexof(HIGH):
498  case indexof(LOW):
499  case indexof(LOLO):
500  case indexof(LALM):
501  case indexof(ALST):
502  case indexof(MLST):
503  pcd->lower_ctrl_limit = prec->lopr;
504  pcd->upper_ctrl_limit = prec->hopr;
505  break;
506  case indexof(ODLY):
507  pcd->lower_ctrl_limit = 0.0;
508  pcd->upper_ctrl_limit = calcoutODLYlimit;
509  break;
510  default:
511  recGblGetControlDouble(paddr,pcd);
512  }
513  return 0;
514 }
515 
516 static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
517 {
518  calcoutRecord *prec = (calcoutRecord *)paddr->precord;
519  int fieldIndex = dbGetFieldIndex(paddr);
520  int linkNumber;
521 
522  if (fieldIndex == indexof(VAL)) {
523  pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
524  pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
525  pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
526  pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
527  } else {
528  linkNumber = get_linkNumber(fieldIndex);
529  if (linkNumber >= 0) {
530  dbGetAlarmLimits(&prec->inpa + linkNumber,
531  &pad->lower_alarm_limit,
532  &pad->lower_warning_limit,
533  &pad->upper_warning_limit,
534  &pad->upper_alarm_limit);
535  } else
536  recGblGetAlarmDouble(paddr, pad);
537  }
538  return 0;
539 }
540 
541 static void checkAlarms(calcoutRecord *prec)
542 {
543  double val, hyst, lalm;
544  double alev;
545  epicsEnum16 asev;
546 
547  if (prec->udf) {
548  recGblSetSevr(prec, UDF_ALARM, prec->udfs);
549  return;
550  }
551 
552  val = prec->val;
553  hyst = prec->hyst;
554  lalm = prec->lalm;
555 
556  /* alarm condition hihi */
557  asev = prec->hhsv;
558  alev = prec->hihi;
559  if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
560  if (recGblSetSevr(prec, HIHI_ALARM, asev))
561  prec->lalm = alev;
562  return;
563  }
564 
565  /* alarm condition lolo */
566  asev = prec->llsv;
567  alev = prec->lolo;
568  if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
569  if (recGblSetSevr(prec, LOLO_ALARM, asev))
570  prec->lalm = alev;
571  return;
572  }
573 
574  /* alarm condition high */
575  asev = prec->hsv;
576  alev = prec->high;
577  if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
578  if (recGblSetSevr(prec, HIGH_ALARM, asev))
579  prec->lalm = alev;
580  return;
581  }
582 
583  /* alarm condition low */
584  asev = prec->lsv;
585  alev = prec->low;
586  if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
587  if (recGblSetSevr(prec, LOW_ALARM, asev))
588  prec->lalm = alev;
589  return;
590  }
591 
592  /* we get here only if val is out of alarm by at least hyst */
593  prec->lalm = val;
594  return;
595 }
596 
597 static void execOutput(calcoutRecord *prec)
598 {
599  /* Determine output data */
600  switch(prec->dopt) {
601  case calcoutDOPT_Use_VAL:
602  prec->oval = prec->val;
603  break;
604  case calcoutDOPT_Use_OVAL:
605  if (calcPerform(&prec->a, &prec->oval, prec->orpc)) {
606  recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
607  } else {
608  prec->udf = isnan(prec->oval);
609  }
610  break;
611  }
612  if (prec->udf){
613  recGblSetSevr(prec, UDF_ALARM, prec->udfs);
614  }
615 
616  /* Check to see what to do if INVALID */
617  if (prec->nsev < INVALID_ALARM ) {
618  /* Output the value */
619  writeValue(prec);
620  /* post output event if set */
621  if (prec->epvt) postEvent(prec->epvt);
622  } else switch (prec->ivoa) {
623  case menuIvoaContinue_normally:
624  writeValue(prec);
625  /* post output event if set */
626  if (prec->epvt) postEvent(prec->epvt);
627  break;
628  case menuIvoaDon_t_drive_outputs:
629  break;
630  case menuIvoaSet_output_to_IVOV:
631  prec->oval = prec->ivov;
632  writeValue(prec);
633  /* post output event if set */
634  if (prec->epvt) postEvent(prec->epvt);
635  break;
636  default:
637  recGblRecordError(S_db_badField, (void *)prec,
638  "calcout:process Illegal IVOA field");
639  }
640 }
641 
642 static void monitor(calcoutRecord *prec)
643 {
644  unsigned monitor_mask;
645  double *pnew;
646  double *pprev;
647  int i;
648 
649  monitor_mask = recGblResetAlarms(prec);
650 
651  /* check for value change */
652  recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
653 
654  /* check for archive change */
655  recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
656 
657  /* send out monitors connected to the value field */
658  if (monitor_mask){
659  db_post_events(prec, &prec->val, monitor_mask);
660  }
661 
662  /* check all input fields for changes*/
663  for (i = 0, pnew = &prec->a, pprev = &prec->la; i<CALCPERFORM_NARGS;
664  i++, pnew++, pprev++) {
665  if ((*pnew != *pprev) || (monitor_mask&DBE_ALARM)) {
666  db_post_events(prec, pnew, monitor_mask|DBE_VALUE|DBE_LOG);
667  *pprev = *pnew;
668  }
669  }
670  /* Check OVAL field */
671  if (prec->povl != prec->oval) {
672  db_post_events(prec, &prec->oval, monitor_mask|DBE_VALUE|DBE_LOG);
673  prec->povl = prec->oval;
674  }
675  return;
676 }
677 
678 static int fetch_values(calcoutRecord *prec)
679 {
680  DBLINK *plink; /* structure of the link field */
681  double *pvalue;
682  long status = 0;
683  int i;
684 
685  for (i = 0, plink = &prec->inpa, pvalue = &prec->a; i<CALCPERFORM_NARGS;
686  i++, plink++, pvalue++) {
687  int newStatus;
688 
689  newStatus = dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0);
690  if (!status) status = newStatus;
691  }
692  return(status);
693 }
694 
695 static void checkLinksCallback(epicsCallback *arg)
696 {
697 
698  calcoutRecord *prec;
699  rpvtStruct *prpvt;
700 
701  callbackGetUser(prec, arg);
702  prpvt = prec->rpvt;
703 
704  dbScanLock((dbCommon *)prec);
705  prpvt->cbScheduled = 0;
706  checkLinks(prec);
707  dbScanUnlock((dbCommon *)prec);
708 
709 }
710 
711 static void checkLinks(calcoutRecord *prec)
712 {
713 
714  DBLINK *plink;
715  rpvtStruct *prpvt = prec->rpvt;
716  int i;
717  int stat;
718  int caLink = 0;
719  int caLinkNc = 0;
720  epicsEnum16 *plinkValid;
721 
722  if (calcoutRecDebug) printf("checkLinks() for %p\n", prec);
723 
724  plink = &prec->inpa;
725  plinkValid = &prec->inav;
726 
727  for (i = 0; i<CALCPERFORM_NARGS+1; i++, plink++, plinkValid++) {
728  if (dbLinkIsVolatile(plink)) {
729  caLink = 1;
730  stat = dbIsLinkConnected(plink);
731  if (!stat && (*plinkValid == calcoutINAV_EXT_NC)) {
732  caLinkNc = 1;
733  }
734  else if (!stat && (*plinkValid == calcoutINAV_EXT)) {
735  *plinkValid = calcoutINAV_EXT_NC;
736  db_post_events(prec, plinkValid, DBE_VALUE);
737  caLinkNc = 1;
738  }
739  else if (stat && (*plinkValid == calcoutINAV_EXT_NC)) {
740  *plinkValid = calcoutINAV_EXT;
741  db_post_events(prec, plinkValid, DBE_VALUE);
742  }
743  }
744  }
745  if (caLinkNc)
746  prpvt->caLinkStat = CA_LINKS_NOT_OK;
747  else if (caLink)
748  prpvt->caLinkStat = CA_LINKS_ALL_OK;
749  else
750  prpvt->caLinkStat = NO_CA_LINKS;
751 
752  if (!prpvt->cbScheduled && caLinkNc) {
753  /* Schedule another epicsCallback */
754  prpvt->cbScheduled = 1;
755  callbackRequestDelayed(&prpvt->checkLinkCb, .5);
756  }
757 }
758 
759 static long writeValue(calcoutRecord *prec)
760 {
761  calcoutdset *pcalcoutDSET = (calcoutdset *)prec->dset;
762 
763 
764  if (!pcalcoutDSET || !pcalcoutDSET->write) {
765  errlogPrintf("%s DSET write does not exist\n", prec->name);
766  recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
767  prec->pact = TRUE;
768  return(-1);
769  }
770  return pcalcoutDSET->write(prec);
771 }
#define HIHI_ALARM
Definition: alarm.h:94
#define RSETNUMBER
Definition: recSup.h:92
LIBCOM_API const char * calcErrorStr(short error)
Convert an error code to a string.
Definition: postfix.c:493
#define report
Definition: calcoutRecord.c:48
#define FALSE
Definition: dbDefs.h:32
#define put_enum_str
Definition: calcoutRecord.c:61
unsigned * LA
Definition: lalr.c:22
pvd::Status status
int i
Definition: scan.c:967
short caLinkStat
#define init_record
#define S_dev_missingSup
Definition: devSup.h:170
#define printf
Definition: epicsStdio.h:41
#define get_array_info
Definition: calcoutRecord.c:55
#define SOFT_ALARM
Definition: alarm.h:106
#define NULL
Definition: catime.c:38
#define DBE_ALARM
Definition: caeventmask.h:41
#define indexof(field)
#define CALCPERFORM_NARGS
Number of input arguments to a calc expression (A-L)
Definition: postfix.h:25
Miscellaneous macro definitions.
#define DBE_ARCHIVE
Definition: caeventmask.h:39
#define put_array_info
Definition: calcoutRecord.c:56
#define get_control_double
Definition: biRecord.c:58
short cbScheduled
#define cvt_dbaddr
Definition: calcoutRecord.c:54
#define DBE_VALUE
Definition: caeventmask.h:38
#define HIGH_ALARM
Definition: alarm.h:95
LIBCOM_API long calcPerform(double *parg, double *presult, const char *pinst)
Run the calculation engine.
Definition: calcPerform.c:45
Device support routines.
#define CALC_ALARM
Definition: alarm.h:103
#define isnan(x)
Definition: epicsMath.h:21
epicsCallback checkLinkCb
#define DBE_LOG
Definition: caeventmask.h:40
#define get_units
Definition: biRecord.c:52
#define DBR_DOUBLE
Definition: db_access.h:76
int calcoutRecDebug
double calcoutODLYlimit
Definition: calcoutRecord.c:90
struct rpvtStruct rpvtStruct
epicsCallback doOutCb
#define initialize
Definition: calcoutRecord.c:49
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 get_enum_str
Definition: calcoutRecord.c:59
float epicsNAN
Definition: epicsMath.cpp:35
epicsUInt16 epicsEnum16
Definition: epicsTypes.h:47
epicsExportAddress(rset, calcoutRSET)
rset calcoutRSET
Definition: calcoutRecord.c:66
#define get_precision
Definition: biRecord.c:53
#define get_enum_strs
Definition: calcoutRecord.c:60
#define TRUE
Definition: dbDefs.h:27
#define CA_LINKS_NOT_OK
if(yy_init)
Definition: scan.c:972
Definition: recSup.h:67
#define get_value
Definition: calcoutRecord.c:53
Routines for code that can&#39;t continue or return after an error.
LIBCOM_API long postfix(const char *psrc, char *pout, short *perror)
Compile an infix expression into postfix byte-code.
Definition: postfix.c:209
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
int calcoutODLYprecision
Definition: calcoutRecord.c:88
#define special
Definition: dfanoutRecord.c:50
#define S_dev_noDSET
Definition: devSup.h:169
#define NO_CA_LINKS
#define get_alarm_double
Definition: aaiRecord.c:69
#define UDF_ALARM
Definition: alarm.h:108
#define CA_LINKS_ALL_OK
Exporting IOC objects.