From 0af48f5a29f93c080d86591ddabffab0a7de95bb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 6 Jan 2010 16:05:49 -0600 Subject: [PATCH 1/3] Alarm filter changes by Bernd Schoeneburg Algorithm by Eric Norum. Developed at the 2009 EPICS Codeathon. --- src/std/rec/aiRecord.c | 140 ++++++++++++++++++++++++++++----------- src/std/rec/aiRecord.dbd | 10 +++ 2 files changed, 110 insertions(+), 40 deletions(-) diff --git a/src/std/rec/aiRecord.c b/src/std/rec/aiRecord.c index 158456e88..7f2ce4bde 100644 --- a/src/std/rec/aiRecord.c +++ b/src/std/rec/aiRecord.c @@ -41,6 +41,9 @@ #undef GEN_SIZE_OFFSET #include "epicsExport.h" +/* Hysterisis for alarm filtering: 1-1/e */ +#define THRESHOLD 0.6321 + /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL @@ -99,7 +102,7 @@ typedef struct aidset { /* analog input dset */ extern unsigned int gts_trigger_counter; */ -static void checkAlarms(aiRecord *prec); +static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime); static void convert(aiRecord *prec); static void monitor(aiRecord *prec); static long readValue(aiRecord *prec); @@ -158,12 +161,15 @@ static long process(void *precord) aidset *pdset = (aidset *)(prec->dset); long status; unsigned char pact=prec->pact; + epicsTimeStamp timeLast; if( (pdset==NULL) || (pdset->read_ai==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_ai"); return(S_dev_missingSup); } + timeLast = prec->time; + status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); @@ -174,7 +180,7 @@ static long process(void *precord) else if (status==2) status=0; /* check for alarms */ - checkAlarms(prec); + checkAlarms(prec,&timeLast); /* check event list */ monitor(prec); /* process the forward scan link record */ @@ -305,60 +311,114 @@ static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad) return(0); } -static void checkAlarms(aiRecord *prec) +static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime) { - double val, hyst, lalm; - double alev; + enum { + range_Lolo = 1, + range_Low, + range_Normal, + range_High, + range_Hihi + } alarmRange; + static const epicsEnum16 range_stat[] = { + SOFT_ALARM, LOLO_ALARM, LOW_ALARM, + NO_ALARM, HIGH_ALARM, HIHI_ALARM + }; + double val, hyst, lalm, alev, aftc, afvl; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + prec->afvl = 0; return; } - val = prec->val; + val = prec->val; hyst = prec->hyst; lalm = prec->lalm; - /* alarm condition hihi */ - asev = prec->hhsv; - alev = prec->hihi; - if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { - if (recGblSetSevr(prec, HIHI_ALARM, asev)) - prec->lalm = alev; - return; + /* check VAL against alarm limits */ + if ((asev = prec->hhsv) && + (val >= (alev = prec->hihi) || + ((lalm == alev) && (val >= alev - hyst)))) + alarmRange = range_Hihi; + else + if ((asev = prec->llsv) && + (val <= (alev = prec->lolo) || + ((lalm == alev) && (val <= alev + hyst)))) + alarmRange = range_Lolo; + else + if ((asev = prec->hsv) && + (val >= (alev = prec->high) || + ((lalm == alev) && (val >= alev - hyst)))) + alarmRange = range_High; + else + if ((asev = prec->lsv) && + (val <= (alev = prec->low) || + ((lalm == alev) && (val <= alev + hyst)))) + alarmRange = range_Low; + else { + alev = val; + asev = NO_ALARM; + alarmRange = range_Normal; } - /* alarm condition lolo */ - asev = prec->llsv; - alev = prec->lolo; - if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { - if (recGblSetSevr(prec, LOLO_ALARM, asev)) - prec->lalm = alev; - return; - } + aftc = prec->aftc; + afvl = 0; - /* alarm condition high */ - asev = prec->hsv; - alev = prec->high; - if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { - if (recGblSetSevr(prec, HIGH_ALARM, asev)) - prec->lalm = alev; - return; - } + if (aftc > 0) { + /* Apply level filtering */ + afvl = prec->afvl; + if (afvl == 0) { + afvl = (double)alarmRange; + } else { + double t = epicsTimeDiffInSeconds(&prec->time, lastTime); + double alpha = aftc / (t + aftc); - /* alarm condition low */ - asev = prec->lsv; - alev = prec->low; - if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { - if (recGblSetSevr(prec, LOW_ALARM, asev)) - prec->lalm = alev; - return; - } + /* The sign of afvl indicates whether the result should be + * rounded up or down. This gives the filter hysteresis. + * If afvl > 0 the floor() function rounds to a lower alarm + * level, otherwise to a higher. + */ + afvl = alpha * afvl + + ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange; + if (afvl - floor(afvl) > THRESHOLD) + afvl = -afvl; /* reverse rounding */ - /* we get here only if val is out of alarm by at least hyst */ - prec->lalm = val; - return; + alarmRange = abs((int)floor(afvl)); + switch (alarmRange) { + case range_Hihi: + asev = prec->hhsv; + alev = prec->hihi; + break; + case range_High: + asev = prec->hsv; + alev = prec->high; + break; + case range_Normal: + asev = NO_ALARM; + break; + case range_Low: + asev = prec->lsv; + alev = prec->low; + break; + case range_Lolo: + asev = prec->llsv; + alev = prec->lolo; + break; + } + } + } + prec->afvl = afvl; + + if (asev) { + /* Report alarm condition, store LALM for future HYST calculations */ + if (recGblSetSevr(prec, range_stat[alarmRange], asev)) + prec->lalm = alev; + } else { + /* No alarm condition, reset LALM */ + prec->lalm = val; + } } static void convert(aiRecord *prec) diff --git a/src/std/rec/aiRecord.dbd b/src/std/rec/aiRecord.dbd index a7518ab73..52fd4b05b 100644 --- a/src/std/rec/aiRecord.dbd +++ b/src/std/rec/aiRecord.dbd @@ -138,6 +138,11 @@ recordtype(ai) { promptgroup(GUI_ALARMS) interest(1) } + field(AFTC,DBF_DOUBLE) { + prompt("Alarm Filter Time Constant") + promptgroup(GUI_ALARMS) + interest(1) + } field(ADEL,DBF_DOUBLE) { prompt("Archive Deadband") promptgroup(GUI_DISPLAY) @@ -153,6 +158,11 @@ recordtype(ai) { special(SPC_NOMOD) interest(3) } + field(AFVL,DBF_DOUBLE) { + prompt("Alarm Filter Value") + special(SPC_NOMOD) + interest(3) + } field(ALST,DBF_DOUBLE) { prompt("Last Value Archived") special(SPC_NOMOD) From 824d378117822298d803975bb4caec71de04d33c Mon Sep 17 00:00:00 2001 From: Kukhee Kim Date: Fri, 28 May 2010 02:16:45 -0700 Subject: [PATCH 2/3] add the alarm filter for ai, calc, longin, mbbi type records --- src/std/rec/calcRecord.c | 142 +++++++++++++++++++++++++---------- src/std/rec/calcRecord.dbd | 10 +++ src/std/rec/longinRecord.c | 135 ++++++++++++++++++++++++--------- src/std/rec/longinRecord.dbd | 10 +++ src/std/rec/mbbiRecord.c | 46 +++++++++++- src/std/rec/mbbiRecord.dbd | 10 +++ 6 files changed, 274 insertions(+), 79 deletions(-) diff --git a/src/std/rec/calcRecord.c b/src/std/rec/calcRecord.c index 85e66f538..acbc85668 100644 --- a/src/std/rec/calcRecord.c +++ b/src/std/rec/calcRecord.c @@ -37,6 +37,9 @@ #undef GEN_SIZE_OFFSET #include "epicsExport.h" +/* Hysterisis for alarm filtering: 1-1/e */ +#define THRESHOLD 0.6321 + /* Create RSET - Record Support Entry Table */ #define report NULL @@ -79,7 +82,7 @@ rset calcRSET={ }; epicsExportAddress(rset, calcRSET); -static void checkAlarms(calcRecord *prec); +static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast); static void monitor(calcRecord *prec); static int fetch_values(calcRecord *prec); @@ -111,6 +114,8 @@ static long init_record(calcRecord *prec, int pass) static long process(calcRecord *prec) { + epicsTimeStamp timeLast; + prec->pact = TRUE; if (fetch_values(prec) == 0) { if (calcPerform(&prec->a, &prec->val, prec->rpcl)) { @@ -118,9 +123,11 @@ static long process(calcRecord *prec) } else prec->udf = isnan(prec->val); } + + timeLast = prec->time; recGblGetTimeStamp(prec); /* check for alarms */ - checkAlarms(prec); + checkAlarms(prec, &timeLast); /* check event list */ monitor(prec); /* process the forward scan link record */ @@ -273,14 +280,27 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) return 0; } -static void checkAlarms(calcRecord *prec) +static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast) { - double val, hyst, lalm; - double alev; + + enum { + range_Lolo = 1, + range_Low, + range_Normal, + range_High, + range_Hihi + } alarmRange; + static const epicsEnum16 range_stat[] = { + SOFT_ALARM, LOLO_ALARM, LOW_ALARM, + NO_ALARM, HIGH_ALARM, HIHI_ALARM + }; + + double val, hyst, lalm, alev, aftc, afvl; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + prec->afvl = 0; return; } @@ -288,45 +308,89 @@ static void checkAlarms(calcRecord *prec) hyst = prec->hyst; lalm = prec->lalm; - /* alarm condition hihi */ - asev = prec->hhsv; - alev = prec->hihi; - if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { - if (recGblSetSevr(prec, HIHI_ALARM, asev)) - prec->lalm = alev; - return; + /* check VAL against alarm limits */ + if ((asev = prec->hhsv) && + (val >= (alev = prec->hihi) || + ((lalm == alev) && (val >= alev - hyst)))) + alarmRange = range_Hihi; + else + if ((asev = prec->llsv) && + (val <= (alev = prec->lolo) || + ((lalm == alev) && (val <= alev + hyst)))) + alarmRange = range_Lolo; + else + if ((asev = prec->hsv) && + (val >= (alev = prec->high) || + ((lalm == alev) && (val >= alev - hyst)))) + alarmRange = range_High; + else + if ((asev = prec->lsv) && + (val <= (alev = prec->low) || + ((lalm == alev) && (val <= alev + hyst)))) + alarmRange = range_Low; + else { + alev = val; + asev = NO_ALARM; + alarmRange = range_Normal; } - /* alarm condition lolo */ - asev = prec->llsv; - alev = prec->lolo; - if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { - if (recGblSetSevr(prec, LOLO_ALARM, asev)) + aftc = prec->aftc; + afvl = 0; + + if (aftc > 0) { + /* Apply level filtering */ + afvl = prec->afvl; + if (afvl == 0) { + afvl = (double)alarmRange; + } else { + double t = epicsTimeDiffInSeconds(&prec->time, timeLast); + double alpha = aftc / (t + aftc); + + /* The sign of afvl indicates whether the result should be + * rounded up or down. This gives the filter hysteresis. + * If afvl > 0 the floor() function rounds to a lower alarm + * level, otherwise to a higher. + */ + afvl = alpha * afvl + + ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange; + if (afvl - floor(afvl) > THRESHOLD) + afvl = -afvl; /* reverse rounding */ + + alarmRange = abs((int)floor(afvl)); + switch (alarmRange) { + case range_Hihi: + asev = prec->hhsv; + alev = prec->hihi; + break; + case range_High: + asev = prec->hsv; + alev = prec->high; + break; + case range_Normal: + asev = NO_ALARM; + break; + case range_Low: + asev = prec->lsv; + alev = prec->low; + break; + case range_Lolo: + asev = prec->llsv; + alev = prec->lolo; + break; + } + } + } + prec->afvl = afvl; + + if (asev) { + /* Report alarm condition, store LALM for future HYST calculations */ + if (recGblSetSevr(prec, range_stat[alarmRange], asev)) prec->lalm = alev; - return; + } else { + /* No alarm condition, reset LALM */ + prec->lalm = val; } - /* alarm condition high */ - asev = prec->hsv; - alev = prec->high; - if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { - if (recGblSetSevr(prec, HIGH_ALARM, asev)) - prec->lalm = alev; - return; - } - - /* alarm condition low */ - asev = prec->lsv; - alev = prec->low; - if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { - if (recGblSetSevr(prec, LOW_ALARM, asev)) - prec->lalm = alev; - return; - } - - /* we get here only if val is out of alarm by at least hyst */ - prec->lalm = val; - return; } static void monitor(calcRecord *prec) diff --git a/src/std/rec/calcRecord.dbd b/src/std/rec/calcRecord.dbd index 0248f34cd..b4d4cc99b 100644 --- a/src/std/rec/calcRecord.dbd +++ b/src/std/rec/calcRecord.dbd @@ -153,6 +153,16 @@ recordtype(calc) { interest(1) menu(menuAlarmSevr) } + field(AFTC, DBF_DOUBLE) { + prompt("Alarm Filter Time Constant") + promptgroup(GUI_ALARMS) + interest(1) + } + field(AFVL, DBF_DOUBLE) { + prompt("Alarm Filter Value") + special(SPC_NOMOD) + interest(3) + } field(HYST,DBF_DOUBLE) { prompt("Alarm Deadband") promptgroup(GUI_ALARMS) diff --git a/src/std/rec/longinRecord.c b/src/std/rec/longinRecord.c index b41bbe506..a625b56e4 100644 --- a/src/std/rec/longinRecord.c +++ b/src/std/rec/longinRecord.c @@ -37,6 +37,8 @@ #undef GEN_SIZE_OFFSET #include "epicsExport.h" + /* Hysterisis for alarm filtering: 1-1/e */ +#define THRESHOLD 0.6321 /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL @@ -87,7 +89,7 @@ struct longindset { /* longin input dset */ DEVSUPFUN get_ioint_info; DEVSUPFUN read_longin; /*returns: (-1,0)=>(failure,success)*/ }; -static void checkAlarms(longinRecord *prec); +static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast); static void monitor(longinRecord *prec); static long readValue(longinRecord *prec); @@ -132,12 +134,14 @@ static long process(longinRecord *prec) struct longindset *pdset = (struct longindset *)(prec->dset); long status; unsigned char pact=prec->pact; + epicsTimeStamp timeLast; if( (pdset==NULL) || (pdset->read_longin==NULL) ) { prec->pact=TRUE; recGblRecordError(S_dev_missingSup,(void *)prec,"read_longin"); return(S_dev_missingSup); } + timeLast = prec->time; status=readValue(prec); /* read the new value */ /* check if device support set pact */ @@ -148,7 +152,7 @@ static long process(longinRecord *prec) if (status==0) prec->udf = FALSE; /* check for alarms */ - checkAlarms(prec); + checkAlarms(prec, &timeLast); /* check event list */ monitor(prec); /* process the forward scan link record */ @@ -230,14 +234,28 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) return(0); } -static void checkAlarms(longinRecord *prec) +static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast) { + enum { + range_Lolo = 1, + range_Low, + range_Normal, + range_High, + range_Hihi + } alarmRange; + static const epicsEnum16 range_stat[] = { + SOFT_ALARM, LOLO_ALARM, LOW_ALARM, + NO_ALARM, HIGH_ALARM, HIHI_ALARM + }; + + double aftc, afvl; epicsInt32 val, hyst, lalm; epicsInt32 alev; epicsEnum16 asev; if (prec->udf) { recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + prec->afvl = 0; return; } @@ -245,45 +263,88 @@ static void checkAlarms(longinRecord *prec) hyst = prec->hyst; lalm = prec->lalm; - /* alarm condition hihi */ - asev = prec->hhsv; - alev = prec->hihi; - if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { - if (recGblSetSevr(prec, HIHI_ALARM, asev)) - prec->lalm = alev; - return; + /* check VAL against alarm limits */ + if ((asev = prec->hhsv) && + (val >= (alev = prec->hihi) || + ((lalm == alev) && (val >= alev - hyst)))) + alarmRange = range_Hihi; + else + if ((asev = prec->llsv) && + (val <= (alev = prec->lolo) || + ((lalm == alev) && (val <= alev + hyst)))) + alarmRange = range_Lolo; + else + if ((asev = prec->hsv) && + (val >= (alev = prec->high) || + ((lalm == alev) && (val >= alev - hyst)))) + alarmRange = range_High; + else + if ((asev = prec->lsv) && + (val <= (alev = prec->low) || + ((lalm == alev) && (val <= alev + hyst)))) + alarmRange = range_Low; + else { + alev = val; + asev = NO_ALARM; + alarmRange = range_Normal; } - /* alarm condition lolo */ - asev = prec->llsv; - alev = prec->lolo; - if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { - if (recGblSetSevr(prec, LOLO_ALARM, asev)) - prec->lalm = alev; - return; - } + aftc = prec->aftc; + afvl = 0; - /* alarm condition high */ - asev = prec->hsv; - alev = prec->high; - if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { - if (recGblSetSevr(prec, HIGH_ALARM, asev)) - prec->lalm = alev; - return; - } + if (aftc > 0) { + /* Apply level filtering */ + afvl = prec->afvl; + if (afvl == 0) { + afvl = (double)alarmRange; + } else { + double t = epicsTimeDiffInSeconds(&prec->time, timeLast); + double alpha = aftc / (t + aftc); - /* alarm condition low */ - asev = prec->lsv; - alev = prec->low; - if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { - if (recGblSetSevr(prec, LOW_ALARM, asev)) - prec->lalm = alev; - return; - } + /* The sign of afvl indicates whether the result should be + * rounded up or down. This gives the filter hysteresis. + * If afvl > 0 the floor() function rounds to a lower alarm + * level, otherwise to a higher. + */ + afvl = alpha * afvl + + ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange; + if (afvl - floor(afvl) > THRESHOLD) + afvl = -afvl; /* reverse rounding */ - /* we get here only if val is out of alarm by at least hyst */ - prec->lalm = val; - return; + alarmRange = abs((int)floor(afvl)); + switch (alarmRange) { + case range_Hihi: + asev = prec->hhsv; + alev = prec->hihi; + break; + case range_High: + asev = prec->hsv; + alev = prec->high; + break; + case range_Normal: + asev = NO_ALARM; + break; + case range_Low: + asev = prec->lsv; + alev = prec->low; + break; + case range_Lolo: + asev = prec->llsv; + alev = prec->lolo; + break; + } + } + } + prec->afvl = afvl; + + if (asev) { + /* Report alarm condition, store LALM for future HYST calculations */ + if (recGblSetSevr(prec, range_stat[alarmRange], asev)) + prec->lalm = alev; + } else { + /* No alarm condition, reset LALM */ + prec->lalm = val; + } } /* DELTA calculates the absolute difference between its arguments diff --git a/src/std/rec/longinRecord.dbd b/src/std/rec/longinRecord.dbd index 4bd734018..ef9506e3d 100644 --- a/src/std/rec/longinRecord.dbd +++ b/src/std/rec/longinRecord.dbd @@ -93,6 +93,16 @@ recordtype(longin) { promptgroup(GUI_ALARMS) interest(1) } + field(AFTC, DBF_DOUBLE) { + prompt("Alarm Filter Time Constant") + promptgroup(GUI_ALARMS) + interest(1) + } + field(AFVL, DBF_DOUBLE) { + prompt("Alarm Filter Value") + special(SPC_NOMOD) + interest(3) + } field(ADEL,DBF_LONG) { prompt("Archive Deadband") promptgroup(GUI_DISPLAY) diff --git a/src/std/rec/mbbiRecord.c b/src/std/rec/mbbiRecord.c index eddc188eb..fd8ebc98a 100644 --- a/src/std/rec/mbbiRecord.c +++ b/src/std/rec/mbbiRecord.c @@ -36,6 +36,9 @@ #include "mbbiRecord.h" #undef GEN_SIZE_OFFSET #include "epicsExport.h" + +/* Hysterisis for alarm filtering: 1-1/e */ +#define THRESHOLD 0.6321 /* Create RSET - Record Support Entry Table*/ #define report NULL #define initialize NULL @@ -84,7 +87,7 @@ struct mbbidset { /* multi bit binary input dset */ DEVSUPFUN get_ioint_info; DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/ }; -static void checkAlarms(mbbiRecord *); +static void checkAlarms(mbbiRecord *, epicsTimeStamp *); static void monitor(mbbiRecord *); static long readValue(mbbiRecord *); @@ -146,6 +149,7 @@ static long process(mbbiRecord *prec) struct mbbidset *pdset = (struct mbbidset *)(prec->dset); long status; unsigned char pact=prec->pact; + epicsTimeStamp timeLast; if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) { prec->pact=TRUE; @@ -153,6 +157,8 @@ static long process(mbbiRecord *prec) return(S_dev_missingSup); } + timeLast = prec->time; + status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); @@ -184,7 +190,7 @@ static long process(mbbiRecord *prec) else if(status == 2) status = 0; /* check for alarms */ - checkAlarms(prec); + checkAlarms(prec, &timeLast); /* check event list */ monitor(prec); @@ -279,14 +285,20 @@ static long put_enum_str(DBADDR *paddr, char *pstring) return(S_db_badChoice); } -static void checkAlarms(mbbiRecord *prec) +static void checkAlarms(mbbiRecord *prec, epicsTimeStamp *timeLast) { + + double aftc, afvl; + + unsigned short alarm; + epicsEnum16 asev; unsigned short *severities; unsigned short val=prec->val; /* check for udf alarm */ if(prec->udf == TRUE ){ recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + prec->afvl = 0; } /* check for state alarm */ @@ -296,9 +308,37 @@ static void checkAlarms(mbbiRecord *prec) } else { /* in a state which is an error */ severities = (unsigned short *)&(prec->zrsv); + /* recGblSetSevr(prec,STATE_ALARM,severities[prec->val]); + */ + alarm = severities[prec->val]; + } + aftc = prec->aftc; + afvl = 0.; + + if(aftc > 0.) { + afvl = prec->afvl; + if(afvl == 0.) { + afvl = (double) alarm; + } else { + double t = epicsTimeDiffInSeconds(&prec->time, timeLast); + double alpha = aftc / (t + aftc); + + afvl = alpha * afvl + + ((afvl>0.)?(1.-alpha):(alpha-1.)) * alarm; + if(afvl - floor(afvl) > THRESHOLD) + afvl = -afvl; + + alarm = abs((int)floor(afvl)); + } + } + + + asev = alarm; + recGblSetSevr(prec, STATE_ALARM, asev); + /* check for cos alarm */ if(val == prec->lalm) return; recGblSetSevr(prec,COS_ALARM,prec->cosv); diff --git a/src/std/rec/mbbiRecord.dbd b/src/std/rec/mbbiRecord.dbd index b0c33ad9e..bbb9fd17a 100644 --- a/src/std/rec/mbbiRecord.dbd +++ b/src/std/rec/mbbiRecord.dbd @@ -394,6 +394,16 @@ recordtype(mbbi) { interest(1) menu(menuAlarmSevr) } + field(AFTC, DBF_DOUBLE) { + prompt("Alarm Filter Time Constant") + promptgroup(GUI_ALARMS) + interest(1) + } + field(AFVL, DBF_DOUBLE) { + prompt("Alarm Filter Value") + special(SPC_NOMOD) + interest(3) + } field(UNSV,DBF_MENU) { prompt("Unknown State Severity") promptgroup(GUI_MBB) From d9e066ac29a2b30809a3544a99f5e624aef419e0 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 9 Mar 2012 12:22:45 -0600 Subject: [PATCH 3/3] Documentation on alarm filtering (AFTC field) --- documentation/RELEASE_NOTES.html | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 100cf023c..1d9fd1f75 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -15,6 +15,23 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.

Changes between 3.14.x and 3.15.0.x

+

Alarm filtering added to input record types

+ +

The record types ai, calc, longin and mbbi have a new alarm filter added to +them. This provides a low-pass filter that can be used to delay the reporting of +alarms caused by the input level passing the HIGH, HIHI, LOW or LOLO values. The +filter is controlled with a new AFTC field that sets the filter's time constant. +The default value for this field is zero, which keeps the record's original +alarm behaviour.

+ +

The record must be scanned often enough for the filtering action to work +effectively and the alarm severity can only change when the record is processed, +but that processing does not have to be regular; the filter uses the time since +the record last processed in its calculation. Setting AFTC to a positive number +of seconds will delay the record going into or out of a minor alarm severity or +from minor to major severity until the input signal has been in that range for +that number of seconds.

+

Post events on Waveform record's NORD field

When the record type or soft device support modify the NORD field of a