Merged alarm-filter branch.
This commit is contained in:
@@ -15,6 +15,23 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.</p>
|
||||
<h2 align="center">Changes between 3.14.x and 3.15.0.x</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Alarm filtering added to input record types</h3>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h3>Post events on Waveform record's NORD field</h3>
|
||||
|
||||
<p>When the record type or soft device support modify the NORD field of a
|
||||
|
||||
+100
-40
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+103
-39
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+98
-37
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user