EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  2008  2009  <20102011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  <20102011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: [Merge] lp:~khkim/epics-base/alarm-filter into lp:epics-base
From: "Kim, Kukhee" <[email protected]>
To: [email protected]
Date: Fri, 28 May 2010 09:21:27 -0000
Kim, Kukhee has proposed merging lp:~khkim/epics-base/alarm-filter into lp:epics-base.

Requested reviews:
  EPICS Core Developers (epics-core)


This branch has alarm filter for ai, calc, longin, and mbbi record types.
-- 
https://code.launchpad.net/~khkim/epics-base/alarm-filter/+merge/26278
Your team EPICS Core Developers is requested to review the proposed merge of lp:~khkim/epics-base/alarm-filter into lp:epics-base.
=== modified file 'src/rec/aiRecord.c'
--- src/rec/aiRecord.c	2010-04-05 18:49:18 +0000
+++ src/rec/aiRecord.c	2010-05-28 09:21:26 +0000
@@ -6,7 +6,7 @@
 * EPICS BASE is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution. 
 \*************************************************************************/
-/* $Id$ */
+/* $Id: aiRecord.c,v 1.20.2.6 2009/04/03 14:40:12 lange Exp $ */
 
 /* aiRecord.c - Record Support Routines for Analog Input records */
 /*
@@ -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 @@
 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 @@
 	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 @@
 	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 */
@@ -283,60 +289,114 @@
     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;
-    }
-
-    /* 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;
-    }
-
-    /* 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;
+    /* 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;
+    }
+
+    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, lastTime);
+            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;
+    } else {
+        /* No alarm condition, reset LALM */
+        prec->lalm = val;
+    }
 }
 
 static void convert(aiRecord *prec)

=== modified file 'src/rec/aiRecord.dbd'
--- src/rec/aiRecord.dbd	2005-11-15 23:35:34 +0000
+++ src/rec/aiRecord.dbd	2010-05-28 09:21:26 +0000
@@ -138,6 +138,11 @@
 		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 @@
 		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)

=== modified file 'src/rec/calcRecord.c'
--- src/rec/calcRecord.c	2009-04-03 14:40:13 +0000
+++ src/rec/calcRecord.c	2010-05-28 09:21:26 +0000
@@ -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 @@
 };
 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,15 +114,19 @@
 
 static long process(calcRecord *prec)
 {
+    epicsTimeStamp timeLast;
+
     prec->pact = TRUE;
     if (fetch_values(prec)==0) {
 	if (calcPerform(&prec->a, &prec->val, prec->rpcl)) {
 	    recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
 	} 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 */
@@ -243,14 +250,27 @@
     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;
     }
 
@@ -258,45 +278,89 @@
     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;
-    }
-
-    /* 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;
-    }
-
-    /* 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;
+        /* 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;
+    }
+
+    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;
+    } else {
+        /* No alarm condition, reset LALM */
+        prec->lalm = val;
+    }
+
 }
 
 static void monitor(calcRecord *prec)

=== modified file 'src/rec/calcRecord.dbd'
--- src/rec/calcRecord.dbd	2007-03-13 16:39:53 +0000
+++ src/rec/calcRecord.dbd	2010-05-28 09:21:26 +0000
@@ -153,6 +153,16 @@
 		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)

=== modified file 'src/rec/longinRecord.c'
--- src/rec/longinRecord.c	2010-04-05 18:49:18 +0000
+++ src/rec/longinRecord.c	2010-05-28 09:21:26 +0000
@@ -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 @@
 	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 @@
 	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 @@
 	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 */
@@ -210,14 +214,28 @@
     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;
     }
 
@@ -225,45 +243,88 @@
     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;
-    }
-
-    /* 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;
-    }
-
-    /* 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;
+    /* 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;
+    }
+
+    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;
+    } else {
+        /* No alarm condition, reset LALM */
+        prec->lalm = val;
+    }
 }
 
 /* DELTA calculates the absolute difference between its arguments

=== modified file 'src/rec/longinRecord.dbd'
--- src/rec/longinRecord.dbd	2002-07-12 21:35:43 +0000
+++ src/rec/longinRecord.dbd	2010-05-28 09:21:26 +0000
@@ -93,6 +93,16 @@
 		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)

=== modified file 'src/rec/mbbiRecord.c'
--- src/rec/mbbiRecord.c	2010-04-05 18:49:18 +0000
+++ src/rec/mbbiRecord.c	2010-05-28 09:21:26 +0000
@@ -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 @@
 	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 @@
 	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 @@
 		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 @@
 	else if(status == 2) status = 0;
 
 	/* check for alarms */
-	checkAlarms(prec);
+	checkAlarms(prec, &timeLast);
 
 	/* check event list */
 	monitor(prec);
@@ -274,14 +280,20 @@
 	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 */
@@ -291,8 +303,36 @@
         } 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;

=== modified file 'src/rec/mbbiRecord.dbd'
--- src/rec/mbbiRecord.dbd	2009-06-08 19:55:49 +0000
+++ src/rec/mbbiRecord.dbd	2010-05-28 09:21:26 +0000
@@ -394,6 +394,16 @@
 		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)


Replies:
Re: [Merge] lp:~khkim/epics-base/alarm-filter into lp:epics-base Ralph Lange

Navigate by Date:
Prev: [Merge] lp:~dirk.zimoch/epics-base/fix-aai-and-aao into lp:epics-base Dirk Zimoch
Next: Re: [Merge] lp:~khkim/epics-base/alarm-filter into lp:epics-base Ralph Lange
Index: 2002  2003  2004  2005  2006  2007  2008  2009  <20102011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: [Merge] lp:~dirk.zimoch/epics-base/fix-aai-and-aao into lp:epics-base Dirk Zimoch
Next: Re: [Merge] lp:~khkim/epics-base/alarm-filter into lp:epics-base Ralph Lange
Index: 2002  2003  2004  2005  2006  2007  2008  2009  <20102011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 02 Feb 2012 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·