From 38f17b720effded591258bf548f3edd1ca20d723 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 24 Apr 2008 16:30:37 +0000 Subject: [PATCH] Allow subroutine changes at runtime, from Kazimierz Gofron (Codeathon) Also did some major cleanup. --- src/rec/subRecord.c | 568 ++++++++++++++++++++++-------------------- src/rec/subRecord.dbd | 11 +- 2 files changed, 304 insertions(+), 275 deletions(-) diff --git a/src/rec/subRecord.c b/src/rec/subRecord.c index c52781050..ba995f6f6 100644 --- a/src/rec/subRecord.c +++ b/src/rec/subRecord.c @@ -1,19 +1,17 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* recSub.c */ -/* base/src/rec $Id$ */ -/* recSub.c - Record Support Routines for Subroutine records */ +/* $Id$ */ + +/* Record Support Routines for Subroutine records */ /* * Original Author: Bob Dalesio - * Current Author: Marty Kraimer * Date: 01-25-90 */ @@ -28,7 +26,6 @@ #include "epicsMath.h" #include "registryFunction.h" #include "alarm.h" -#include "callback.h" #include "cantProceed.h" #include "dbAccess.h" #include "epicsPrint.h" @@ -37,6 +34,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #define GEN_SIZE_OFFSET #include "subRecord.h" #undef GEN_SIZE_OFFSET @@ -45,340 +43,370 @@ /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL -static long init_record(); -static long process(); -#define special NULL +static long init_record(subRecord *, int); +static long process(subRecord *); +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL #define put_array_info NULL -static long get_units(); -static long get_precision(); +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); #define get_enum_str NULL #define get_enum_strs NULL #define put_enum_str NULL -static long get_graphic_double(); -static long get_control_double(); -static long get_alarm_double(); +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); rset subRSET={ - RSETNUMBER, - report, - initialize, - init_record, - process, - special, - get_value, - cvt_dbaddr, - get_array_info, - put_array_info, - get_units, - get_precision, - get_enum_str, - get_enum_strs, - put_enum_str, - get_graphic_double, - get_control_double, - get_alarm_double + RSETNUMBER, + report, + initialize, + init_record, + process, + special, + get_value, + cvt_dbaddr, + get_array_info, + put_array_info, + get_units, + get_precision, + get_enum_str, + get_enum_strs, + put_enum_str, + get_graphic_double, + get_control_double, + get_alarm_double }; -epicsExportAddress(rset,subRSET); +epicsExportAddress(rset, subRSET); -static void checkAlarms(); -static long do_sub(); -static long fetch_values(); -static void monitor(); +static void checkAlarms(subRecord *); +static long do_sub(subRecord *); +static long fetch_values(subRecord *); +static void monitor(subRecord *); #define INP_ARG_MAX 12 -typedef long (*SUBFUNCPTR)(); -static long init_record(psub,pass) - struct subRecord *psub; - int pass; +static long init_record(subRecord *prec, int pass) { - SUBFUNCPTR psubroutine; - long status = 0; + SUBFUNCPTR psubroutine; + long status = 0; struct link *plink; int i; double *pvalue; if (pass==0) return(0); - plink = &psub->inpa; - pvalue = &psub->a; - for(i=0; itype==CONSTANT) { - recGblInitConstantLink(plink,DBF_DOUBLE,pvalue); + plink = &prec->inpa; + pvalue = &prec->a; + for (i = 0; i < INP_ARG_MAX; i++, plink++, pvalue++) { + if (plink->type == CONSTANT) { + recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); } } - if(strlen(psub->inam)!=0) { + if (prec->inam[0]) { /* convert the initialization subroutine name */ - psubroutine = (SUBFUNCPTR)registryFunctionFind(psub->inam); - if(psubroutine==0) { - recGblRecordError(S_db_BadSub,(void *)psub,"recSub(init_record)"); - return(S_db_BadSub); + psubroutine = (SUBFUNCPTR)registryFunctionFind(prec->inam); + if (psubroutine == 0) { + recGblRecordError(S_db_BadSub, (void *)prec, "recSub(init_record)"); + return S_db_BadSub; } /* invoke the initialization subroutine */ - status = (*psubroutine)(psub,process); + status = (*psubroutine)(prec); } - if(strlen(psub->snam)==0) { - epicsPrintf("%s snam not specified\n",psub->name); - psub->pact = TRUE; - return(0); + if (prec->snam[0] == 0) { + epicsPrintf("%s.SNAM is empty\n", prec->name); + prec->pact = TRUE; + return 0; } - psub->sadr = (void *)registryFunctionFind(psub->snam); - if(psub->sadr==0) { - recGblRecordError(S_db_BadSub,(void *)psub,"recSub(init_record)"); - return(S_db_BadSub); + prec->sadr = (void *)registryFunctionFind(prec->snam); + if (prec->sadr == NULL) { + recGblRecordError(S_db_BadSub, (void *)prec, "recSub(init_record)"); + return S_db_BadSub; } - return(0); + return 0; } -static long process(psub) - struct subRecord *psub; +static long process(subRecord *prec) { - long status=0; - unsigned char pact=psub->pact; + long status = 0; + int pact = prec->pact; - if(!psub->pact || !psub->sadr){ - psub->pact = TRUE; - status = fetch_values(psub); - psub->pact = FALSE; - } - if(status==0) status = do_sub(psub); - /* check if device support set pact */ - if ( !pact && psub->pact ) return(0); - /*previously we had different rules. Lets support old subs*/ - psub->pact = TRUE; - if(status==1) return(0); - recGblGetTimeStamp(psub); - /* check for alarms */ - checkAlarms(psub); - /* check event list */ - monitor(psub); - /* process the forward scan link record */ - recGblFwdLink(psub); - psub->pact = FALSE; - return(0); + if (!pact) { + prec->pact = TRUE; + status = fetch_values(prec); + prec->pact = FALSE; + } + if (status == 0) status = do_sub(prec); + + /* Is subroutine asynchronous? */ + if (!pact && prec->pact) return 0; + prec->pact = TRUE; + + /* Old async signal, deprecated */ + if (status == 1) return 0; + + recGblGetTimeStamp(prec); + + /* check for alarms */ + checkAlarms(prec); + + /* publish changes */ + monitor(prec); + + recGblFwdLink(prec); + prec->pact = FALSE; + + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + subRecord *prec = (subRecord *)paddr->precord; + + if (!after) { + if (prec->snam[0] == 0) + prec->pact = FALSE; + return 0; + } + + if (prec->snam[0] == 0) { + epicsPrintf("%s.SNAM is empty\n", prec->name); + prec->pact = TRUE; + return 0; + } + + prec->sadr = (SUBFUNCPTR)registryFunctionFind(prec->snam); + if (prec->sadr) return 0; + + recGblRecordError(S_db_BadSub, (void *)prec, + "subRecord(special) registryFunctionFind failed"); + return S_db_BadSub; } -static long get_units(paddr,units) - struct dbAddr *paddr; - char *units; +static long get_units(DBADDR *paddr, char *units) { - struct subRecord *psub=(struct subRecord *)paddr->precord; + subRecord *prec = (subRecord *)paddr->precord; - strncpy(units,psub->egu,DB_UNITS_SIZE); - return(0); + strncpy(units, prec->egu, DB_UNITS_SIZE); + return 0; } -static long get_precision(paddr,precision) - struct dbAddr *paddr; - long *precision; +static long get_precision(DBADDR *paddr, long *precision) { - struct subRecord *psub=(struct subRecord *)paddr->precord; + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); - *precision = psub->prec; - if(paddr->pfield==(void *)&psub->val) return(0); - recGblGetPrec(paddr,precision); - return(0); + *precision = prec->prec; + if (fieldIndex != subRecordVAL) + recGblGetPrec(paddr, precision); + + return 0; } -static long get_graphic_double(paddr,pgd) - struct dbAddr *paddr; - struct dbr_grDouble *pgd; +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { - struct subRecord *psub=(struct subRecord *)paddr->precord; + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); - if(paddr->pfield==(void *)&psub->val - || paddr->pfield==(void *)&psub->hihi - || paddr->pfield==(void *)&psub->high - || paddr->pfield==(void *)&psub->low - || paddr->pfield==(void *)&psub->lolo){ - pgd->upper_disp_limit = psub->hopr; - pgd->lower_disp_limit = psub->lopr; - return(0); - } + switch (fieldIndex) { + case subRecordVAL: + case subRecordHIHI: case subRecordHIGH: + case subRecordLOW: case subRecordLOLO: + case subRecordA: case subRecordB: + case subRecordC: case subRecordD: + case subRecordE: case subRecordF: + case subRecordG: case subRecordH: + case subRecordI: case subRecordJ: + case subRecordK: case subRecordL: + case subRecordLA: case subRecordLB: + case subRecordLC: case subRecordLD: + case subRecordLE: case subRecordLF: + case subRecordLG: case subRecordLH: + case subRecordLI: case subRecordLJ: + case subRecordLK: case subRecordLL: + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + break; - if(paddr->pfield>=(void *)&psub->a && paddr->pfield<=(void *)&psub->l){ - pgd->upper_disp_limit = psub->hopr; - pgd->lower_disp_limit = psub->lopr; - return(0); + default: + recGblGetGraphicDouble(paddr, pgd); } - if(paddr->pfield>=(void *)&psub->la && paddr->pfield<=(void *)&psub->ll){ - pgd->upper_disp_limit = psub->hopr; - pgd->lower_disp_limit = psub->lopr; - return(0); - } - recGblGetGraphicDouble(paddr,pgd); - return(0); + return 0; } -static long get_control_double(paddr,pcd) - struct dbAddr *paddr; - struct dbr_ctrlDouble *pcd; +static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) { - struct subRecord *psub=(struct subRecord *)paddr->precord; + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); - if(paddr->pfield==(void *)&psub->val - || paddr->pfield==(void *)&psub->hihi - || paddr->pfield==(void *)&psub->high - || paddr->pfield==(void *)&psub->low - || paddr->pfield==(void *)&psub->lolo){ - pcd->upper_ctrl_limit = psub->hopr; - pcd->lower_ctrl_limit = psub->lopr; - return(0); - } + switch (fieldIndex) { + case subRecordVAL: + case subRecordHIHI: case subRecordHIGH: + case subRecordLOW: case subRecordLOLO: + case subRecordA: case subRecordB: + case subRecordC: case subRecordD: + case subRecordE: case subRecordF: + case subRecordG: case subRecordH: + case subRecordI: case subRecordJ: + case subRecordK: case subRecordL: + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + break; - if(paddr->pfield>=(void *)&psub->a && paddr->pfield<=(void *)&psub->l){ - pcd->upper_ctrl_limit = psub->hopr; - pcd->lower_ctrl_limit = psub->lopr; - return(0); + default: + recGblGetControlDouble(paddr, pcd); } - if(paddr->pfield>=(void *)&psub->la && paddr->pfield<=(void *)&psub->ll){ - pcd->upper_ctrl_limit = psub->hopr; - pcd->lower_ctrl_limit = psub->lopr; - return(0); - } - recGblGetControlDouble(paddr,pcd); - return(0); + return 0; } -static long get_alarm_double(paddr,pad) - struct dbAddr *paddr; - struct dbr_alDouble *pad; +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) { - struct subRecord *psub=(struct subRecord *)paddr->precord; + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); - if(paddr->pfield==(void *)&psub->val){ - pad->upper_alarm_limit = psub->hihi; - pad->upper_warning_limit = psub->high; - pad->lower_warning_limit = psub->low; - pad->lower_alarm_limit = psub->lolo; - } else recGblGetAlarmDouble(paddr,pad); - return(0); + if (fieldIndex == subRecordVAL) { + pad->upper_alarm_limit = prec->hihi; + pad->upper_warning_limit = prec->high; + pad->lower_warning_limit = prec->low; + pad->lower_alarm_limit = prec->lolo; + } else { + recGblGetAlarmDouble(paddr, pad); + } + return 0; } -static void checkAlarms(psub) - struct subRecord *psub; +static void checkAlarms(subRecord *prec) { - double val; - double hyst, lalm, hihi, high, low, lolo; - unsigned short hhsv, llsv, hsv, lsv; + double val, hyst, lalm; + double hihi, high, low, lolo; + epicsEnum16 hhsv, llsv, hsv, lsv; - if (psub->udf) { - recGblSetSevr(psub,UDF_ALARM,INVALID_ALARM); - return; - } - hihi = psub->hihi; lolo = psub->lolo; high = psub->high; low = psub->low; - hhsv = psub->hhsv; llsv = psub->llsv; hsv = psub->hsv; lsv = psub->lsv; - val = psub->val; hyst = psub->hyst; lalm = psub->lalm; - - /* alarm condition hihi */ - if (hhsv && (val >= hihi || ((lalm==hihi) && (val >= hihi-hyst)))){ - if (recGblSetSevr(psub,HIHI_ALARM,psub->hhsv)) psub->lalm = hihi; - return; - } - - /* alarm condition lolo */ - if (llsv && (val <= lolo || ((lalm==lolo) && (val <= lolo+hyst)))){ - if (recGblSetSevr(psub,LOLO_ALARM,psub->llsv)) psub->lalm = lolo; - return; - } - - /* alarm condition high */ - if (hsv && (val >= high || ((lalm==high) && (val >= high-hyst)))){ - if (recGblSetSevr(psub,HIGH_ALARM,psub->hsv)) psub->lalm = high; - return; - } - - /* alarm condition low */ - if (lsv && (val <= low || ((lalm==low) && (val <= low+hyst)))){ - if (recGblSetSevr(psub,LOW_ALARM,psub->lsv)) psub->lalm = low; - return; - } - - /* we get here only if val is out of alarm by at least hyst */ - psub->lalm = val; - return; -} - -static void monitor(psub) - struct subRecord *psub; -{ - unsigned short monitor_mask; - double delta; - double *pnew; - double *pprev; - int i; - - /* get previous stat and sevr and new stat and sevr*/ - monitor_mask = recGblResetAlarms(psub); - /* check for value change */ - delta = psub->mlst - psub->val; - if(delta<0.0) delta = -delta; - if (delta > psub->mdel) { - /* post events for value change */ - monitor_mask |= DBE_VALUE; - /* update last value monitored */ - psub->mlst = psub->val; - } - /* check for archive change */ - delta = psub->alst - psub->val; - if(delta<0.0) delta = -delta; - if (delta > psub->adel) { - /* post events on value field for archive change */ - monitor_mask |= DBE_LOG; - /* update last archive value monitored */ - psub->alst = psub->val; - } - /* send out monitors connected to the value field */ - if (monitor_mask){ - db_post_events(psub,&psub->val,monitor_mask); - } - /* check all input fields for changes*/ - for(i=0, pnew=&psub->a, pprev=&psub->la; iudf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + hhsv = prec->hhsv; + hihi = prec->hihi; + if (hhsv && (val >= hihi || ((lalm == hihi) && (val >= hihi - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, prec->hhsv)) + prec->lalm = hihi; + return; + } + + /* alarm condition lolo */ + llsv = prec->llsv; + lolo = prec->lolo; + if (llsv && (val <= lolo || ((lalm == lolo) && (val <= lolo + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, prec->llsv)) + prec->lalm = lolo; + return; + } + + /* alarm condition high */ + hsv = prec->hsv; + high = prec->high; + if (hsv && (val >= high || ((lalm == high) && (val >= high - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, prec->hsv)) + prec->lalm = high; + return; + } + + /* alarm condition low */ + lsv = prec->lsv; + low = prec->low; + if (lsv && (val <= low || ((lalm == low) && (val <= low + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, prec->lsv)) + prec->lalm = low; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; } -static long fetch_values(psub) -struct subRecord *psub; +static void monitor(subRecord *prec) { - struct link *plink; /* structure of the link field */ - double *pvalue; - int i; - long status; + unsigned monitor_mask; + double delta; + double *pnew; + double *pold; + int i; - for(i=0, plink=&psub->inpa, pvalue=&psub->a; ival - prec->mlst; + if (delta < 0.0) delta = -delta; + if (delta > prec->mdel) { + /* post events for value change */ + monitor_mask |= DBE_VALUE; + /* update last value monitored */ + prec->mlst = prec->val; + } + /* check for archive change */ + delta = prec->val - prec->alst; + if (delta < 0.0) delta = -delta; + if (delta > prec->adel) { + /* post events on value field for archive change */ + monitor_mask |= DBE_LOG; + /* update last archive value monitored */ + prec->alst = prec->val; + } + /* send out monitors connected to the value field */ + if (monitor_mask) { + db_post_events(prec, &prec->val, monitor_mask); + } + /* check all input fields for changes */ + for (i = 0, pnew = &prec->a, pold = &prec->la; + i < INP_ARG_MAX; i++, pnew++, pold++) { + if (*pnew != *pold) { + db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG); + *pold = *pnew; } - return(0); + } + return; } -static long do_sub(psub) -struct subRecord *psub; /* pointer to subroutine record */ +static long fetch_values(subRecord *prec) { - long status; - SUBFUNCPTR psubroutine; + struct link *plink = &prec->inpa; + double *pvalue = &prec->a; + int i; - - /* call the subroutine */ - psubroutine = (SUBFUNCPTR)(psub->sadr); - if(psubroutine==NULL) { - recGblSetSevr(psub,BAD_SUB_ALARM,INVALID_ALARM); - return(0); - } - status = (*psubroutine)(psub); - if(status < 0){ - recGblSetSevr(psub,SOFT_ALARM,psub->brsv); - } else psub->udf = isnan(psub->val); - return(status); + for (i = 0; i < INP_ARG_MAX; i++) { + if (dbGetLink(plink++, DBR_DOUBLE, pvalue++, 0, 0)) + return -1; + } + return 0; +} + +static long do_sub(subRecord *prec) +{ + SUBFUNCPTR psubroutine = prec->sadr; + long status; + + if (psubroutine == NULL) { + recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM); + return 0; + } + + status = (*psubroutine)(prec); + if (status < 0) { + recGblSetSevr(prec, SOFT_ALARM, prec->brsv); + } else { + prec->udf = isnan(prec->val); + } + return status; } diff --git a/src/rec/subRecord.dbd b/src/rec/subRecord.dbd index 2592b3ce3..6b21c2348 100644 --- a/src/rec/subRecord.dbd +++ b/src/rec/subRecord.dbd @@ -1,10 +1,9 @@ #************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. -# EPICS BASE Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found +# EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* recordtype(sub) { @@ -24,15 +23,17 @@ recordtype(sub) { field(SNAM,DBF_STRING) { prompt("Subroutine Name") promptgroup(GUI_SUB) - special(SPC_NOMOD) + special(SPC_MOD) interest(1) size(40) } + %struct subRecord; + %typedef long (*SUBFUNCPTR)(struct subRecord *); field(SADR,DBF_NOACCESS) { prompt("Subroutine Address") special(SPC_NOMOD) interest(4) - extra("void * sadr") + extra("SUBFUNCPTR sadr") } field(INPA,DBF_INLINK) { prompt("Input A")