Merge lp:~khkim/epics-base/alarm-filter into lp:~epics-core/epics-base/3.14

Proposed by Kim, Kukhee
Status: Merged
Merge reported by: Andrew Johnson
Merged at revision: not available
Proposed branch: lp:~khkim/epics-base/alarm-filter
Merge into: lp:~epics-core/epics-base/3.14
Diff against target: 822 lines (+401/-136)
8 files modified
src/rec/aiRecord.c (+106/-46)
src/rec/aiRecord.dbd (+10/-0)
src/rec/calcRecord.c (+108/-44)
src/rec/calcRecord.dbd (+10/-0)
src/rec/longinRecord.c (+103/-42)
src/rec/longinRecord.dbd (+10/-0)
src/rec/mbbiRecord.c (+44/-4)
src/rec/mbbiRecord.dbd (+10/-0)
To merge this branch: bzr merge lp:~khkim/epics-base/alarm-filter
Reviewer Review Type Date Requested Status
Andrew Johnson Approve
Review via email: mp+26278@code.launchpad.net

Description of the change

This branch has alarm filter for ai, calc, longin, and mbbi record types.

To post a comment you must log in.
Revision history for this message
Ralph Lange (ralph-lange) wrote :

I can easily see this for the "numerical" calc and the longin records.
But what does the new behavior for the "enum" mbbi record mean? Suppressing spikes? How does that interact with the ACKT field?

Revision history for this message
Andrew Johnson (anj) wrote :

Merged into my record-updates branch which will go into 3.15 soon.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/rec/aiRecord.c'
--- src/rec/aiRecord.c 2010-04-05 18:49:18 +0000
+++ src/rec/aiRecord.c 2010-05-28 09:22:29 +0000
@@ -6,7 +6,7 @@
6* EPICS BASE is distributed subject to a Software License Agreement found6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution. 7* in file LICENSE that is included with this distribution.
8\*************************************************************************/8\*************************************************************************/
9/* $Id$ */9/* $Id: aiRecord.c,v 1.20.2.6 2009/04/03 14:40:12 lange Exp $ */
1010
11/* aiRecord.c - Record Support Routines for Analog Input records */11/* aiRecord.c - Record Support Routines for Analog Input records */
12/*12/*
@@ -41,6 +41,9 @@
41#undef GEN_SIZE_OFFSET41#undef GEN_SIZE_OFFSET
42#include "epicsExport.h"42#include "epicsExport.h"
4343
44/* Hysterisis for alarm filtering: 1-1/e */
45#define THRESHOLD 0.6321
46
44/* Create RSET - Record Support Entry Table*/47/* Create RSET - Record Support Entry Table*/
45#define report NULL48#define report NULL
46#define initialize NULL49#define initialize NULL
@@ -99,7 +102,7 @@
99extern unsigned int gts_trigger_counter;102extern unsigned int gts_trigger_counter;
100*/103*/
101104
102static void checkAlarms(aiRecord *prec);105static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime);
103static void convert(aiRecord *prec);106static void convert(aiRecord *prec);
104static void monitor(aiRecord *prec);107static void monitor(aiRecord *prec);
105static long readValue(aiRecord *prec);108static long readValue(aiRecord *prec);
@@ -158,12 +161,15 @@
158 aidset *pdset = (aidset *)(prec->dset);161 aidset *pdset = (aidset *)(prec->dset);
159 long status;162 long status;
160 unsigned char pact=prec->pact;163 unsigned char pact=prec->pact;
164 epicsTimeStamp timeLast;
161165
162 if( (pdset==NULL) || (pdset->read_ai==NULL) ) {166 if( (pdset==NULL) || (pdset->read_ai==NULL) ) {
163 prec->pact=TRUE;167 prec->pact=TRUE;
164 recGblRecordError(S_dev_missingSup,(void *)prec,"read_ai");168 recGblRecordError(S_dev_missingSup,(void *)prec,"read_ai");
165 return(S_dev_missingSup);169 return(S_dev_missingSup);
166 }170 }
171 timeLast = prec->time;
172
167 status=readValue(prec); /* read the new value */173 status=readValue(prec); /* read the new value */
168 /* check if device support set pact */174 /* check if device support set pact */
169 if ( !pact && prec->pact ) return(0);175 if ( !pact && prec->pact ) return(0);
@@ -174,7 +180,7 @@
174 else if (status==2) status=0;180 else if (status==2) status=0;
175181
176 /* check for alarms */182 /* check for alarms */
177 checkAlarms(prec);183 checkAlarms(prec,&timeLast);
178 /* check event list */184 /* check event list */
179 monitor(prec);185 monitor(prec);
180 /* process the forward scan link record */186 /* process the forward scan link record */
@@ -283,60 +289,114 @@
283 return(0);289 return(0);
284}290}
285291
286292
287static void checkAlarms(aiRecord *prec)293static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime)
288{294{
289 double val, hyst, lalm;295 enum {
290 double alev;296 range_Lolo = 1,
297 range_Low,
298 range_Normal,
299 range_High,
300 range_Hihi
301 } alarmRange;
302 static const epicsEnum16 range_stat[] = {
303 SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
304 NO_ALARM, HIGH_ALARM, HIHI_ALARM
305 };
306 double val, hyst, lalm, alev, aftc, afvl;
291 epicsEnum16 asev;307 epicsEnum16 asev;
292308
293 if (prec->udf) {309 if (prec->udf) {
294 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);310 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
311 prec->afvl = 0;
295 return;312 return;
296 }313 }
297314
298 val = prec->val;315 val = prec->val;
299 hyst = prec->hyst;316 hyst = prec->hyst;
300 lalm = prec->lalm;317 lalm = prec->lalm;
301318
302 /* alarm condition hihi */319 /* check VAL against alarm limits */
303 asev = prec->hhsv;320 if ((asev = prec->hhsv) &&
304 alev = prec->hihi;321 (val >= (alev = prec->hihi) ||
305 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {322 ((lalm == alev) && (val >= alev - hyst))))
306 if (recGblSetSevr(prec, HIHI_ALARM, asev))323 alarmRange = range_Hihi;
307 prec->lalm = alev;324 else
308 return;325 if ((asev = prec->llsv) &&
309 }326 (val <= (alev = prec->lolo) ||
310327 ((lalm == alev) && (val <= alev + hyst))))
311 /* alarm condition lolo */328 alarmRange = range_Lolo;
312 asev = prec->llsv;329 else
313 alev = prec->lolo;330 if ((asev = prec->hsv) &&
314 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {331 (val >= (alev = prec->high) ||
315 if (recGblSetSevr(prec, LOLO_ALARM, asev))332 ((lalm == alev) && (val >= alev - hyst))))
316 prec->lalm = alev;333 alarmRange = range_High;
317 return;334 else
318 }335 if ((asev = prec->lsv) &&
319336 (val <= (alev = prec->low) ||
320 /* alarm condition high */337 ((lalm == alev) && (val <= alev + hyst))))
321 asev = prec->hsv;338 alarmRange = range_Low;
322 alev = prec->high;339 else {
323 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {340 alev = val;
324 if (recGblSetSevr(prec, HIGH_ALARM, asev))341 asev = NO_ALARM;
325 prec->lalm = alev;342 alarmRange = range_Normal;
326 return;343 }
327 }344
328345 aftc = prec->aftc;
329 /* alarm condition low */346 afvl = 0;
330 asev = prec->lsv;347
331 alev = prec->low;348 if (aftc > 0) {
332 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {349 /* Apply level filtering */
333 if (recGblSetSevr(prec, LOW_ALARM, asev))350 afvl = prec->afvl;
334 prec->lalm = alev;351 if (afvl == 0) {
335 return;352 afvl = (double)alarmRange;
336 }353 } else {
337354 double t = epicsTimeDiffInSeconds(&prec->time, lastTime);
338 /* we get here only if val is out of alarm by at least hyst */355 double alpha = aftc / (t + aftc);
339 prec->lalm = val;356
340 return;357 /* The sign of afvl indicates whether the result should be
358 * rounded up or down. This gives the filter hysteresis.
359 * If afvl > 0 the floor() function rounds to a lower alarm
360 * level, otherwise to a higher.
361 */
362 afvl = alpha * afvl +
363 ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
364 if (afvl - floor(afvl) > THRESHOLD)
365 afvl = -afvl; /* reverse rounding */
366
367 alarmRange = abs((int)floor(afvl));
368 switch (alarmRange) {
369 case range_Hihi:
370 asev = prec->hhsv;
371 alev = prec->hihi;
372 break;
373 case range_High:
374 asev = prec->hsv;
375 alev = prec->high;
376 break;
377 case range_Normal:
378 asev = NO_ALARM;
379 break;
380 case range_Low:
381 asev = prec->lsv;
382 alev = prec->low;
383 break;
384 case range_Lolo:
385 asev = prec->llsv;
386 alev = prec->lolo;
387 break;
388 }
389 }
390 }
391 prec->afvl = afvl;
392
393 if (asev) {
394 /* Report alarm condition, store LALM for future HYST calculations */
395 if (recGblSetSevr(prec, range_stat[alarmRange], asev))
396 prec->lalm = alev;
397 } else {
398 /* No alarm condition, reset LALM */
399 prec->lalm = val;
400 }
341}401}
342402
343403
344static void convert(aiRecord *prec)404static void convert(aiRecord *prec)
345405
=== modified file 'src/rec/aiRecord.dbd'
--- src/rec/aiRecord.dbd 2005-11-15 23:35:34 +0000
+++ src/rec/aiRecord.dbd 2010-05-28 09:22:29 +0000
@@ -138,6 +138,11 @@
138 promptgroup(GUI_ALARMS)138 promptgroup(GUI_ALARMS)
139 interest(1)139 interest(1)
140 }140 }
141 field(AFTC,DBF_DOUBLE) {
142 prompt("Alarm Filter Time Constant")
143 promptgroup(GUI_ALARMS)
144 interest(1)
145 }
141 field(ADEL,DBF_DOUBLE) {146 field(ADEL,DBF_DOUBLE) {
142 prompt("Archive Deadband")147 prompt("Archive Deadband")
143 promptgroup(GUI_DISPLAY)148 promptgroup(GUI_DISPLAY)
@@ -153,6 +158,11 @@
153 special(SPC_NOMOD)158 special(SPC_NOMOD)
154 interest(3)159 interest(3)
155 }160 }
161 field(AFVL,DBF_DOUBLE) {
162 prompt("Alarm Filter Value")
163 special(SPC_NOMOD)
164 interest(3)
165 }
156 field(ALST,DBF_DOUBLE) {166 field(ALST,DBF_DOUBLE) {
157 prompt("Last Value Archived")167 prompt("Last Value Archived")
158 special(SPC_NOMOD)168 special(SPC_NOMOD)
159169
=== modified file 'src/rec/calcRecord.c'
--- src/rec/calcRecord.c 2009-04-03 14:40:13 +0000
+++ src/rec/calcRecord.c 2010-05-28 09:22:29 +0000
@@ -37,6 +37,9 @@
37#undef GEN_SIZE_OFFSET37#undef GEN_SIZE_OFFSET
38#include "epicsExport.h"38#include "epicsExport.h"
3939
40/* Hysterisis for alarm filtering: 1-1/e */
41#define THRESHOLD 0.6321
42
40/* Create RSET - Record Support Entry Table */43/* Create RSET - Record Support Entry Table */
4144
42#define report NULL45#define report NULL
@@ -79,7 +82,7 @@
79};82};
80epicsExportAddress(rset, calcRSET);83epicsExportAddress(rset, calcRSET);
8184
82static void checkAlarms(calcRecord *prec);85static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast);
83static void monitor(calcRecord *prec);86static void monitor(calcRecord *prec);
84static int fetch_values(calcRecord *prec);87static int fetch_values(calcRecord *prec);
8588
@@ -111,15 +114,19 @@
111114
112static long process(calcRecord *prec)115static long process(calcRecord *prec)
113{116{
117 epicsTimeStamp timeLast;
118
114 prec->pact = TRUE;119 prec->pact = TRUE;
115 if (fetch_values(prec)==0) {120 if (fetch_values(prec)==0) {
116 if (calcPerform(&prec->a, &prec->val, prec->rpcl)) {121 if (calcPerform(&prec->a, &prec->val, prec->rpcl)) {
117 recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);122 recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
118 } else prec->udf = isnan(prec->val);123 } else prec->udf = isnan(prec->val);
119 }124 }
125
126 timeLast = prec->time;
120 recGblGetTimeStamp(prec);127 recGblGetTimeStamp(prec);
121 /* check for alarms */128 /* check for alarms */
122 checkAlarms(prec);129 checkAlarms(prec, &timeLast);
123 /* check event list */130 /* check event list */
124 monitor(prec);131 monitor(prec);
125 /* process the forward scan link record */132 /* process the forward scan link record */
@@ -243,14 +250,27 @@
243 return 0;250 return 0;
244}251}
245252
246static void checkAlarms(calcRecord *prec)253static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast)
247{254{
248 double val, hyst, lalm;255
249 double alev;256 enum {
257 range_Lolo = 1,
258 range_Low,
259 range_Normal,
260 range_High,
261 range_Hihi
262 } alarmRange;
263 static const epicsEnum16 range_stat[] = {
264 SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
265 NO_ALARM, HIGH_ALARM, HIHI_ALARM
266 };
267
268 double val, hyst, lalm, alev, aftc, afvl;
250 epicsEnum16 asev;269 epicsEnum16 asev;
251270
252 if (prec->udf) {271 if (prec->udf) {
253 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);272 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
273 prec->afvl = 0;
254 return;274 return;
255 }275 }
256276
@@ -258,45 +278,89 @@
258 hyst = prec->hyst;278 hyst = prec->hyst;
259 lalm = prec->lalm;279 lalm = prec->lalm;
260280
261 /* alarm condition hihi */281 /* check VAL against alarm limits */
262 asev = prec->hhsv;282 if ((asev = prec->hhsv) &&
263 alev = prec->hihi;283 (val >= (alev = prec->hihi) ||
264 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {284 ((lalm == alev) && (val >= alev - hyst))))
265 if (recGblSetSevr(prec, HIHI_ALARM, asev))285 alarmRange = range_Hihi;
266 prec->lalm = alev;286 else
267 return;287 if ((asev = prec->llsv) &&
268 }288 (val <= (alev = prec->lolo) ||
269289 ((lalm == alev) && (val <= alev + hyst))))
270 /* alarm condition lolo */290 alarmRange = range_Lolo;
271 asev = prec->llsv;291 else
272 alev = prec->lolo;292 if ((asev = prec->hsv) &&
273 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {293 (val >= (alev = prec->high) ||
274 if (recGblSetSevr(prec, LOLO_ALARM, asev))294 ((lalm == alev) && (val >= alev - hyst))))
275 prec->lalm = alev;295 alarmRange = range_High;
276 return;296 else
277 }297 if ((asev = prec->lsv) &&
278298 (val <= (alev = prec->low) ||
279 /* alarm condition high */299 ((lalm == alev) && (val <= alev + hyst))))
280 asev = prec->hsv;300 alarmRange = range_Low;
281 alev = prec->high;301 else {
282 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {302 alev = val;
283 if (recGblSetSevr(prec, HIGH_ALARM, asev))303 asev = NO_ALARM;
284 prec->lalm = alev;304 alarmRange = range_Normal;
285 return;305 }
286 }306
287307 aftc = prec->aftc;
288 /* alarm condition low */308 afvl = 0;
289 asev = prec->lsv;309
290 alev = prec->low;310 if (aftc > 0) {
291 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {311 /* Apply level filtering */
292 if (recGblSetSevr(prec, LOW_ALARM, asev))312 afvl = prec->afvl;
293 prec->lalm = alev;313 if (afvl == 0) {
294 return;314 afvl = (double)alarmRange;
295 }315 } else {
296316 double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
297 /* we get here only if val is out of alarm by at least hyst */317 double alpha = aftc / (t + aftc);
298 prec->lalm = val;318
299 return;319 /* The sign of afvl indicates whether the result should be
320 * rounded up or down. This gives the filter hysteresis.
321 * If afvl > 0 the floor() function rounds to a lower alarm
322 * level, otherwise to a higher.
323 */
324 afvl = alpha * afvl +
325 ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
326 if (afvl - floor(afvl) > THRESHOLD)
327 afvl = -afvl; /* reverse rounding */
328
329 alarmRange = abs((int)floor(afvl));
330 switch (alarmRange) {
331 case range_Hihi:
332 asev = prec->hhsv;
333 alev = prec->hihi;
334 break;
335 case range_High:
336 asev = prec->hsv;
337 alev = prec->high;
338 break;
339 case range_Normal:
340 asev = NO_ALARM;
341 break;
342 case range_Low:
343 asev = prec->lsv;
344 alev = prec->low;
345 break;
346 case range_Lolo:
347 asev = prec->llsv;
348 alev = prec->lolo;
349 break;
350 }
351 }
352 }
353 prec->afvl = afvl;
354
355 if (asev) {
356 /* Report alarm condition, store LALM for future HYST calculations */
357 if (recGblSetSevr(prec, range_stat[alarmRange], asev))
358 prec->lalm = alev;
359 } else {
360 /* No alarm condition, reset LALM */
361 prec->lalm = val;
362 }
363
300}364}
301365
302static void monitor(calcRecord *prec)366static void monitor(calcRecord *prec)
303367
=== modified file 'src/rec/calcRecord.dbd'
--- src/rec/calcRecord.dbd 2007-03-13 16:39:53 +0000
+++ src/rec/calcRecord.dbd 2010-05-28 09:22:29 +0000
@@ -153,6 +153,16 @@
153 interest(1)153 interest(1)
154 menu(menuAlarmSevr)154 menu(menuAlarmSevr)
155 }155 }
156 field(AFTC, DBF_DOUBLE) {
157 prompt("Alarm Filter Time Constant")
158 promptgroup(GUI_ALARMS)
159 interest(1)
160 }
161 field(AFVL, DBF_DOUBLE) {
162 prompt("Alarm Filter Value")
163 special(SPC_NOMOD)
164 interest(3)
165 }
156 field(HYST,DBF_DOUBLE) {166 field(HYST,DBF_DOUBLE) {
157 prompt("Alarm Deadband")167 prompt("Alarm Deadband")
158 promptgroup(GUI_ALARMS)168 promptgroup(GUI_ALARMS)
159169
=== modified file 'src/rec/longinRecord.c'
--- src/rec/longinRecord.c 2010-04-05 18:49:18 +0000
+++ src/rec/longinRecord.c 2010-05-28 09:22:29 +0000
@@ -37,6 +37,8 @@
37#undef GEN_SIZE_OFFSET37#undef GEN_SIZE_OFFSET
38#include "epicsExport.h"38#include "epicsExport.h"
3939
4040
41
41/* Hysterisis for alarm filtering: 1-1/e */42/* Hysterisis for alarm filtering: 1-1/e */
43#define THRESHOLD 0.6321
42/* Create RSET - Record Support Entry Table*/44/* Create RSET - Record Support Entry Table*/
43#define report NULL45#define report NULL
44#define initialize NULL46#define initialize NULL
@@ -87,7 +89,7 @@
87 DEVSUPFUN get_ioint_info;89 DEVSUPFUN get_ioint_info;
88 DEVSUPFUN read_longin; /*returns: (-1,0)=>(failure,success)*/90 DEVSUPFUN read_longin; /*returns: (-1,0)=>(failure,success)*/
89};91};
90static void checkAlarms(longinRecord *prec);92static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast);
91static void monitor(longinRecord *prec);93static void monitor(longinRecord *prec);
92static long readValue(longinRecord *prec);94static long readValue(longinRecord *prec);
9395
@@ -132,12 +134,14 @@
132 struct longindset *pdset = (struct longindset *)(prec->dset);134 struct longindset *pdset = (struct longindset *)(prec->dset);
133 long status;135 long status;
134 unsigned char pact=prec->pact;136 unsigned char pact=prec->pact;
137 epicsTimeStamp timeLast;
135138
136 if( (pdset==NULL) || (pdset->read_longin==NULL) ) {139 if( (pdset==NULL) || (pdset->read_longin==NULL) ) {
137 prec->pact=TRUE;140 prec->pact=TRUE;
138 recGblRecordError(S_dev_missingSup,(void *)prec,"read_longin");141 recGblRecordError(S_dev_missingSup,(void *)prec,"read_longin");
139 return(S_dev_missingSup);142 return(S_dev_missingSup);
140 }143 }
144 timeLast = prec->time;
141145
142 status=readValue(prec); /* read the new value */146 status=readValue(prec); /* read the new value */
143 /* check if device support set pact */147 /* check if device support set pact */
@@ -148,7 +152,7 @@
148 if (status==0) prec->udf = FALSE;152 if (status==0) prec->udf = FALSE;
149153
150 /* check for alarms */154 /* check for alarms */
151 checkAlarms(prec);155 checkAlarms(prec, &timeLast);
152 /* check event list */156 /* check event list */
153 monitor(prec);157 monitor(prec);
154 /* process the forward scan link record */158 /* process the forward scan link record */
@@ -210,14 +214,28 @@
210 return(0);214 return(0);
211}215}
212216
213217
214static void checkAlarms(longinRecord *prec)218static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast)
215{219{
220 enum {
221 range_Lolo = 1,
222 range_Low,
223 range_Normal,
224 range_High,
225 range_Hihi
226 } alarmRange;
227 static const epicsEnum16 range_stat[] = {
228 SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
229 NO_ALARM, HIGH_ALARM, HIHI_ALARM
230 };
231
232 double aftc, afvl;
216 epicsInt32 val, hyst, lalm;233 epicsInt32 val, hyst, lalm;
217 epicsInt32 alev;234 epicsInt32 alev;
218 epicsEnum16 asev;235 epicsEnum16 asev;
219236
220 if (prec->udf) {237 if (prec->udf) {
221 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);238 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
239 prec->afvl = 0;
222 return;240 return;
223 }241 }
224242
@@ -225,45 +243,88 @@
225 hyst = prec->hyst;243 hyst = prec->hyst;
226 lalm = prec->lalm;244 lalm = prec->lalm;
227245
228 /* alarm condition hihi */246 /* check VAL against alarm limits */
229 asev = prec->hhsv;247 if ((asev = prec->hhsv) &&
230 alev = prec->hihi;248 (val >= (alev = prec->hihi) ||
231 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {249 ((lalm == alev) && (val >= alev - hyst))))
232 if (recGblSetSevr(prec, HIHI_ALARM, asev))250 alarmRange = range_Hihi;
233 prec->lalm = alev;251 else
234 return;252 if ((asev = prec->llsv) &&
235 }253 (val <= (alev = prec->lolo) ||
236254 ((lalm == alev) && (val <= alev + hyst))))
237 /* alarm condition lolo */255 alarmRange = range_Lolo;
238 asev = prec->llsv;256 else
239 alev = prec->lolo;257 if ((asev = prec->hsv) &&
240 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {258 (val >= (alev = prec->high) ||
241 if (recGblSetSevr(prec, LOLO_ALARM, asev))259 ((lalm == alev) && (val >= alev - hyst))))
242 prec->lalm = alev;260 alarmRange = range_High;
243 return;261 else
244 }262 if ((asev = prec->lsv) &&
245263 (val <= (alev = prec->low) ||
246 /* alarm condition high */264 ((lalm == alev) && (val <= alev + hyst))))
247 asev = prec->hsv;265 alarmRange = range_Low;
248 alev = prec->high;266 else {
249 if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {267 alev = val;
250 if (recGblSetSevr(prec, HIGH_ALARM, asev))268 asev = NO_ALARM;
251 prec->lalm = alev;269 alarmRange = range_Normal;
252 return;270 }
253 }271
254272 aftc = prec->aftc;
255 /* alarm condition low */273 afvl = 0;
256 asev = prec->lsv;274
257 alev = prec->low;275 if (aftc > 0) {
258 if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {276 /* Apply level filtering */
259 if (recGblSetSevr(prec, LOW_ALARM, asev))277 afvl = prec->afvl;
260 prec->lalm = alev;278 if (afvl == 0) {
261 return;279 afvl = (double)alarmRange;
262 }280 } else {
263281 double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
264 /* we get here only if val is out of alarm by at least hyst */282 double alpha = aftc / (t + aftc);
265 prec->lalm = val;283
266 return;284 /* The sign of afvl indicates whether the result should be
285 * rounded up or down. This gives the filter hysteresis.
286 * If afvl > 0 the floor() function rounds to a lower alarm
287 * level, otherwise to a higher.
288 */
289 afvl = alpha * afvl +
290 ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
291 if (afvl - floor(afvl) > THRESHOLD)
292 afvl = -afvl; /* reverse rounding */
293
294 alarmRange = abs((int)floor(afvl));
295 switch (alarmRange) {
296 case range_Hihi:
297 asev = prec->hhsv;
298 alev = prec->hihi;
299 break;
300 case range_High:
301 asev = prec->hsv;
302 alev = prec->high;
303 break;
304 case range_Normal:
305 asev = NO_ALARM;
306 break;
307 case range_Low:
308 asev = prec->lsv;
309 alev = prec->low;
310 break;
311 case range_Lolo:
312 asev = prec->llsv;
313 alev = prec->lolo;
314 break;
315 }
316 }
317 }
318 prec->afvl = afvl;
319
320 if (asev) {
321 /* Report alarm condition, store LALM for future HYST calculations */
322 if (recGblSetSevr(prec, range_stat[alarmRange], asev))
323 prec->lalm = alev;
324 } else {
325 /* No alarm condition, reset LALM */
326 prec->lalm = val;
327 }
267}328}
268329
269330
270/* DELTA calculates the absolute difference between its arguments331/* DELTA calculates the absolute difference between its arguments
271332
=== modified file 'src/rec/longinRecord.dbd'
--- src/rec/longinRecord.dbd 2002-07-12 21:35:43 +0000
+++ src/rec/longinRecord.dbd 2010-05-28 09:22:29 +0000
@@ -93,6 +93,16 @@
93 promptgroup(GUI_ALARMS)93 promptgroup(GUI_ALARMS)
94 interest(1)94 interest(1)
95 }95 }
96 field(AFTC, DBF_DOUBLE) {
97 prompt("Alarm Filter Time Constant")
98 promptgroup(GUI_ALARMS)
99 interest(1)
100 }
101 field(AFVL, DBF_DOUBLE) {
102 prompt("Alarm Filter Value")
103 special(SPC_NOMOD)
104 interest(3)
105 }
96 field(ADEL,DBF_LONG) {106 field(ADEL,DBF_LONG) {
97 prompt("Archive Deadband")107 prompt("Archive Deadband")
98 promptgroup(GUI_DISPLAY)108 promptgroup(GUI_DISPLAY)
99109
=== modified file 'src/rec/mbbiRecord.c'
--- src/rec/mbbiRecord.c 2010-04-05 18:49:18 +0000
+++ src/rec/mbbiRecord.c 2010-05-28 09:22:29 +0000
@@ -36,6 +36,9 @@
36#include "mbbiRecord.h"36#include "mbbiRecord.h"
37#undef GEN_SIZE_OFFSET37#undef GEN_SIZE_OFFSET
38#include "epicsExport.h"38#include "epicsExport.h"
39
40/* Hysterisis for alarm filtering: 1-1/e */
41#define THRESHOLD 0.6321
39/* Create RSET - Record Support Entry Table*/42/* Create RSET - Record Support Entry Table*/
40#define report NULL43#define report NULL
41#define initialize NULL44#define initialize NULL
@@ -84,7 +87,7 @@
84 DEVSUPFUN get_ioint_info;87 DEVSUPFUN get_ioint_info;
85 DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/88 DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/
86};89};
87static void checkAlarms(mbbiRecord *);90static void checkAlarms(mbbiRecord *, epicsTimeStamp *);
88static void monitor(mbbiRecord *);91static void monitor(mbbiRecord *);
89static long readValue(mbbiRecord *);92static long readValue(mbbiRecord *);
9093
9194
@@ -146,6 +149,7 @@
146 struct mbbidset *pdset = (struct mbbidset *)(prec->dset);149 struct mbbidset *pdset = (struct mbbidset *)(prec->dset);
147 long status;150 long status;
148 unsigned char pact=prec->pact;151 unsigned char pact=prec->pact;
152 epicsTimeStamp timeLast;
149153
150 if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) {154 if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) {
151 prec->pact=TRUE;155 prec->pact=TRUE;
@@ -153,6 +157,8 @@
153 return(S_dev_missingSup);157 return(S_dev_missingSup);
154 }158 }
155159
160 timeLast = prec->time;
161
156 status=readValue(prec); /* read the new value */162 status=readValue(prec); /* read the new value */
157 /* check if device support set pact */163 /* check if device support set pact */
158 if ( !pact && prec->pact ) return(0);164 if ( !pact && prec->pact ) return(0);
@@ -184,7 +190,7 @@
184 else if(status == 2) status = 0;190 else if(status == 2) status = 0;
185191
186 /* check for alarms */192 /* check for alarms */
187 checkAlarms(prec);193 checkAlarms(prec, &timeLast);
188194
189 /* check event list */195 /* check event list */
190 monitor(prec);196 monitor(prec);
@@ -274,14 +280,20 @@
274 return(S_db_badChoice);280 return(S_db_badChoice);
275}281}
276282
277static void checkAlarms(mbbiRecord *prec)283static void checkAlarms(mbbiRecord *prec, epicsTimeStamp *timeLast)
278{284{
285
286 double aftc, afvl;
287
288 unsigned short alarm;
289 epicsEnum16 asev;
279 unsigned short *severities;290 unsigned short *severities;
280 unsigned short val=prec->val;291 unsigned short val=prec->val;
281292
282 /* check for udf alarm */293 /* check for udf alarm */
283 if(prec->udf == TRUE ){294 if(prec->udf == TRUE ){
284 recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM);295 recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM);
296 prec->afvl = 0;
285 }297 }
286298
287 /* check for state alarm */299 /* check for state alarm */
@@ -291,8 +303,36 @@
291 } else {303 } else {
292 /* in a state which is an error */304 /* in a state which is an error */
293 severities = (unsigned short *)&(prec->zrsv);305 severities = (unsigned short *)&(prec->zrsv);
306 /*
294 recGblSetSevr(prec,STATE_ALARM,severities[prec->val]);307 recGblSetSevr(prec,STATE_ALARM,severities[prec->val]);
295 }308 */
309 alarm = severities[prec->val];
310
311 }
312
313 aftc = prec->aftc;
314 afvl = 0.;
315
316 if(aftc > 0.) {
317 afvl = prec->afvl;
318 if(afvl == 0.) {
319 afvl = (double) alarm;
320 } else {
321 double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
322 double alpha = aftc / (t + aftc);
323
324 afvl = alpha * afvl +
325 ((afvl>0.)?(1.-alpha):(alpha-1.)) * alarm;
326 if(afvl - floor(afvl) > THRESHOLD)
327 afvl = -afvl;
328
329 alarm = abs((int)floor(afvl));
330 }
331 }
332
333
334 asev = alarm;
335 recGblSetSevr(prec, STATE_ALARM, asev);
296336
297 /* check for cos alarm */337 /* check for cos alarm */
298 if(val == prec->lalm) return;338 if(val == prec->lalm) return;
299339
=== modified file 'src/rec/mbbiRecord.dbd'
--- src/rec/mbbiRecord.dbd 2009-06-08 19:55:49 +0000
+++ src/rec/mbbiRecord.dbd 2010-05-28 09:22:29 +0000
@@ -394,6 +394,16 @@
394 interest(1)394 interest(1)
395 menu(menuAlarmSevr)395 menu(menuAlarmSevr)
396 }396 }
397 field(AFTC, DBF_DOUBLE) {
398 prompt("Alarm Filter Time Constant")
399 promptgroup(GUI_ALARMS)
400 interest(1)
401 }
402 field(AFVL, DBF_DOUBLE) {
403 prompt("Alarm Filter Value")
404 special(SPC_NOMOD)
405 interest(3)
406 }
397 field(UNSV,DBF_MENU) {407 field(UNSV,DBF_MENU) {
398 prompt("Unknown State Severity")408 prompt("Unknown State Severity")
399 promptgroup(GUI_MBB)409 promptgroup(GUI_MBB)

Subscribers

People subscribed via source and target branches