31 #include "epicsMath.h" 42 #define GEN_SIZE_OFFSET 43 #include "calcoutRecord.h" 44 #undef GEN_SIZE_OFFSET 49 #define initialize NULL 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 59 #define get_enum_str NULL 60 #define get_enum_strs NULL 61 #define put_enum_str NULL 105 #define NO_CA_LINKS 0 106 #define CA_LINKS_ALL_OK 1 107 #define CA_LINKS_NOT_OK 2 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);
127 static long init_record(
struct dbCommon *pcommon,
int pass)
129 struct calcoutRecord *
prec = (
struct calcoutRecord *)pcommon;
135 calcoutdset *pcalcoutDSET;
143 if (!(pcalcoutDSET = (calcoutdset *)prec->dset)) {
144 recGblRecordError(
S_dev_noDSET, (
void *)prec,
"calcout:init_record");
149 if ((pcalcoutDSET->common.number < 5) || (pcalcoutDSET->write ==
NULL)) {
157 plinkValid = &prec->inav;
162 recGblInitConstantLink(plink,
DBF_DOUBLE, pvalue);
165 if (dbLinkIsConstant(plink)) {
166 *plinkValid = calcoutINAV_CON;
168 else if (dbLinkIsVolatile(plink)) {
169 int conn = dbIsLinkConnected(plink);
172 *plinkValid = calcoutINAV_EXT;
175 *plinkValid = calcoutINAV_EXT_NC;
181 *plinkValid = calcoutINAV_LOC;
183 if (!dbIsLinkConnected(plink)) {
184 errlogPrintf(
"calcout: %s.INP%c in no-vo disco state\n",
190 prec->clcv =
postfix(prec->calc, prec->rpcl, &error_number);
192 recGblRecordError(S_db_badField, (
void *)prec,
193 "calcout: init_record: Illegal CALC field");
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");
207 callbackSetCallback(checkLinksCallback, &prpvt->
checkLinkCb);
212 prec->epvt = eventNameToHandle(prec->oevt);
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;
223 static long process(
struct dbCommon *pcommon)
225 struct calcoutRecord *
prec = (
struct calcoutRecord *)pcommon;
235 if (fetch_values(prec) == 0) {
236 if (
calcPerform(&prec->a, &prec->val, prec->rpcl)) {
239 prec->udf =
isnan(prec->val);
244 switch (prec->oopt) {
245 case calcoutOOPT_Every_Time:
248 case calcoutOOPT_On_Change:
249 doOutput = ! (fabs(prec->pval - prec->val) <= prec->mdel);
251 case calcoutOOPT_Transition_To_Zero:
252 doOutput = ((prec->pval != 0.0) && (prec->val == 0.0));
254 case calcoutOOPT_Transition_To_Non_zero:
255 doOutput = ((prec->pval == 0.0) && (prec->val != 0.0));
257 case calcoutOOPT_When_Zero:
258 doOutput = (prec->val == 0.0);
260 case calcoutOOPT_When_Non_zero:
261 doOutput = (prec->val != 0.0);
267 prec->pval = prec->val;
269 if (prec->odly > 0.0) {
271 recGblGetTimeStamp(prec);
272 db_post_events(prec, &prec->dlya,
DBE_VALUE);
273 callbackRequestProcessCallbackDelayed(&prpvt->
doOutCb,
274 prec->prio, prec, (
double)prec->odly);
279 if (prec->pact)
return 0;
283 recGblGetTimeStamp(prec);
287 recGblGetTimeStamp(prec);
288 db_post_events(prec, &prec->dlya,
DBE_VALUE);
292 if (prec->pact)
return 0;
296 recGblGetTimeStamp(prec);
305 static long special(DBADDR *paddr,
int after)
307 calcoutRecord *
prec = (calcoutRecord *)paddr->precord;
310 int fieldIndex = dbGetFieldIndex(paddr);
316 if (!after)
return 0;
318 case(calcoutRecordCALC):
319 prec->clcv =
postfix(prec->calc, prec->rpcl, &error_number);
321 recGblRecordError(S_db_badField, (
void *)prec,
322 "calcout: special(): Illegal CALC field");
326 db_post_events(prec, &prec->clcv,
DBE_VALUE);
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");
337 db_post_events(prec, &prec->oclv,
DBE_VALUE);
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;
357 if (fieldIndex != calcoutRecordOUT)
358 recGblInitConstantLink(plink,
DBF_DOUBLE, pvalue);
360 if (dbLinkIsConstant(plink)) {
362 *plinkValid = calcoutINAV_CON;
363 }
else if (dbLinkIsVolatile(plink)) {
364 int conn = dbIsLinkConnected(plink);
367 *plinkValid = calcoutINAV_EXT;
370 *plinkValid = calcoutINAV_EXT_NC;
372 if (!prpvt->cbScheduled) {
373 callbackRequestDelayed(&prpvt->checkLinkCb, .5);
374 prpvt->cbScheduled = 1;
381 *plinkValid = calcoutINAV_LOC;
383 if (!dbIsLinkConnected(plink)) {
385 prec->name, lnkIndex);
388 db_post_events(prec, plinkValid,
DBE_VALUE);
390 case(calcoutRecordOEVT):
391 prec->epvt = eventNameToHandle(prec->oevt);
394 recGblDbaddrError(S_db_badChoice, paddr,
"calc: special");
395 return(S_db_badChoice);
399 #define indexof(field) calcoutRecord##field 401 static long get_linkNumber(
int fieldIndex) {
403 return fieldIndex -
indexof(A);
409 static long get_units(DBADDR *paddr,
char *units)
411 calcoutRecord *
prec = (calcoutRecord *)paddr->precord;
412 int fieldIndex = dbGetFieldIndex(paddr);
415 if(fieldIndex ==
indexof(ODLY)) {
420 if(paddr->pfldDes->field_type ==
DBF_DOUBLE) {
421 linkNumber = get_linkNumber(dbGetFieldIndex(paddr));
423 dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE);
425 strncpy(units,prec->egu,DB_UNITS_SIZE);
430 static long get_precision(
const DBADDR *paddr,
long *pprecision)
432 calcoutRecord *
prec = (calcoutRecord *)paddr->precord;
433 int fieldIndex = dbGetFieldIndex(paddr);
436 if (fieldIndex ==
indexof(ODLY)) {
441 *pprecision = prec->prec;
442 if (fieldIndex ==
indexof(VAL))
445 linkNumber = get_linkNumber(fieldIndex);
446 if (linkNumber >= 0) {
449 if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0)
450 *pprecision = precision;
452 recGblGetPrec(paddr, pprecision);
458 calcoutRecord *
prec = (calcoutRecord *)paddr->precord;
459 int fieldIndex = dbGetFieldIndex(paddr);
462 switch (fieldIndex) {
471 pgd->lower_disp_limit = prec->lopr;
472 pgd->upper_disp_limit = prec->hopr;
475 recGblGetGraphicDouble(paddr,pgd);
476 pgd->lower_disp_limit = 0.0;
479 linkNumber = get_linkNumber(fieldIndex);
480 if (linkNumber >= 0) {
481 dbGetGraphicLimits(&prec->inpa + linkNumber,
482 &pgd->lower_disp_limit,
483 &pgd->upper_disp_limit);
485 recGblGetGraphicDouble(paddr,pgd);
492 calcoutRecord *
prec = (calcoutRecord *)paddr->precord;
494 switch (dbGetFieldIndex(paddr)) {
503 pcd->lower_ctrl_limit = prec->lopr;
504 pcd->upper_ctrl_limit = prec->hopr;
507 pcd->lower_ctrl_limit = 0.0;
511 recGblGetControlDouble(paddr,pcd);
518 calcoutRecord *
prec = (calcoutRecord *)paddr->precord;
519 int fieldIndex = dbGetFieldIndex(paddr);
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;
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);
536 recGblGetAlarmDouble(paddr, pad);
541 static void checkAlarms(calcoutRecord *
prec)
543 double val, hyst, lalm;
548 recGblSetSevr(prec,
UDF_ALARM, prec->udfs);
559 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
568 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
577 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
586 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
587 if (recGblSetSevr(prec,
LOW_ALARM, asev))
597 static void execOutput(calcoutRecord *
prec)
601 case calcoutDOPT_Use_VAL:
602 prec->oval = prec->val;
604 case calcoutDOPT_Use_OVAL:
605 if (
calcPerform(&prec->a, &prec->oval, prec->orpc)) {
608 prec->udf =
isnan(prec->oval);
613 recGblSetSevr(prec,
UDF_ALARM, prec->udfs);
621 if (prec->epvt) postEvent(prec->epvt);
622 }
else switch (prec->ivoa) {
623 case menuIvoaContinue_normally:
626 if (prec->epvt) postEvent(prec->epvt);
628 case menuIvoaDon_t_drive_outputs:
630 case menuIvoaSet_output_to_IVOV:
631 prec->oval = prec->ivov;
634 if (prec->epvt) postEvent(prec->epvt);
637 recGblRecordError(S_db_badField, (
void *)prec,
638 "calcout:process Illegal IVOA field");
642 static void monitor(calcoutRecord *
prec)
644 unsigned monitor_mask;
649 monitor_mask = recGblResetAlarms(prec);
652 recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask,
DBE_VALUE);
655 recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask,
DBE_ARCHIVE);
659 db_post_events(prec, &prec->val, monitor_mask);
664 i++, pnew++, pprev++) {
665 if ((*pnew != *pprev) || (monitor_mask&
DBE_ALARM)) {
671 if (prec->povl != prec->oval) {
673 prec->povl = prec->oval;
678 static int fetch_values(calcoutRecord *
prec)
686 i++, plink++, pvalue++) {
689 newStatus = dbGetLink(plink,
DBR_DOUBLE, pvalue, 0, 0);
690 if (!status) status = newStatus;
695 static void checkLinksCallback(epicsCallback *arg)
701 callbackGetUser(prec, arg);
704 dbScanLock((dbCommon *)prec);
707 dbScanUnlock((dbCommon *)prec);
711 static void checkLinks(calcoutRecord *
prec)
725 plinkValid = &prec->inav;
728 if (dbLinkIsVolatile(plink)) {
730 stat = dbIsLinkConnected(plink);
731 if (!stat && (*plinkValid == calcoutINAV_EXT_NC)) {
734 else if (!stat && (*plinkValid == calcoutINAV_EXT)) {
735 *plinkValid = calcoutINAV_EXT_NC;
736 db_post_events(prec, plinkValid,
DBE_VALUE);
739 else if (stat && (*plinkValid == calcoutINAV_EXT_NC)) {
740 *plinkValid = calcoutINAV_EXT;
741 db_post_events(prec, plinkValid,
DBE_VALUE);
759 static long writeValue(calcoutRecord *
prec)
761 calcoutdset *pcalcoutDSET = (calcoutdset *)prec->dset;
764 if (!pcalcoutDSET || !pcalcoutDSET->write) {
765 errlogPrintf(
"%s DSET write does not exist\n", prec->name);
770 return pcalcoutDSET->write(prec);
LIBCOM_API const char * calcErrorStr(short error)
Convert an error code to a string.
#define CALCPERFORM_NARGS
Number of input arguments to a calc expression (A-L)
Miscellaneous macro definitions.
#define get_control_double
epicsCallback checkLinkCb
struct rpvtStruct rpvtStruct
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
int errlogPrintf(const char *pFormat,...)
epicsExportAddress(rset, calcoutRSET)
Routines for code that can'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.
#define get_graphic_double