diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 9f0c79849..8d47d9b2a 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -15,6 +15,40 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.
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.
+ +When the record type or soft device support modify the NORD field of a +waveform record they now also post a DBE_VALUE and DBE_LOG event, signalling the +array length change to any clients monitoring the NORD field. Input device +support routines should be modified to do this as well.
+ +Non-VAL fields now report meaningful information for precision, units, +graphic limits, control limits, and alarm limits instead of simply using +PREC, EGU, HOPR, LOPR, DRVL, DRVH, HIHI, HIGH, LOW, and LOLO. All delay +fields have a default precision of 2 digits, units "s" and control limits +of 0 to 100,000 seconds (these precision and limit values can be changed +for each record type as a whole at runtime by updating a registered global +variable). Input fields like A-L of the calc record read their metadata +from the corresponding INPn link if possible.
The definitions from the header file epicsStdioRedirect.h have been moved
diff --git a/src/std/dev/devWfSoft.c b/src/std/dev/devWfSoft.c
index 92bb3c566..ea9280051 100644
--- a/src/std/dev/devWfSoft.c
+++ b/src/std/dev/devWfSoft.c
@@ -20,6 +20,7 @@
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
+#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "waveformRecord.h"
@@ -75,6 +76,7 @@ static long read_wf(waveformRecord *prec)
if (prec->tsel.type == CONSTANT &&
prec->tse == epicsTimeEventDeviceTime)
dbGetTimeStamp(&prec->inp, &prec->time);
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
}
return 0;
diff --git a/src/std/rec/aSubRecord.c b/src/std/rec/aSubRecord.c
index 24447cee3..1c033b3ba 100644
--- a/src/std/rec/aSubRecord.c
+++ b/src/std/rec/aSubRecord.c
@@ -53,14 +53,14 @@ static long special(DBADDR *, int);
static long cvt_dbaddr(DBADDR *);
static long get_array_info(DBADDR *, long *, long *);
static long put_array_info(DBADDR *, long );
-#define get_units NULL
+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
-#define get_graphic_double NULL
-#define get_control_double NULL
-#define get_alarm_double NULL
+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 aSubRSET = {
RSETNUMBER,
@@ -330,15 +330,116 @@ static long fetch_values(aSubRecord *prec)
return 0;
}
-static long get_precision(DBADDR *paddr, long *precision)
+#define indexof(field) aSubRecord##field
+
+static long get_inlinkNumber(int fieldIndex) {
+ if (fieldIndex >= indexof(A) && fieldIndex <= indexof(U))
+ return fieldIndex - indexof(A);
+ return -1;
+}
+
+static long get_outlinkNumber(int fieldIndex) {
+ if (fieldIndex >= indexof(VALA) && fieldIndex <= indexof(VALU))
+ return fieldIndex - indexof(VALA);
+ return -1;
+}
+
+static long get_units(DBADDR *paddr, char *units)
{
aSubRecord *prec = (aSubRecord *)paddr->precord;
+ int linkNumber;
- *precision = prec->prec;
- recGblGetPrec(paddr, precision);
+ linkNumber = get_inlinkNumber(dbGetFieldIndex(paddr));
+ if (linkNumber >= 0) {
+ dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE);
+ return 0;
+ }
+ linkNumber = get_outlinkNumber(dbGetFieldIndex(paddr));
+ if (linkNumber >= 0) {
+ dbGetUnits(&prec->outa + linkNumber, units, DB_UNITS_SIZE);
+ }
return 0;
}
+static long get_precision(DBADDR *paddr, long *pprecision)
+{
+ aSubRecord *prec = (aSubRecord *)paddr->precord;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
+ short precision;
+
+ *pprecision = prec->prec;
+ linkNumber = get_inlinkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0)
+ *pprecision = precision;
+ return 0;
+ }
+ linkNumber = get_outlinkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ if (dbGetPrecision(&prec->outa + linkNumber, &precision) == 0)
+ *pprecision = precision;
+ return 0;
+ }
+ recGblGetPrec(paddr, pprecision);
+ return 0;
+}
+
+static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
+{
+ aSubRecord *prec = (aSubRecord *)paddr->precord;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
+
+ linkNumber = get_inlinkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetGraphicLimits(&prec->inpa + linkNumber,
+ &pgd->lower_disp_limit,
+ &pgd->upper_disp_limit);
+ return 0;
+ }
+ linkNumber = get_outlinkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetGraphicLimits(&prec->outa + linkNumber,
+ &pgd->lower_disp_limit,
+ &pgd->upper_disp_limit);
+ }
+ return 0;
+}
+
+static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
+{
+ recGblGetControlDouble(paddr,pcd);
+ return 0;
+}
+
+static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
+{
+ aSubRecord *prec = (aSubRecord *)paddr->precord;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
+
+ linkNumber = get_inlinkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetAlarmLimits(&prec->inpa + linkNumber,
+ &pad->lower_alarm_limit,
+ &pad->lower_warning_limit,
+ &pad->upper_warning_limit,
+ &pad->upper_alarm_limit);
+ return 0;
+ }
+ linkNumber = get_outlinkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetAlarmLimits(&prec->outa + linkNumber,
+ &pad->lower_alarm_limit,
+ &pad->lower_warning_limit,
+ &pad->upper_warning_limit,
+ &pad->upper_alarm_limit);
+ return 0;
+ }
+ recGblGetAlarmDouble(paddr, pad);
+ return 0;
+}
static void monitor(aSubRecord *prec)
{
diff --git a/src/std/rec/aaiRecord.c b/src/std/rec/aaiRecord.c
index 5c88791c4..428916de9 100644
--- a/src/std/rec/aaiRecord.c
+++ b/src/std/rec/aaiRecord.c
@@ -109,7 +109,7 @@ static long init_record(aaiRecord *prec, int pass)
recGblRecordError(S_dev_noDSET, prec, "aai: init_record");
return S_dev_noDSET;
}
-
+
if (pass == 0) {
if (prec->nelm <= 0)
prec->nelm = 1;
@@ -120,12 +120,12 @@ static long init_record(aaiRecord *prec, int pass)
} else {
prec->nord = 0;
}
-
+
/* we must call pdset->init_record in pass 0
because it may set prec->bptr which must
not change after links are established before pass 1
*/
-
+
if (pdset->init_record) {
/* init_record may set the bptr to point to the data */
if ((status = pdset->init_record(prec)))
@@ -141,7 +141,7 @@ static long init_record(aaiRecord *prec, int pass)
/* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */
if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
}
/* must have read_aai function defined */
@@ -210,11 +210,20 @@ static long put_array_info(DBADDR *paddr, long nNew)
return 0;
}
+#define indexof(field) aaiRecord##field
+
static long get_units(DBADDR *paddr, char *units)
{
aaiRecord *prec = (aaiRecord *)paddr->precord;
- strncpy(units, prec->egu, DB_UNITS_SIZE);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
+ break;
+ case indexof(HOPR):
+ case indexof(LOPR):
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return 0;
}
@@ -223,8 +232,8 @@ static long get_precision(DBADDR *paddr, long *precision)
aaiRecord *prec = (aaiRecord *)paddr->precord;
*precision = prec->prec;
- if (paddr->pfield == prec->bptr) return 0;
- recGblGetPrec(paddr, precision);
+ if (dbGetFieldIndex(paddr) != indexof(VAL))
+ recGblGetPrec(paddr, precision);
return 0;
}
@@ -232,10 +241,18 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
aaiRecord *prec = (aaiRecord *)paddr->precord;
- if (paddr->pfield == prec->bptr) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr, pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ case indexof(NORD):
+ pgd->upper_disp_limit = prec->nelm;
+ pgd->lower_disp_limit = 0;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr, pgd);
+ }
return 0;
}
@@ -243,10 +260,18 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
aaiRecord *prec = (aaiRecord *)paddr->precord;
- if (paddr->pfield == prec->bptr) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr, pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ case indexof(NORD):
+ pcd->upper_ctrl_limit = prec->nelm;
+ pcd->lower_ctrl_limit = 0;
+ break;
+ default:
+ recGblGetControlDouble(paddr, pcd);
+ }
return 0;
}
diff --git a/src/std/rec/aaoRecord.c b/src/std/rec/aaoRecord.c
index 695f8e1b1..46c837310 100644
--- a/src/std/rec/aaoRecord.c
+++ b/src/std/rec/aaoRecord.c
@@ -109,7 +109,7 @@ static long init_record(aaoRecord *prec, int pass)
recGblRecordError(S_dev_noDSET, prec, "aao: init_record");
return S_dev_noDSET;
}
-
+
if (pass == 0) {
if (prec->nelm <= 0)
prec->nelm = 1;
@@ -120,12 +120,12 @@ static long init_record(aaoRecord *prec, int pass)
} else {
prec->nord = 0;
}
-
+
/* we must call pdset->init_record in pass 0
because it may set prec->bptr which must
not change after links are established before pass 1
*/
-
+
if (pdset->init_record) {
/* init_record may set the bptr to point to the data */
if ((status = pdset->init_record(prec)))
@@ -138,12 +138,12 @@ static long init_record(aaoRecord *prec, int pass)
}
return 0;
}
-
+
/* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */
if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
}
-
+
/* must have write_aao function defined */
if (pdset->number < 5 || pdset->write_aao == NULL) {
recGblRecordError(S_dev_missingSup, prec, "aao: init_record");
@@ -210,11 +210,20 @@ static long put_array_info(DBADDR *paddr, long nNew)
return 0;
}
+#define indexof(field) aaoRecord##field
+
static long get_units(DBADDR *paddr, char *units)
{
aaoRecord *prec = (aaoRecord *)paddr->precord;
- strncpy(units, prec->egu, DB_UNITS_SIZE);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
+ break;
+ case indexof(HOPR):
+ case indexof(LOPR):
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return 0;
}
@@ -223,8 +232,8 @@ static long get_precision(DBADDR *paddr, long *precision)
aaoRecord *prec = (aaoRecord *)paddr->precord;
*precision = prec->prec;
- if (paddr->pfield == (void *)prec->bptr) return 0;
- recGblGetPrec(paddr, precision);
+ if (dbGetFieldIndex(paddr) != indexof(VAL))
+ recGblGetPrec(paddr, precision);
return 0;
}
@@ -232,10 +241,18 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
aaoRecord *prec = (aaoRecord *)paddr->precord;
- if (paddr->pfield == prec->bptr) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ case indexof(NORD):
+ pgd->upper_disp_limit = prec->nelm;
+ pgd->lower_disp_limit = 0;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr, pgd);
+ }
return 0;
}
@@ -243,10 +260,18 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
aaoRecord *prec = (aaoRecord *)paddr->precord;
- if(paddr->pfield == prec->bptr){
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ case indexof(NORD):
+ pcd->upper_ctrl_limit = prec->nelm;
+ pcd->lower_ctrl_limit = 0;
+ break;
+ default:
+ recGblGetControlDouble(paddr, pcd);
+ }
return 0;
}
diff --git a/src/std/rec/aiRecord.c b/src/std/rec/aiRecord.c
index 343d62126..94f665af7 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
@@ -94,12 +97,7 @@ typedef struct aidset { /* analog input dset */
}aidset;
-/*Following from timing system */
-/*
-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 +156,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 +175,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 */
@@ -217,11 +218,22 @@ static long special(DBADDR *paddr,int after)
}
}
+#define indexof(field) aiRecord##field
+
static long get_units(DBADDR *paddr, char *units)
{
aiRecord *prec=(aiRecord *)paddr->precord;
- strncpy(units,prec->egu,DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_DOUBLE) {
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(ASLO):
+ case indexof(AOFF):
+ case indexof(SMOO):
+ break;
+ default:
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
+ }
return(0);
}
@@ -230,7 +242,7 @@ static long get_precision(DBADDR *paddr, long *precision)
aiRecord *prec=(aiRecord *)paddr->precord;
*precision = prec->prec;
- if(paddr->pfield == (void *)&prec->val) return(0);
+ if (dbGetFieldIndex(paddr) == indexof(VAL)) return(0);
recGblGetPrec(paddr,precision);
return(0);
}
@@ -238,43 +250,54 @@ static long get_precision(DBADDR *paddr, long *precision)
static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
{
aiRecord *prec=(aiRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == aiRecordVAL
- || fieldIndex == aiRecordHIHI
- || fieldIndex == aiRecordHIGH
- || fieldIndex == aiRecordLOW
- || fieldIndex == aiRecordLOLO
- || fieldIndex == aiRecordHOPR
- || fieldIndex == aiRecordLOPR) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ case indexof(SVAL):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
{
aiRecord *prec=(aiRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == aiRecordVAL
- || fieldIndex == aiRecordHIHI
- || fieldIndex == aiRecordHIGH
- || fieldIndex == aiRecordLOW
- || fieldIndex == aiRecordLOLO) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ case indexof(SVAL):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
{
aiRecord *prec=(aiRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == aiRecordVAL) {
+ if (dbGetFieldIndex(paddr) == indexof(VAL)) {
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
@@ -283,60 +306,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)
diff --git a/src/std/rec/aoRecord.c b/src/std/rec/aoRecord.c
index 5f8436ab2..5b77b607e 100644
--- a/src/std/rec/aoRecord.c
+++ b/src/std/rec/aoRecord.c
@@ -276,11 +276,21 @@ static long special(DBADDR *paddr, int after)
}
}
+#define indexof(field) aoRecord##field
+
static long get_units(DBADDR * paddr,char *units)
{
aoRecord *prec=(aoRecord *)paddr->precord;
- strncpy(units,prec->egu,DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_DOUBLE) {
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(ASLO):
+ case indexof(AOFF):
+ break;
+ default:
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
+ }
return(0);
}
@@ -289,10 +299,14 @@ static long get_precision(DBADDR *paddr,long *precision)
aoRecord *prec=(aoRecord *)paddr->precord;
*precision = prec->prec;
- if(paddr->pfield == (void *)&prec->val
- || paddr->pfield == (void *)&prec->oval
- || paddr->pfield == (void *)&prec->pval) return(0);
- recGblGetPrec(paddr,precision);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(OVAL):
+ case indexof(PVAL):
+ break;
+ default:
+ recGblGetPrec(paddr,precision);
+ }
return(0);
}
@@ -300,16 +314,24 @@ static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
{
aoRecord *prec=(aoRecord *)paddr->precord;
- if(paddr->pfield==(void *)&prec->val
- || paddr->pfield==(void *)&prec->hihi
- || paddr->pfield==(void *)&prec->high
- || paddr->pfield==(void *)&prec->low
- || paddr->pfield==(void *)&prec->lolo
- || paddr->pfield==(void *)&prec->oval
- || paddr->pfield==(void *)&prec->pval){
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(OVAL):
+ case indexof(PVAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ case indexof(IVOV):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
@@ -317,23 +339,30 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
aoRecord *prec=(aoRecord *)paddr->precord;
- if(paddr->pfield==(void *)&prec->val
- || paddr->pfield==(void *)&prec->hihi
- || paddr->pfield==(void *)&prec->high
- || paddr->pfield==(void *)&prec->low
- || paddr->pfield==(void *)&prec->lolo
- || paddr->pfield==(void *)&prec->oval
- || paddr->pfield==(void *)&prec->pval){
- pcd->upper_ctrl_limit = prec->drvh;
- pcd->lower_ctrl_limit = prec->drvl;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(OVAL):
+ case indexof(PVAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pcd->upper_ctrl_limit = prec->drvh;
+ pcd->lower_ctrl_limit = prec->drvl;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
{
aoRecord *prec=(aoRecord *)paddr->precord;
- if(paddr->pfield==(void *)&prec->val){
+ if(dbGetFieldIndex(paddr) == indexof(VAL)){
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
diff --git a/src/std/rec/boRecord.c b/src/std/rec/boRecord.c
index 5248bd661..ed6364734 100644
--- a/src/std/rec/boRecord.c
+++ b/src/std/rec/boRecord.c
@@ -51,13 +51,13 @@ static long process(boRecord *);
#define cvt_dbaddr NULL
#define get_array_info NULL
#define put_array_info NULL
-#define get_units NULL
+static long get_units(DBADDR *, char *);
static long get_precision(DBADDR *, long *);
static long get_enum_str(DBADDR *, char *);
static long get_enum_strs(DBADDR *, struct dbr_enumStrs *);
static long put_enum_str(DBADDR *, char *);
#define get_graphic_double NULL
-#define get_control_double NULL
+static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
#define get_alarm_double NULL
rset boRSET={
@@ -82,6 +82,11 @@ rset boRSET={
};
epicsExportAddress(rset,boRSET);
+int boHIGHprecision = 2;
+epicsExportAddress(int, boHIGHprecision);
+double boHIGHlimit = 100000;
+epicsExportAddress(double, boHIGHlimit);
+
struct bodset { /* binary output dset */
long number;
DEVSUPFUN dev_report;
@@ -268,12 +273,31 @@ static long process(boRecord *prec)
return(status);
}
+#define indexof(field) boRecord##field
+
+static long get_units(DBADDR *paddr, char *units)
+{
+ if(dbGetFieldIndex(paddr) == indexof(HIGH))
+ strcpy(units, "s");
+ return(0);
+}
+
static long get_precision(DBADDR *paddr, long *precision)
{
- boRecord *prec=(boRecord *)paddr->precord;
+ if(dbGetFieldIndex(paddr) == indexof(HIGH))
+ *precision = boHIGHprecision;
+ else
+ recGblGetPrec(paddr,precision);
+ return(0);
+}
- if(paddr->pfield == (void *)&prec->high) *precision=2;
- else recGblGetPrec(paddr,precision);
+static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
+{
+ if(dbGetFieldIndex(paddr) == indexof(HIGH)) {
+ pcd->lower_ctrl_limit = 0.0;
+ pcd->upper_ctrl_limit = boHIGHlimit;
+ } else
+ recGblGetControlDouble(paddr,pcd);
return(0);
}
@@ -285,7 +309,7 @@ static long get_enum_str(DBADDR *paddr, char *pstring)
index = dbGetFieldIndex(paddr);
- if(index!=boRecordVAL) {
+ if(index!=indexof(VAL)) {
strcpy(pstring,"Illegal_Value");
} else if(*pfield==0) {
strncpy(pstring,prec->znam,sizeof(prec->znam));
diff --git a/src/std/rec/boRecord.dbd b/src/std/rec/boRecord.dbd
index adc6704ad..4bc44c2fd 100644
--- a/src/std/rec/boRecord.dbd
+++ b/src/std/rec/boRecord.dbd
@@ -149,3 +149,6 @@ recordtype(bo) {
interest(2)
}
}
+
+variable(boHIGHprecision, int)
+variable(boHIGHlimit, double)
diff --git a/src/std/rec/calcRecord.c b/src/std/rec/calcRecord.c
index 722ce0698..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
@@ -54,32 +57,32 @@ static long get_precision(DBADDR *paddr, long *precision);
#define get_enum_strs NULL
#define put_enum_str NULL
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd);
-static long get_ctrl_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd);
+static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd);
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad);
rset calcRSET={
- 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_ctrl_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, calcRSET);
-static void checkAlarms(calcRecord *prec);
+static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast);
static void monitor(calcRecord *prec);
static int fetch_values(calcRecord *prec);
@@ -96,30 +99,35 @@ static long init_record(calcRecord *prec, int pass)
plink = &prec->inpa;
pvalue = &prec->a;
for (i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) {
- if (plink->type == CONSTANT) {
- recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
- }
+ if (plink->type == CONSTANT) {
+ recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
+ }
}
if (postfix(prec->calc, prec->rpcl, &error_number)) {
- recGblRecordError(S_db_badField, (void *)prec,
- "calc: init_record: Illegal CALC field");
- errlogPrintf("%s.CALC: %s in expression \"%s\"\n",
- prec->name, calcErrorStr(error_number), prec->calc);
+ recGblRecordError(S_db_badField, (void *)prec,
+ "calc: init_record: Illegal CALC field");
+ errlogPrintf("%s.CALC: %s in expression \"%s\"\n",
+ prec->name, calcErrorStr(error_number), prec->calc);
}
return 0;
}
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);
+ 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 */
@@ -135,24 +143,41 @@ static long special(DBADDR *paddr, int after)
if (!after) return 0;
if (paddr->special == SPC_CALC) {
- if (postfix(prec->calc, prec->rpcl, &error_number)) {
- recGblRecordError(S_db_badField, (void *)prec,
- "calc: Illegal CALC field");
- errlogPrintf("%s.CALC: %s in expression \"%s\"\n",
- prec->name, calcErrorStr(error_number), prec->calc);
- return S_db_badField;
- }
- return 0;
+ if (postfix(prec->calc, prec->rpcl, &error_number)) {
+ recGblRecordError(S_db_badField, (void *)prec,
+ "calc: Illegal CALC field");
+ errlogPrintf("%s.CALC: %s in expression \"%s\"\n",
+ prec->name, calcErrorStr(error_number), prec->calc);
+ return S_db_badField;
+ }
+ return 0;
}
recGblDbaddrError(S_db_badChoice, paddr, "calc::special - bad special value!");
return S_db_badChoice;
}
+#define indexof(field) calcRecord##field
+
+static long get_linkNumber(int fieldIndex) {
+ if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L))
+ return fieldIndex - indexof(A);
+ if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL))
+ return fieldIndex - indexof(LA);
+ return -1;
+}
+
static long get_units(DBADDR *paddr, char *units)
{
calcRecord *prec = (calcRecord *)paddr->precord;
+ int linkNumber;
- strncpy(units, prec->egu, DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_DOUBLE) {
+ linkNumber = get_linkNumber(dbGetFieldIndex(paddr));
+ if (linkNumber >= 0)
+ dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE);
+ else
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return 0;
}
@@ -160,98 +185,122 @@ static long get_precision(DBADDR *paddr, long *pprecision)
{
calcRecord *prec = (calcRecord *)paddr->precord;
int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
*pprecision = prec->prec;
-
- if (fieldIndex != calcRecordVAL)
- recGblGetPrec(paddr, pprecision);
-
+ if (fieldIndex == indexof(VAL)) {
+ return 0;
+ }
+ linkNumber = get_linkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ short precision;
+ if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0)
+ *pprecision = precision;
+ else
+ *pprecision = 15;
+ } else
+ recGblGetPrec(paddr, pprecision);
return 0;
}
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
calcRecord *prec = (calcRecord *)paddr->precord;
-
- if (paddr->pfield == (void *)&prec->val ||
- paddr->pfield == (void *)&prec->hihi ||
- paddr->pfield == (void *)&prec->high ||
- paddr->pfield == (void *)&prec->low ||
- paddr->pfield == (void *)&prec->lolo) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- return 0;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
+
+ switch (fieldIndex) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pgd->lower_disp_limit = prec->lopr;
+ pgd->upper_disp_limit = prec->hopr;
+ break;
+ default:
+ linkNumber = get_linkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetGraphicLimits(&prec->inpa + linkNumber,
+ &pgd->lower_disp_limit,
+ &pgd->upper_disp_limit);
+ } else
+ recGblGetGraphicDouble(paddr,pgd);
}
-
- if (paddr->pfield >= (void *)&prec->a &&
- paddr->pfield <= (void *)&prec->l) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- return 0;
- }
- if (paddr->pfield >= (void *)&prec->la &&
- paddr->pfield <= (void *)&prec->ll) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- return 0;
- }
- recGblGetGraphicDouble(paddr, pgd);
return 0;
}
-static long get_ctrl_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
+static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
calcRecord *prec = (calcRecord *)paddr->precord;
-
- if (paddr->pfield == (void *)&prec->val ||
- paddr->pfield == (void *)&prec->hihi ||
- paddr->pfield == (void *)&prec->high ||
- paddr->pfield == (void *)&prec->low ||
- paddr->pfield == (void *)&prec->lolo) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- return 0;
+
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pcd->lower_ctrl_limit = prec->lopr;
+ pcd->upper_ctrl_limit = prec->hopr;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
}
-
- if (paddr->pfield >= (void *)&prec->a &&
- paddr->pfield <= (void *)&prec->l) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- return 0;
- }
- if (paddr->pfield >= (void *)&prec->la &&
- paddr->pfield <= (void *)&prec->ll) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- return 0;
- }
- recGblGetControlDouble(paddr, pcd);
return 0;
}
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
{
calcRecord *prec = (calcRecord *)paddr->precord;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
- if (paddr->pfield == (void *)&prec->val) {
- pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
- pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
- pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
+ if (fieldIndex == indexof(VAL)) {
pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
+ pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
+ pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
+ pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
} else {
- recGblGetAlarmDouble(paddr, pad);
+ linkNumber = get_linkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetAlarmLimits(&prec->inpa + linkNumber,
+ &pad->lower_alarm_limit,
+ &pad->lower_warning_limit,
+ &pad->upper_warning_limit,
+ &pad->upper_alarm_limit);
+ } else
+ recGblGetAlarmDouble(paddr, 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;
}
@@ -259,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)
@@ -311,34 +404,34 @@ static void monitor(calcRecord *prec)
delta = prec->mlst - prec->val;
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;
+ /* post events for value change */
+ monitor_mask |= DBE_VALUE;
+ /* update last value monitored */
+ prec->mlst = prec->val;
}
/* check for archive change */
delta = prec->alst - prec->val;
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;
+ /* 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);
+ db_post_events(prec, &prec->val, monitor_mask);
}
/* check all input fields for changes*/
pnew = &prec->a;
pprev = &prec->la;
for (i = 0; i < CALCPERFORM_NARGS; i++, pnew++, pprev++) {
- if (*pnew != *pprev ||
- monitor_mask & DBE_ALARM) {
- db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG);
- *pprev = *pnew;
- }
+ if (*pnew != *pprev ||
+ monitor_mask & DBE_ALARM) {
+ db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG);
+ *pprev = *pnew;
+ }
}
return;
}
@@ -353,10 +446,10 @@ static int fetch_values(calcRecord *prec)
plink = &prec->inpa;
pvalue = &prec->a;
for(i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) {
- int newStatus;
+ int newStatus;
- newStatus = dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0);
- if (status == 0) status = newStatus;
+ newStatus = dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0);
+ if (status == 0) status = newStatus;
}
return status;
}
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/calcoutRecord.c b/src/std/rec/calcoutRecord.c
index 9e18c0996..3fe97732e 100644
--- a/src/std/rec/calcoutRecord.c
+++ b/src/std/rec/calcoutRecord.c
@@ -59,7 +59,7 @@ static long get_precision(DBADDR *, long *);
#define get_enum_strs NULL
#define put_enum_str NULL
static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
-static long get_ctrl_double(DBADDR *, struct dbr_ctrlDouble *);
+static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
rset calcoutRSET = {
@@ -79,11 +79,16 @@ rset calcoutRSET = {
get_enum_strs,
put_enum_str,
get_graphic_double,
- get_ctrl_double,
+ get_control_double,
get_alarm_double
};
epicsExportAddress(rset, calcoutRSET);
-
+
+int calcoutODLYprecision = 2;
+epicsExportAddress(int, calcoutODLYprecision);
+double calcoutODLYlimit = 100000;
+epicsExportAddress(double, calcoutODLYlimit);
+
typedef struct calcoutDSET {
long number;
DEVSUPFUN dev_report;
@@ -371,11 +376,34 @@ static long special(DBADDR *paddr, int after)
}
}
+#define indexof(field) calcoutRecord##field
+
+static long get_linkNumber(int fieldIndex) {
+ if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L))
+ return fieldIndex - indexof(A);
+ if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL))
+ return fieldIndex - indexof(LA);
+ return -1;
+}
+
static long get_units(DBADDR *paddr, char *units)
{
calcoutRecord *prec = (calcoutRecord *)paddr->precord;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
- strncpy(units, prec->egu, DB_UNITS_SIZE);
+ if(fieldIndex == indexof(ODLY)) {
+ strcpy(units, "s");
+ return 0;
+ }
+
+ if(paddr->pfldDes->field_type == DBF_DOUBLE) {
+ linkNumber = get_linkNumber(dbGetFieldIndex(paddr));
+ if (linkNumber >= 0)
+ dbGetUnits(&prec->inpa + linkNumber, units, DB_UNITS_SIZE);
+ else
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return 0;
}
@@ -383,86 +411,109 @@ static long get_precision(DBADDR *paddr, long *pprecision)
{
calcoutRecord *prec = (calcoutRecord *)paddr->precord;
int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
+ if(fieldIndex == indexof(ODLY)) {
+ *pprecision = calcoutODLYprecision;
+ return 0;
+ }
*pprecision = prec->prec;
-
- if (fieldIndex != calcoutRecordVAL)
+ if (fieldIndex == indexof(VAL)) {
+ return 0;
+ }
+ linkNumber = get_linkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ short precision;
+ if (dbGetPrecision(&prec->inpa + linkNumber, &precision) == 0)
+ *pprecision = precision;
+ else
+ *pprecision = 15;
+ } else
recGblGetPrec(paddr, pprecision);
-
return 0;
}
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
calcoutRecord *prec = (calcoutRecord *)paddr->precord;
-
- if (paddr->pfield == (void *)&prec->val ||
- paddr->pfield == (void *)&prec->hihi ||
- paddr->pfield == (void *)&prec->high ||
- paddr->pfield == (void *)&prec->low ||
- paddr->pfield == (void *)&prec->lolo) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- return 0;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
+
+ switch (fieldIndex) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pgd->lower_disp_limit = prec->lopr;
+ pgd->upper_disp_limit = prec->hopr;
+ break;
+ case indexof(ODLY):
+ recGblGetGraphicDouble(paddr,pgd);
+ pgd->lower_disp_limit = 0.0;
+ break;
+ default:
+ linkNumber = get_linkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetGraphicLimits(&prec->inpa + linkNumber,
+ &pgd->lower_disp_limit,
+ &pgd->upper_disp_limit);
+ } else
+ recGblGetGraphicDouble(paddr,pgd);
}
-
- if (paddr->pfield >= (void *)&prec->a &&
- paddr->pfield <= (void *)&prec->l) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- return 0;
- }
- if (paddr->pfield >= (void *)&prec->la &&
- paddr->pfield <= (void *)&prec->ll) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- return 0;
- }
- recGblGetGraphicDouble(paddr, pgd);
return 0;
}
-static long get_ctrl_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
+static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
calcoutRecord *prec = (calcoutRecord *)paddr->precord;
-
- if (paddr->pfield == (void *)&prec->val ||
- paddr->pfield == (void *)&prec->hihi ||
- paddr->pfield == (void *)&prec->high ||
- paddr->pfield == (void *)&prec->low ||
- paddr->pfield == (void *)&prec->lolo) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- return 0;
+
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pcd->lower_ctrl_limit = prec->lopr;
+ pcd->upper_ctrl_limit = prec->hopr;
+ break;
+ case indexof(ODLY):
+ pcd->lower_ctrl_limit = 0.0;
+ pcd->upper_ctrl_limit = calcoutODLYlimit;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
}
-
- if (paddr->pfield >= (void *)&prec->a &&
- paddr->pfield <= (void *)&prec->l) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- return 0;
- }
- if (paddr->pfield >= (void *)&prec->la &&
- paddr->pfield <= (void *)&prec->ll) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- return 0;
- }
- recGblGetControlDouble(paddr, pcd);
return 0;
}
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
{
calcoutRecord *prec = (calcoutRecord *)paddr->precord;
+ int fieldIndex = dbGetFieldIndex(paddr);
+ int linkNumber;
- if (paddr->pfield == (void *)&prec->val) {
+ if (fieldIndex == indexof(VAL)) {
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
} else {
- recGblGetAlarmDouble(paddr, pad);
+ linkNumber = get_linkNumber(fieldIndex);
+ if (linkNumber >= 0) {
+ dbGetAlarmLimits(&prec->inpa + linkNumber,
+ &pad->lower_alarm_limit,
+ &pad->lower_warning_limit,
+ &pad->upper_warning_limit,
+ &pad->upper_alarm_limit);
+ } else
+ recGblGetAlarmDouble(paddr, pad);
}
return 0;
}
diff --git a/src/std/rec/calcoutRecord.dbd b/src/std/rec/calcoutRecord.dbd
index bc916c961..f58ac242f 100644
--- a/src/std/rec/calcoutRecord.dbd
+++ b/src/std/rec/calcoutRecord.dbd
@@ -513,3 +513,6 @@ recordtype(calcout) {
extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]")
}
}
+
+variable(calcoutODLYprecision, int)
+variable(calcoutODLYlimit, double)
diff --git a/src/std/rec/compressRecord.c b/src/std/rec/compressRecord.c
index 2879852d8..46966dba4 100644
--- a/src/std/rec/compressRecord.c
+++ b/src/std/rec/compressRecord.c
@@ -400,11 +400,16 @@ static long put_array_info(DBADDR *paddr, long nNew)
return(0);
}
+#define indexof(field) compressRecord##field
+
static long get_units(DBADDR *paddr,char *units)
{
compressRecord *prec=(compressRecord *)paddr->precord;
- strncpy(units,prec->egu,DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_DOUBLE
+ || dbGetFieldIndex(paddr) == indexof(VAL)) {
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return(0);
}
@@ -413,7 +418,7 @@ static long get_precision(DBADDR *paddr, long *precision)
compressRecord *prec=(compressRecord *)paddr->precord;
*precision = prec->prec;
- if(paddr->pfield == (void *)prec->bptr) return(0);
+ if(dbGetFieldIndex(paddr) == indexof(BPTR)) return(0);
recGblGetPrec(paddr,precision);
return(0);
}
@@ -422,12 +427,16 @@ static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
{
compressRecord *prec=(compressRecord *)paddr->precord;
- if(paddr->pfield==(void *)prec->bptr
- || paddr->pfield==(void *)&prec->ihil
- || paddr->pfield==(void *)&prec->ilil){
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(BPTR):
+ case indexof(IHIL):
+ case indexof(ILIL):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
@@ -435,11 +444,15 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
compressRecord *prec=(compressRecord *)paddr->precord;
- if(paddr->pfield==(void *)prec->bptr
- || paddr->pfield==(void *)&prec->ihil
- || paddr->pfield==(void *)&prec->ilil){
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(BPTR):
+ case indexof(IHIL):
+ case indexof(ILIL):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
diff --git a/src/std/rec/dfanoutRecord.c b/src/std/rec/dfanoutRecord.c
index 672c1cccd..4628ab7a6 100644
--- a/src/std/rec/dfanoutRecord.c
+++ b/src/std/rec/dfanoutRecord.c
@@ -126,73 +126,72 @@ static long process(dfanoutRecord *prec)
return(status);
}
+#define indexof(field) dfanoutRecord##field
+
static long get_units(DBADDR *paddr,char *units)
{
dfanoutRecord *prec=(dfanoutRecord *)paddr->precord;
- strncpy(units,prec->egu,DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_DOUBLE) {
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return(0);
}
static long get_precision(DBADDR *paddr,long *precision)
{
dfanoutRecord *prec=(dfanoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == dfanoutRecordVAL
- || fieldIndex == dfanoutRecordHIHI
- || fieldIndex == dfanoutRecordHIGH
- || fieldIndex == dfanoutRecordLOW
- || fieldIndex == dfanoutRecordLOLO
- || fieldIndex == dfanoutRecordHOPR
- || fieldIndex == dfanoutRecordLOPR) {
- *precision = prec->prec;
- } else {
- recGblGetPrec(paddr,precision);
- }
+ *precision = prec->prec;
+ if (dbGetFieldIndex(paddr) == indexof(VAL)) return(0);
+ recGblGetPrec(paddr,precision);
return(0);
}
-static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
+static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
{
dfanoutRecord *prec=(dfanoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == dfanoutRecordVAL
- || fieldIndex == dfanoutRecordHIHI
- || fieldIndex == dfanoutRecordHIGH
- || fieldIndex == dfanoutRecordLOW
- || fieldIndex == dfanoutRecordLOLO
- || fieldIndex == dfanoutRecordHOPR
- || fieldIndex == dfanoutRecordLOPR) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
{
dfanoutRecord *prec=(dfanoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == dfanoutRecordVAL
- || fieldIndex == dfanoutRecordHIHI
- || fieldIndex == dfanoutRecordHIGH
- || fieldIndex == dfanoutRecordLOW
- || fieldIndex == dfanoutRecordLOLO) {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
{
dfanoutRecord *prec=(dfanoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
-
- if(fieldIndex == dfanoutRecordVAL) {
+ if(dbGetFieldIndex(paddr) == indexof(VAL)) {
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
diff --git a/src/std/rec/histogramRecord.c b/src/std/rec/histogramRecord.c
index e78be26c5..cb086d523 100644
--- a/src/std/rec/histogramRecord.c
+++ b/src/std/rec/histogramRecord.c
@@ -52,7 +52,7 @@ static long special(DBADDR *, int);
static long cvt_dbaddr(DBADDR *);
static long get_array_info(DBADDR *, long *, long *);
#define put_array_info NULL
-#define get_units NULL
+static long get_units(DBADDR *, char *);
static long get_precision(DBADDR *paddr,long *precision);
#define get_enum_str NULL
#define get_enum_strs NULL
@@ -83,6 +83,9 @@ rset histogramRSET={
};
epicsExportAddress(rset,histogramRSET);
+int histogramSDELprecision = 2;
+epicsExportAddress(int, histogramSDELprecision);
+
struct histogramdset { /* histogram input dset */
long number;
DEVSUPFUN dev_report;
@@ -386,42 +389,71 @@ static long readValue(histogramRecord *prec)
return(status);
}
+#define indexof(field) histogramRecord##field
+
+static long get_units(DBADDR *paddr, char *units)
+{
+ if (dbGetFieldIndex(paddr) == indexof(SDEL)) {
+ strcpy(units,"s");
+ }
+ /* We should have EGU for other DOUBLE values or probably get it from input link SVL */
+ return(0);
+}
+
static long get_precision(DBADDR *paddr,long *precision)
{
histogramRecord *prec=(histogramRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- *precision = prec->prec;
- if(fieldIndex == histogramRecordULIM
- || fieldIndex == histogramRecordLLIM
- || fieldIndex == histogramRecordSDEL
- || fieldIndex == histogramRecordSGNL
- || fieldIndex == histogramRecordSVAL
- || fieldIndex == histogramRecordWDTH) {
- *precision = prec->prec;
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(ULIM):
+ case indexof(LLIM):
+ case indexof(SGNL):
+ case indexof(SVAL):
+ case indexof(WDTH):
+ *precision = prec->prec;
+ break;
+ case indexof(SDEL):
+ *precision = histogramSDELprecision;
+ break;
+ default:
+ recGblGetPrec(paddr,precision);
}
- recGblGetPrec(paddr,precision);
return(0);
}
+
static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
{
histogramRecord *prec=(histogramRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == histogramRecordBPTR){
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(BPTR):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ case indexof(WDTH):
+ pgd->upper_disp_limit = prec->ulim-prec->llim;
+ pgd->lower_disp_limit = 0.0;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
{
histogramRecord *prec=(histogramRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == histogramRecordBPTR){
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(BPTR):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ case indexof(WDTH):
+ pcd->upper_ctrl_limit = prec->ulim-prec->llim;
+ pcd->lower_ctrl_limit = 0.0;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
diff --git a/src/std/rec/histogramRecord.dbd b/src/std/rec/histogramRecord.dbd
index 6def0f41a..dc0d2df31 100644
--- a/src/std/rec/histogramRecord.dbd
+++ b/src/std/rec/histogramRecord.dbd
@@ -135,3 +135,5 @@ recordtype(histogram) {
interest(1)
}
}
+
+variable(histogramSDELprecision, int)
diff --git a/src/std/rec/longinRecord.c b/src/std/rec/longinRecord.c
index 011b6c882..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 */
@@ -158,11 +162,15 @@ static long process(longinRecord *prec)
return(status);
}
+#define indexof(field) longinRecord##field
+
static long get_units(DBADDR *paddr,char *units)
{
longinRecord *prec=(longinRecord *)paddr->precord;
- strncpy(units,prec->egu,DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_LONG) {
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return(0);
}
@@ -171,14 +179,22 @@ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
longinRecord *prec=(longinRecord *)paddr->precord;
- if(paddr->pfield==(void *)&prec->val
- || paddr->pfield==(void *)&prec->hihi
- || paddr->pfield==(void *)&prec->high
- || paddr->pfield==(void *)&prec->low
- || paddr->pfield==(void *)&prec->lolo){
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ case indexof(SVAL):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
@@ -186,14 +202,22 @@ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
longinRecord *prec=(longinRecord *)paddr->precord;
- if(paddr->pfield==(void *)&prec->val
- || paddr->pfield==(void *)&prec->hihi
- || paddr->pfield==(void *)&prec->high
- || paddr->pfield==(void *)&prec->low
- || paddr->pfield==(void *)&prec->lolo){
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ case indexof(SVAL):
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
@@ -201,7 +225,7 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
{
longinRecord *prec=(longinRecord *)paddr->precord;
- if(paddr->pfield==(void *)&prec->val){
+ if(dbGetFieldIndex(paddr) == indexof(VAL)){
pad->upper_alarm_limit = prec->hihi;
pad->upper_warning_limit = prec->high;
pad->lower_warning_limit = prec->low;
@@ -210,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;
}
@@ -225,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/longoutRecord.c b/src/std/rec/longoutRecord.c
index d7d53d44f..ba1fa34b9 100644
--- a/src/std/rec/longoutRecord.c
+++ b/src/std/rec/longoutRecord.c
@@ -190,58 +190,73 @@ static long process(longoutRecord *prec)
return(status);
}
+#define indexof(field) longoutRecord##field
+
static long get_units(DBADDR *paddr,char *units)
{
longoutRecord *prec=(longoutRecord *)paddr->precord;
- strncpy(units,prec->egu,DB_UNITS_SIZE);
+ if(paddr->pfldDes->field_type == DBF_LONG) {
+ strncpy(units,prec->egu,DB_UNITS_SIZE);
+ }
return(0);
}
static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
{
longoutRecord *prec=(longoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
-
- if(fieldIndex == longoutRecordVAL
- || fieldIndex == longoutRecordHIHI
- || fieldIndex == longoutRecordHIGH
- || fieldIndex == longoutRecordLOW
- || fieldIndex == longoutRecordLOLO) {
- pgd->upper_disp_limit = prec->hopr;
- pgd->lower_disp_limit = prec->lopr;
- } else recGblGetGraphicDouble(paddr,pgd);
+
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ pgd->upper_disp_limit = prec->hopr;
+ pgd->lower_disp_limit = prec->lopr;
+ break;
+ default:
+ recGblGetGraphicDouble(paddr,pgd);
+ }
return(0);
}
-
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
{
longoutRecord *prec=(longoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == longoutRecordVAL
- || fieldIndex == longoutRecordHIHI
- || fieldIndex == longoutRecordHIGH
- || fieldIndex == longoutRecordLOW
- || fieldIndex == longoutRecordLOLO) {
- /* do not change pre drvh/drvl behavior */
- if(prec->drvh > prec->drvl) {
- pcd->upper_ctrl_limit = prec->drvh;
- pcd->lower_ctrl_limit = prec->drvl;
- } else {
- pcd->upper_ctrl_limit = prec->hopr;
- pcd->lower_ctrl_limit = prec->lopr;
- }
- } else recGblGetControlDouble(paddr,pcd);
+ switch (dbGetFieldIndex(paddr)) {
+ case indexof(VAL):
+ case indexof(HIHI):
+ case indexof(HIGH):
+ case indexof(LOW):
+ case indexof(LOLO):
+ case indexof(LALM):
+ case indexof(ALST):
+ case indexof(MLST):
+ /* do not change pre drvh/drvl behavior */
+ if(prec->drvh > prec->drvl) {
+ pcd->upper_ctrl_limit = prec->drvh;
+ pcd->lower_ctrl_limit = prec->drvl;
+ } else {
+ pcd->upper_ctrl_limit = prec->hopr;
+ pcd->lower_ctrl_limit = prec->lopr;
+ }
+ break;
+ default:
+ recGblGetControlDouble(paddr,pcd);
+ }
return(0);
}
+
static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
{
longoutRecord *prec=(longoutRecord *)paddr->precord;
- int fieldIndex = dbGetFieldIndex(paddr);
- if(fieldIndex == longoutRecordVAL) {
+ if(dbGetFieldIndex(paddr) == indexof(VAL)) {
pad->upper_alarm_limit = prec->hihi;
pad->upper_warning_limit = prec->high;
pad->lower_warning_limit = prec->low;
diff --git a/src/std/rec/mbbiRecord.c b/src/std/rec/mbbiRecord.c
index 6a1ef943d..3bdffe12e 100644
--- a/src/std/rec/mbbiRecord.c
+++ b/src/std/rec/mbbiRecord.c
@@ -1,6 +1,6 @@
/*************************************************************************\
* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie.
-* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
+* Copyright (c) 2012 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.
@@ -27,6 +27,7 @@
#include "dbEvent.h"
#include "dbFldTypes.h"
#include "devSup.h"
+#include "epicsMath.h"
#include "errMdef.h"
#include "menuSimm.h"
#include "recSup.h"
@@ -36,6 +37,10 @@
#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
@@ -54,56 +59,56 @@ static long put_enum_str(DBADDR *, char *);
#define get_graphic_double NULL
#define get_control_double NULL
#define get_alarm_double NULL
-rset mbbiRSET={
- 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
+
+rset mbbiRSET = {
+ 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,mbbiRSET);
struct mbbidset { /* multi bit binary input dset */
- long number;
- DEVSUPFUN dev_report;
- DEVSUPFUN init;
- DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/
- DEVSUPFUN get_ioint_info;
- DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/
+ long number;
+ DEVSUPFUN dev_report;
+ DEVSUPFUN init;
+ DEVSUPFUN init_record; /* returns: (-1,0) => (failure, success)*/
+ 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 *);
static void init_common(mbbiRecord *prec)
{
- epicsUInt32 *pstate_values;
- char *pstate_string;
- short i;
+ epicsUInt32 *pstate_values = &prec->zrvl;
+ char *pstate_string = prec->zrst;
+ int i;
- /* determine if any states are defined */
- pstate_values = &(prec->zrvl); pstate_string = prec->zrst;
- prec->sdef = FALSE;
- for (i=0; i<16; i++, pstate_string += sizeof(prec->zrst)) {
- if((*(pstate_values+i) != 0) || (*pstate_string !='\0')) {
- prec->sdef = TRUE;
- return;
- }
- }
- return;
+ /* Check if any states are defined */
+ for (i = 0; i < 16; i++, pstate_string += sizeof(prec->zrst)) {
+ if ((pstate_values[i] != 0) || (*pstate_string != '\0')) {
+ prec->sdef = TRUE;
+ return;
+ }
+ }
+ prec->sdef = FALSE;
}
static long init_record(mbbiRecord *prec, int pass)
@@ -111,258 +116,312 @@ static long init_record(mbbiRecord *prec, int pass)
struct mbbidset *pdset;
long status;
- if (pass==0) return(0);
+ if (pass == 0)
+ return 0;
if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
}
if (prec->siol.type == CONSTANT) {
- recGblInitConstantLink(&prec->siol,DBF_USHORT,&prec->sval);
+ recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval);
}
- if(!(pdset = (struct mbbidset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"mbbi: init_record");
- return(S_dev_noDSET);
+
+ pdset = (struct mbbidset *) prec->dset;
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "mbbi: init_record");
+ return S_dev_noDSET;
}
/* must have read_mbbi function defined */
- if( (pdset->number < 5) || (pdset->read_mbbi == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"mbbi: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->read_mbbi == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "mbbi: init_record");
+ return S_dev_missingSup;
}
- /* initialize mask*/
- prec->mask = (1 << prec->nobt) - 1;
- if( pdset->init_record ) {
- if((status=(*pdset->init_record)(prec))) return(status);
+ /* initialize mask if the user didn't */
+ if (prec->mask == 0)
+ prec->mask = (1 << prec->nobt) - 1;
+
+ if (pdset->init_record) {
+ status = pdset->init_record(prec);
}
+ else
+ status = 0;
+
init_common(prec);
prec->mlst = prec->val;
prec->lalm = prec->val;
prec->oraw = prec->rval;
- return(0);
+ return status;
}
static long process(mbbiRecord *prec)
{
- struct mbbidset *pdset = (struct mbbidset *)(prec->dset);
- long status;
- unsigned char pact=prec->pact;
+ struct mbbidset *pdset = (struct mbbidset *) prec->dset;
+ long status;
+ int pact = prec->pact;
+ epicsTimeStamp timeLast;
- if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) {
- prec->pact=TRUE;
- recGblRecordError(S_dev_missingSup,(void *)prec,"read_mbbi");
- return(S_dev_missingSup);
- }
+ if ((pdset == NULL) || (pdset->read_mbbi == NULL)) {
+ prec->pact = TRUE;
+ recGblRecordError(S_dev_missingSup, prec, "read_mbbi");
+ return S_dev_missingSup;
+ }
- status=readValue(prec); /* read the new value */
- /* check if device support set pact */
- if ( !pact && prec->pact ) return(0);
- prec->pact = TRUE;
+ timeLast = prec->time;
- recGblGetTimeStamp(prec);
- if(status==0) { /* convert the value */
- epicsUInt32 *pstate_values;
- short i;
- epicsUInt32 rval = prec->rval;
+ status = readValue(prec); /* read the new value */
+ /* check if device support set pact */
+ if (!pact && prec->pact)
+ return 0;
+ prec->pact = TRUE;
- prec->udf = FALSE;
- if(prec->shft>0) rval >>= prec->shft;
- if (prec->sdef){
- pstate_values = &(prec->zrvl);
- prec->val = 65535; /* initalize to unknown state*/
- for (i = 0; i < 16; i++){
- if (*pstate_values == rval){
- prec->val = i;
- break;
- }
- pstate_values++;
- }
- }else{
- /* the raw value is the desired value */
- prec->val = (unsigned short)rval;
- }
- }
- else if(status == 2) status = 0;
+ recGblGetTimeStamp(prec);
+ if (status == 0) { /* convert the value */
+ epicsUInt32 *pstate_values;
+ short i;
+ epicsUInt32 rval = prec->rval;
- /* check for alarms */
- checkAlarms(prec);
+ prec->udf = FALSE;
+ if (prec->shft > 0)
+ rval >>= prec->shft;
+ if (prec->sdef) {
+ pstate_values = &(prec->zrvl);
+ prec->val = 65535; /* initalize to unknown state*/
+ for (i = 0; i < 16; i++) {
+ if (*pstate_values == rval) {
+ prec->val = i;
+ break;
+ }
+ pstate_values++;
+ }
+ }
+ else {
+ /* the raw value is the desired value */
+ prec->val = rval;
+ }
+ }
+ else {
+ if (status == 2) {
+ status = 0;
+ }
+ }
- /* check event list */
- monitor(prec);
+ /* check for alarms */
+ checkAlarms(prec, &timeLast);
- /* process the forward scan link record */
- recGblFwdLink(prec);
+ /* check event list */
+ monitor(prec);
- prec->pact=FALSE;
- return(status);
+ /* process the forward scan link record */
+ recGblFwdLink(prec);
+
+ prec->pact=FALSE;
+ return status;
}
-static long special(DBADDR *paddr,int after)
+static long special(DBADDR *paddr, int after)
{
- mbbiRecord *prec = (mbbiRecord *)(paddr->precord);
- int special_type = paddr->special;
- int fieldIndex = dbGetFieldIndex(paddr);
+ mbbiRecord *prec = (mbbiRecord *) paddr->precord;
+ int special_type = paddr->special;
+ int fieldIndex = dbGetFieldIndex(paddr);
- if(!after) return(0);
- switch(special_type) {
- case(SPC_MOD):
- init_common(prec);
- if (fieldIndex >= mbbiRecordZRST && fieldIndex <= mbbiRecordFFST)
- db_post_events(prec,&prec->val,DBE_PROPERTY);
- return(0);
+ if (!after) return 0;
+ switch (special_type) {
+ case SPC_MOD:
+ init_common(prec);
+ if (fieldIndex >= mbbiRecordZRST && fieldIndex <= mbbiRecordFFST) {
+ int event = DBE_PROPERTY;
+
+ if (prec->val == fieldIndex - mbbiRecordZRST)
+ event |= DBE_VALUE | DBE_LOG;
+ db_post_events(prec, &prec->val, event);
+ }
+ return 0;
default:
- recGblDbaddrError(S_db_badChoice,paddr,"mbbi: special");
- return(S_db_badChoice);
+ recGblDbaddrError(S_db_badChoice, paddr, "mbbi: special");
+ return S_db_badChoice;
}
}
-static long get_enum_str(DBADDR *paddr,char* pstring)
+static long get_enum_str(DBADDR *paddr, char *pstring)
{
- mbbiRecord *prec=(mbbiRecord *)paddr->precord;
- char *psource;
- int index;
- unsigned short *pfield = (unsigned short *)paddr->pfield;
- unsigned short val=*pfield;
+ mbbiRecord *prec = (mbbiRecord *) paddr->precord;
+ int index;
+ unsigned short *pfield = paddr->pfield;
+ epicsEnum16 val = *pfield;
index = dbGetFieldIndex(paddr);
- if(index!=mbbiRecordVAL) {
- strcpy(pstring,"Illegal_Value");
- } else if(val<= 15) {
- psource = (prec->zrst);
- psource += (val * sizeof(prec->zrst));
- strncpy(pstring,psource,sizeof(prec->zrst));
- } else {
- strcpy(pstring,"Illegal Value");
+ if (index != mbbiRecordVAL) {
+ strcpy(pstring, "Illegal_Value");
}
- return(0);
+ else if (val <= 15) {
+ char *pstate = prec->zrst + val * sizeof(prec->zrst);
+
+ strncpy(pstring, pstate, sizeof(prec->zrst));
+ }
+ else {
+ strcpy(pstring, "Illegal Value");
+ }
+ return 0;
}
static long get_enum_strs(DBADDR *paddr, struct dbr_enumStrs *pes)
{
- mbbiRecord *prec=(mbbiRecord *)paddr->precord;
- char *psource;
- int i;
- short no_str;
+ mbbiRecord *prec = (mbbiRecord *) paddr->precord;
+ char *pstate = prec->zrst;
+ int i;
+ short states = 0;
- no_str = 0;
- memset(pes->strs,'\0',sizeof(pes->strs));
- for(i=0,psource=(prec->zrst); i<16; i++, psource += sizeof(prec->zrst) ) {
- strncpy(pes->strs[i],psource,sizeof(prec->zrst));
- if(*psource!=0) no_str=i+1;
+ memset(pes->strs, '\0', sizeof(pes->strs));
+ for (i = 0; i < 16; i++, pstate += sizeof(prec->zrst) ) {
+ strncpy(pes->strs[i], pstate, sizeof(prec->zrst));
+ if (*pstate!=0) states = i+1;
}
- pes->no_str=no_str;
- return(0);
+ pes->no_str = states;
+ return 0;
}
static long put_enum_str(DBADDR *paddr, char *pstring)
{
- mbbiRecord *prec=(mbbiRecord *)paddr->precord;
- char *pstate_name;
- short i;
+ mbbiRecord *prec = (mbbiRecord *) paddr->precord;
+ char *pstate;
+ short i;
- if (prec->sdef){
- pstate_name = prec->zrst;
- for (i = 0; i < 16; i++){
- if(strncmp(pstate_name,pstring,sizeof(prec->zrst))==0){
- prec->val = i;
- prec->udf = FALSE;
- return(0);
- }
- pstate_name += sizeof(prec->zrst);
- }
+ if (prec->sdef) {
+ pstate = prec->zrst;
+ for (i = 0; i < 16; i++) {
+ if (strncmp(pstate, pstring, sizeof(prec->zrst)) == 0) {
+ prec->val = i;
+ prec->udf = FALSE;
+ return 0;
+ }
+ pstate += sizeof(prec->zrst);
}
- return(S_db_badChoice);
+ }
+ return S_db_badChoice;
}
-static void checkAlarms(mbbiRecord *prec)
+static void checkAlarms(mbbiRecord *prec, epicsTimeStamp *timeLast)
{
- unsigned short *severities;
- unsigned short val=prec->val;
+ double aftc, afvl;
+ unsigned short alarm;
+ epicsEnum16 asev;
+ epicsEnum16 val = prec->val;
- /* check for udf alarm */
- if(prec->udf == TRUE ){
- recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM);
+ /* check for udf alarm */
+ if (prec->udf) {
+ recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
+ prec->afvl = 0;
+ return;
+ }
+
+ /* check for state alarm */
+ if (val > 15) {
+ /* Unknown state */
+ alarm = prec->unsv;
+ }
+ else {
+ /* State has a severity field */
+ epicsEnum16 *severities = &prec->zrsv;
+
+ 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);
- /* check for state alarm */
- /* unknown state */
- if (val > 15){
- recGblSetSevr(prec,STATE_ALARM,prec->unsv);
- } else {
- /* in a state which is an error */
- severities = (unsigned short *)&(prec->zrsv);
- recGblSetSevr(prec,STATE_ALARM,severities[prec->val]);
- }
+ afvl = alpha * afvl +
+ ((afvl > 0) ? (1.0 - alpha) : (alpha - 1.0)) * alarm;
+ if (afvl - floor(afvl) > THRESHOLD)
+ afvl = -afvl;
- /* check for cos alarm */
- if(val == prec->lalm) return;
- recGblSetSevr(prec,COS_ALARM,prec->cosv);
- prec->lalm = val;
- return;
+ 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);
+ prec->lalm = val;
}
static void monitor(mbbiRecord *prec)
{
- unsigned short monitor_mask;
+ unsigned short monitor_mask;
- monitor_mask = recGblResetAlarms(prec);
- /* check for value change */
- if (prec->mlst != prec->val){
- /* post events for value change and archive change */
- monitor_mask |= (DBE_VALUE | DBE_LOG);
- /* update last value monitored */
- prec->mlst = prec->val;
- }
- /* send out monitors connected to the value field */
- if (monitor_mask){
- db_post_events(prec,&prec->val,monitor_mask);
- }
- if(prec->oraw!=prec->rval) {
- db_post_events(prec,&prec->rval,monitor_mask|DBE_VALUE);
- prec->oraw = prec->rval;
- }
- return;
+ monitor_mask = recGblResetAlarms(prec);
+ /* check for value change */
+ if (prec->mlst != prec->val) {
+ /* post events for value change and archive change */
+ monitor_mask |= (DBE_VALUE | DBE_LOG);
+ /* update last value monitored */
+ prec->mlst = prec->val;
+ }
+ /* send out monitors connected to the value field */
+ if (monitor_mask) {
+ db_post_events(prec, &prec->val, monitor_mask);
+ }
+ if (prec->oraw != prec->rval) {
+ db_post_events(prec, &prec->rval, monitor_mask | DBE_VALUE);
+ prec->oraw = prec->rval;
+ }
}
static long readValue(mbbiRecord *prec)
{
- long status;
- struct mbbidset *pdset = (struct mbbidset *) (prec->dset);
+ struct mbbidset *pdset = (struct mbbidset *) prec->dset;
+ long status;
- if (prec->pact == TRUE){
- status=(*pdset->read_mbbi)(prec);
- return(status);
- }
+ if (prec->pact) {
+ status = pdset->read_mbbi(prec);
+ return status;
+ }
- status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0);
- if (status)
- return(status);
+ status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0);
+ if (status)
+ return status;
- if (prec->simm == menuSimmNO){
- status=(*pdset->read_mbbi)(prec);
- return(status);
- }
- if (prec->simm == menuSimmYES){
- status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0);
- if (status==0){
- prec->val=(unsigned short)prec->sval;
- prec->udf=FALSE;
- }
- status=2; /* dont convert */
- }
- else if (prec->simm == menuSimmRAW){
- status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0);
- if (status==0){
- prec->rval=prec->sval;
- prec->udf=FALSE;
- }
- status=0; /* convert since we've written RVAL */
- } else {
- status=-1;
- recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
- return(status);
- }
- recGblSetSevr(prec,SIMM_ALARM,prec->sims);
+ if (prec->simm == menuSimmNO) {
+ status = pdset->read_mbbi(prec);
+ return status;
+ }
+ if (prec->simm == menuSimmYES) {
+ status=dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0);
+ if (status == 0) {
+ prec->val = prec->sval;
+ prec->udf = FALSE;
+ }
+ status = 2; /* dont convert */
+ }
+ else if (prec->simm == menuSimmRAW) {
+ status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0);
+ if (status == 0) {
+ prec->rval = prec->sval;
+ prec->udf = FALSE;
+ }
+ status = 0; /* convert since we've written RVAL */
+ }
+ else {
+ status = -1;
+ recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
+ return status;
+ }
+ recGblSetSevr(prec, SIMM_ALARM, prec->sims);
- return(status);
+ return status;
}
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)
diff --git a/src/std/rec/mbboRecord.c b/src/std/rec/mbboRecord.c
index 5df97ff07..c2fc5e84c 100644
--- a/src/std/rec/mbboRecord.c
+++ b/src/std/rec/mbboRecord.c
@@ -1,6 +1,6 @@
/*************************************************************************\
* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie.
-* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
+* Copyright (c) 2012 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.
@@ -60,35 +60,35 @@ static long put_enum_str(DBADDR *, char *);
#define get_control_double NULL
#define get_alarm_double NULL
-rset mbboRSET={
- 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
+rset mbboRSET = {
+ 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,mbboRSET);
struct mbbodset { /* multi bit binary output dset */
- long number;
- DEVSUPFUN dev_report;
- DEVSUPFUN init;
- DEVSUPFUN init_record; /*returns: (0,2)=>(success,success no convert)*/
- DEVSUPFUN get_ioint_info;
- DEVSUPFUN write_mbbo; /*returns: (0,2)=>(success,success no convert)*/
+ long number;
+ DEVSUPFUN dev_report;
+ DEVSUPFUN init;
+ DEVSUPFUN init_record; /*returns: (0, 2) => (success, success no convert)*/
+ DEVSUPFUN get_ioint_info;
+ DEVSUPFUN write_mbbo; /*returns: (0, 2) => (success, success no convert)*/
};
@@ -100,360 +100,370 @@ static long writeValue(mbboRecord *);
static void init_common(mbboRecord *prec)
{
- epicsUInt32 *pstate_values;
- char *pstate_string;
- short i;
+ epicsUInt32 *pstate_values = &prec->zrvl;
+ char *pstate_string = prec->zrst;
+ int i;
- /* determine if any states are defined */
- pstate_values = &(prec->zrvl); pstate_string = prec->zrst;
- prec->sdef = FALSE;
- for (i=0; i<16; i++, pstate_string += sizeof(prec->zrst)) {
- if((*(pstate_values+i)!= 0) || (*pstate_string !='\0')) {
- prec->sdef = TRUE;
- return;
- }
+ /* Check if any states are defined */
+ for (i = 0; i < 16; i++, pstate_string += sizeof(prec->zrst)) {
+ if ((pstate_values[i] != 0) || (*pstate_string != '\0')) {
+ prec->sdef = TRUE;
+ return;
}
- return;
+ }
+ prec->sdef = FALSE;
}
static long init_record(mbboRecord *prec, int pass)
{
struct mbbodset *pdset;
long status;
- int i;
- if (pass==0) {
+ if (pass == 0) {
init_common(prec);
- return(0);
+ return 0;
}
- /* mbbo.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
}
- if(!(pdset = (struct mbbodset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"mbbo: init_record");
- return(S_dev_noDSET);
+ pdset = (struct mbbodset *) prec->dset;
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "mbbo: init_record");
+ return S_dev_noDSET;
}
/* must have write_mbbo function defined */
- if( (pdset->number < 5) || (pdset->write_mbbo == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"mbbo: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->write_mbbo == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "mbbo: init_record");
+ return S_dev_missingSup;
}
- if (prec->dol.type == CONSTANT){
- if(recGblInitConstantLink(&prec->dol,DBF_USHORT,&prec->val))
- prec->udf = FALSE;
+ if (prec->dol.type == CONSTANT) {
+ if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val))
+ prec->udf = FALSE;
}
- /* initialize mask*/
- prec->mask = 0;
- for (i=0; i