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
1=== modified file 'src/rec/aiRecord.c'
2--- src/rec/aiRecord.c 2010-04-05 18:49:18 +0000
3+++ src/rec/aiRecord.c 2010-05-28 09:22:29 +0000
4@@ -6,7 +6,7 @@
5 * EPICS BASE is distributed subject to a Software License Agreement found
6 * in file LICENSE that is included with this distribution.
7 \*************************************************************************/
8-/* $Id$ */
9+/* $Id: aiRecord.c,v 1.20.2.6 2009/04/03 14:40:12 lange Exp $ */
10
11 /* aiRecord.c - Record Support Routines for Analog Input records */
12 /*
13@@ -41,6 +41,9 @@
14 #undef GEN_SIZE_OFFSET
15 #include "epicsExport.h"
16
17+/* Hysterisis for alarm filtering: 1-1/e */
18+#define THRESHOLD 0.6321
19+
20 /* Create RSET - Record Support Entry Table*/
21 #define report NULL
22 #define initialize NULL
23@@ -99,7 +102,7 @@
24 extern unsigned int gts_trigger_counter;
25 */
26
27-static void checkAlarms(aiRecord *prec);
28+static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime);
29 static void convert(aiRecord *prec);
30 static void monitor(aiRecord *prec);
31 static long readValue(aiRecord *prec);
32@@ -158,12 +161,15 @@
33 aidset *pdset = (aidset *)(prec->dset);
34 long status;
35 unsigned char pact=prec->pact;
36+ epicsTimeStamp timeLast;
37
38 if( (pdset==NULL) || (pdset->read_ai==NULL) ) {
39 prec->pact=TRUE;
40 recGblRecordError(S_dev_missingSup,(void *)prec,"read_ai");
41 return(S_dev_missingSup);
42 }
43+ timeLast = prec->time;
44+
45 status=readValue(prec); /* read the new value */
46 /* check if device support set pact */
47 if ( !pact && prec->pact ) return(0);
48@@ -174,7 +180,7 @@
49 else if (status==2) status=0;
50
51 /* check for alarms */
52- checkAlarms(prec);
53+ checkAlarms(prec,&timeLast);
54 /* check event list */
55 monitor(prec);
56 /* process the forward scan link record */
57@@ -283,60 +289,114 @@
58 return(0);
59 }
60
61
62-static void checkAlarms(aiRecord *prec)
63+static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime)
64 {
65- double val, hyst, lalm;
66- double alev;
67+ enum {
68+ range_Lolo = 1,
69+ range_Low,
70+ range_Normal,
71+ range_High,
72+ range_Hihi
73+ } alarmRange;
74+ static const epicsEnum16 range_stat[] = {
75+ SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
76+ NO_ALARM, HIGH_ALARM, HIHI_ALARM
77+ };
78+ double val, hyst, lalm, alev, aftc, afvl;
79 epicsEnum16 asev;
80
81 if (prec->udf) {
82 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
83+ prec->afvl = 0;
84 return;
85 }
86
87- val = prec->val;
88+ val = prec->val;
89 hyst = prec->hyst;
90 lalm = prec->lalm;
91
92- /* alarm condition hihi */
93- asev = prec->hhsv;
94- alev = prec->hihi;
95- if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
96- if (recGblSetSevr(prec, HIHI_ALARM, asev))
97- prec->lalm = alev;
98- return;
99- }
100-
101- /* alarm condition lolo */
102- asev = prec->llsv;
103- alev = prec->lolo;
104- if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
105- if (recGblSetSevr(prec, LOLO_ALARM, asev))
106- prec->lalm = alev;
107- return;
108- }
109-
110- /* alarm condition high */
111- asev = prec->hsv;
112- alev = prec->high;
113- if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
114- if (recGblSetSevr(prec, HIGH_ALARM, asev))
115- prec->lalm = alev;
116- return;
117- }
118-
119- /* alarm condition low */
120- asev = prec->lsv;
121- alev = prec->low;
122- if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
123- if (recGblSetSevr(prec, LOW_ALARM, asev))
124- prec->lalm = alev;
125- return;
126- }
127-
128- /* we get here only if val is out of alarm by at least hyst */
129- prec->lalm = val;
130- return;
131+ /* check VAL against alarm limits */
132+ if ((asev = prec->hhsv) &&
133+ (val >= (alev = prec->hihi) ||
134+ ((lalm == alev) && (val >= alev - hyst))))
135+ alarmRange = range_Hihi;
136+ else
137+ if ((asev = prec->llsv) &&
138+ (val <= (alev = prec->lolo) ||
139+ ((lalm == alev) && (val <= alev + hyst))))
140+ alarmRange = range_Lolo;
141+ else
142+ if ((asev = prec->hsv) &&
143+ (val >= (alev = prec->high) ||
144+ ((lalm == alev) && (val >= alev - hyst))))
145+ alarmRange = range_High;
146+ else
147+ if ((asev = prec->lsv) &&
148+ (val <= (alev = prec->low) ||
149+ ((lalm == alev) && (val <= alev + hyst))))
150+ alarmRange = range_Low;
151+ else {
152+ alev = val;
153+ asev = NO_ALARM;
154+ alarmRange = range_Normal;
155+ }
156+
157+ aftc = prec->aftc;
158+ afvl = 0;
159+
160+ if (aftc > 0) {
161+ /* Apply level filtering */
162+ afvl = prec->afvl;
163+ if (afvl == 0) {
164+ afvl = (double)alarmRange;
165+ } else {
166+ double t = epicsTimeDiffInSeconds(&prec->time, lastTime);
167+ double alpha = aftc / (t + aftc);
168+
169+ /* The sign of afvl indicates whether the result should be
170+ * rounded up or down. This gives the filter hysteresis.
171+ * If afvl > 0 the floor() function rounds to a lower alarm
172+ * level, otherwise to a higher.
173+ */
174+ afvl = alpha * afvl +
175+ ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
176+ if (afvl - floor(afvl) > THRESHOLD)
177+ afvl = -afvl; /* reverse rounding */
178+
179+ alarmRange = abs((int)floor(afvl));
180+ switch (alarmRange) {
181+ case range_Hihi:
182+ asev = prec->hhsv;
183+ alev = prec->hihi;
184+ break;
185+ case range_High:
186+ asev = prec->hsv;
187+ alev = prec->high;
188+ break;
189+ case range_Normal:
190+ asev = NO_ALARM;
191+ break;
192+ case range_Low:
193+ asev = prec->lsv;
194+ alev = prec->low;
195+ break;
196+ case range_Lolo:
197+ asev = prec->llsv;
198+ alev = prec->lolo;
199+ break;
200+ }
201+ }
202+ }
203+ prec->afvl = afvl;
204+
205+ if (asev) {
206+ /* Report alarm condition, store LALM for future HYST calculations */
207+ if (recGblSetSevr(prec, range_stat[alarmRange], asev))
208+ prec->lalm = alev;
209+ } else {
210+ /* No alarm condition, reset LALM */
211+ prec->lalm = val;
212+ }
213 }
214
215
216 static void convert(aiRecord *prec)
217
218=== modified file 'src/rec/aiRecord.dbd'
219--- src/rec/aiRecord.dbd 2005-11-15 23:35:34 +0000
220+++ src/rec/aiRecord.dbd 2010-05-28 09:22:29 +0000
221@@ -138,6 +138,11 @@
222 promptgroup(GUI_ALARMS)
223 interest(1)
224 }
225+ field(AFTC,DBF_DOUBLE) {
226+ prompt("Alarm Filter Time Constant")
227+ promptgroup(GUI_ALARMS)
228+ interest(1)
229+ }
230 field(ADEL,DBF_DOUBLE) {
231 prompt("Archive Deadband")
232 promptgroup(GUI_DISPLAY)
233@@ -153,6 +158,11 @@
234 special(SPC_NOMOD)
235 interest(3)
236 }
237+ field(AFVL,DBF_DOUBLE) {
238+ prompt("Alarm Filter Value")
239+ special(SPC_NOMOD)
240+ interest(3)
241+ }
242 field(ALST,DBF_DOUBLE) {
243 prompt("Last Value Archived")
244 special(SPC_NOMOD)
245
246=== modified file 'src/rec/calcRecord.c'
247--- src/rec/calcRecord.c 2009-04-03 14:40:13 +0000
248+++ src/rec/calcRecord.c 2010-05-28 09:22:29 +0000
249@@ -37,6 +37,9 @@
250 #undef GEN_SIZE_OFFSET
251 #include "epicsExport.h"
252
253+/* Hysterisis for alarm filtering: 1-1/e */
254+#define THRESHOLD 0.6321
255+
256 /* Create RSET - Record Support Entry Table */
257
258 #define report NULL
259@@ -79,7 +82,7 @@
260 };
261 epicsExportAddress(rset, calcRSET);
262
263-static void checkAlarms(calcRecord *prec);
264+static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast);
265 static void monitor(calcRecord *prec);
266 static int fetch_values(calcRecord *prec);
267
268@@ -111,15 +114,19 @@
269
270 static long process(calcRecord *prec)
271 {
272+ epicsTimeStamp timeLast;
273+
274 prec->pact = TRUE;
275 if (fetch_values(prec)==0) {
276 if (calcPerform(&prec->a, &prec->val, prec->rpcl)) {
277 recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
278 } else prec->udf = isnan(prec->val);
279 }
280+
281+ timeLast = prec->time;
282 recGblGetTimeStamp(prec);
283 /* check for alarms */
284- checkAlarms(prec);
285+ checkAlarms(prec, &timeLast);
286 /* check event list */
287 monitor(prec);
288 /* process the forward scan link record */
289@@ -243,14 +250,27 @@
290 return 0;
291 }
292
293-static void checkAlarms(calcRecord *prec)
294+static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast)
295 {
296- double val, hyst, lalm;
297- double alev;
298+
299+ enum {
300+ range_Lolo = 1,
301+ range_Low,
302+ range_Normal,
303+ range_High,
304+ range_Hihi
305+ } alarmRange;
306+ static const epicsEnum16 range_stat[] = {
307+ SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
308+ NO_ALARM, HIGH_ALARM, HIHI_ALARM
309+ };
310+
311+ double val, hyst, lalm, alev, aftc, afvl;
312 epicsEnum16 asev;
313
314 if (prec->udf) {
315 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
316+ prec->afvl = 0;
317 return;
318 }
319
320@@ -258,45 +278,89 @@
321 hyst = prec->hyst;
322 lalm = prec->lalm;
323
324- /* alarm condition hihi */
325- asev = prec->hhsv;
326- alev = prec->hihi;
327- if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
328- if (recGblSetSevr(prec, HIHI_ALARM, asev))
329- prec->lalm = alev;
330- return;
331- }
332-
333- /* alarm condition lolo */
334- asev = prec->llsv;
335- alev = prec->lolo;
336- if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
337- if (recGblSetSevr(prec, LOLO_ALARM, asev))
338- prec->lalm = alev;
339- return;
340- }
341-
342- /* alarm condition high */
343- asev = prec->hsv;
344- alev = prec->high;
345- if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
346- if (recGblSetSevr(prec, HIGH_ALARM, asev))
347- prec->lalm = alev;
348- return;
349- }
350-
351- /* alarm condition low */
352- asev = prec->lsv;
353- alev = prec->low;
354- if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
355- if (recGblSetSevr(prec, LOW_ALARM, asev))
356- prec->lalm = alev;
357- return;
358- }
359-
360- /* we get here only if val is out of alarm by at least hyst */
361- prec->lalm = val;
362- return;
363+ /* check VAL against alarm limits */
364+ if ((asev = prec->hhsv) &&
365+ (val >= (alev = prec->hihi) ||
366+ ((lalm == alev) && (val >= alev - hyst))))
367+ alarmRange = range_Hihi;
368+ else
369+ if ((asev = prec->llsv) &&
370+ (val <= (alev = prec->lolo) ||
371+ ((lalm == alev) && (val <= alev + hyst))))
372+ alarmRange = range_Lolo;
373+ else
374+ if ((asev = prec->hsv) &&
375+ (val >= (alev = prec->high) ||
376+ ((lalm == alev) && (val >= alev - hyst))))
377+ alarmRange = range_High;
378+ else
379+ if ((asev = prec->lsv) &&
380+ (val <= (alev = prec->low) ||
381+ ((lalm == alev) && (val <= alev + hyst))))
382+ alarmRange = range_Low;
383+ else {
384+ alev = val;
385+ asev = NO_ALARM;
386+ alarmRange = range_Normal;
387+ }
388+
389+ aftc = prec->aftc;
390+ afvl = 0;
391+
392+ if (aftc > 0) {
393+ /* Apply level filtering */
394+ afvl = prec->afvl;
395+ if (afvl == 0) {
396+ afvl = (double)alarmRange;
397+ } else {
398+ double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
399+ double alpha = aftc / (t + aftc);
400+
401+ /* The sign of afvl indicates whether the result should be
402+ * rounded up or down. This gives the filter hysteresis.
403+ * If afvl > 0 the floor() function rounds to a lower alarm
404+ * level, otherwise to a higher.
405+ */
406+ afvl = alpha * afvl +
407+ ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
408+ if (afvl - floor(afvl) > THRESHOLD)
409+ afvl = -afvl; /* reverse rounding */
410+
411+ alarmRange = abs((int)floor(afvl));
412+ switch (alarmRange) {
413+ case range_Hihi:
414+ asev = prec->hhsv;
415+ alev = prec->hihi;
416+ break;
417+ case range_High:
418+ asev = prec->hsv;
419+ alev = prec->high;
420+ break;
421+ case range_Normal:
422+ asev = NO_ALARM;
423+ break;
424+ case range_Low:
425+ asev = prec->lsv;
426+ alev = prec->low;
427+ break;
428+ case range_Lolo:
429+ asev = prec->llsv;
430+ alev = prec->lolo;
431+ break;
432+ }
433+ }
434+ }
435+ prec->afvl = afvl;
436+
437+ if (asev) {
438+ /* Report alarm condition, store LALM for future HYST calculations */
439+ if (recGblSetSevr(prec, range_stat[alarmRange], asev))
440+ prec->lalm = alev;
441+ } else {
442+ /* No alarm condition, reset LALM */
443+ prec->lalm = val;
444+ }
445+
446 }
447
448 static void monitor(calcRecord *prec)
449
450=== modified file 'src/rec/calcRecord.dbd'
451--- src/rec/calcRecord.dbd 2007-03-13 16:39:53 +0000
452+++ src/rec/calcRecord.dbd 2010-05-28 09:22:29 +0000
453@@ -153,6 +153,16 @@
454 interest(1)
455 menu(menuAlarmSevr)
456 }
457+ field(AFTC, DBF_DOUBLE) {
458+ prompt("Alarm Filter Time Constant")
459+ promptgroup(GUI_ALARMS)
460+ interest(1)
461+ }
462+ field(AFVL, DBF_DOUBLE) {
463+ prompt("Alarm Filter Value")
464+ special(SPC_NOMOD)
465+ interest(3)
466+ }
467 field(HYST,DBF_DOUBLE) {
468 prompt("Alarm Deadband")
469 promptgroup(GUI_ALARMS)
470
471=== modified file 'src/rec/longinRecord.c'
472--- src/rec/longinRecord.c 2010-04-05 18:49:18 +0000
473+++ src/rec/longinRecord.c 2010-05-28 09:22:29 +0000
474@@ -37,6 +37,8 @@
475 #undef GEN_SIZE_OFFSET
476 #include "epicsExport.h"
477
478
479+
480/* Hysterisis for alarm filtering: 1-1/e */
481+#define THRESHOLD 0.6321
482 /* Create RSET - Record Support Entry Table*/
483 #define report NULL
484 #define initialize NULL
485@@ -87,7 +89,7 @@
486 DEVSUPFUN get_ioint_info;
487 DEVSUPFUN read_longin; /*returns: (-1,0)=>(failure,success)*/
488 };
489-static void checkAlarms(longinRecord *prec);
490+static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast);
491 static void monitor(longinRecord *prec);
492 static long readValue(longinRecord *prec);
493
494@@ -132,12 +134,14 @@
495 struct longindset *pdset = (struct longindset *)(prec->dset);
496 long status;
497 unsigned char pact=prec->pact;
498+ epicsTimeStamp timeLast;
499
500 if( (pdset==NULL) || (pdset->read_longin==NULL) ) {
501 prec->pact=TRUE;
502 recGblRecordError(S_dev_missingSup,(void *)prec,"read_longin");
503 return(S_dev_missingSup);
504 }
505+ timeLast = prec->time;
506
507 status=readValue(prec); /* read the new value */
508 /* check if device support set pact */
509@@ -148,7 +152,7 @@
510 if (status==0) prec->udf = FALSE;
511
512 /* check for alarms */
513- checkAlarms(prec);
514+ checkAlarms(prec, &timeLast);
515 /* check event list */
516 monitor(prec);
517 /* process the forward scan link record */
518@@ -210,14 +214,28 @@
519 return(0);
520 }
521
522
523-static void checkAlarms(longinRecord *prec)
524+static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast)
525 {
526+ enum {
527+ range_Lolo = 1,
528+ range_Low,
529+ range_Normal,
530+ range_High,
531+ range_Hihi
532+ } alarmRange;
533+ static const epicsEnum16 range_stat[] = {
534+ SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
535+ NO_ALARM, HIGH_ALARM, HIHI_ALARM
536+ };
537+
538+ double aftc, afvl;
539 epicsInt32 val, hyst, lalm;
540 epicsInt32 alev;
541 epicsEnum16 asev;
542
543 if (prec->udf) {
544 recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM);
545+ prec->afvl = 0;
546 return;
547 }
548
549@@ -225,45 +243,88 @@
550 hyst = prec->hyst;
551 lalm = prec->lalm;
552
553- /* alarm condition hihi */
554- asev = prec->hhsv;
555- alev = prec->hihi;
556- if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
557- if (recGblSetSevr(prec, HIHI_ALARM, asev))
558- prec->lalm = alev;
559- return;
560- }
561-
562- /* alarm condition lolo */
563- asev = prec->llsv;
564- alev = prec->lolo;
565- if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
566- if (recGblSetSevr(prec, LOLO_ALARM, asev))
567- prec->lalm = alev;
568- return;
569- }
570-
571- /* alarm condition high */
572- asev = prec->hsv;
573- alev = prec->high;
574- if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
575- if (recGblSetSevr(prec, HIGH_ALARM, asev))
576- prec->lalm = alev;
577- return;
578- }
579-
580- /* alarm condition low */
581- asev = prec->lsv;
582- alev = prec->low;
583- if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
584- if (recGblSetSevr(prec, LOW_ALARM, asev))
585- prec->lalm = alev;
586- return;
587- }
588-
589- /* we get here only if val is out of alarm by at least hyst */
590- prec->lalm = val;
591- return;
592+ /* check VAL against alarm limits */
593+ if ((asev = prec->hhsv) &&
594+ (val >= (alev = prec->hihi) ||
595+ ((lalm == alev) && (val >= alev - hyst))))
596+ alarmRange = range_Hihi;
597+ else
598+ if ((asev = prec->llsv) &&
599+ (val <= (alev = prec->lolo) ||
600+ ((lalm == alev) && (val <= alev + hyst))))
601+ alarmRange = range_Lolo;
602+ else
603+ if ((asev = prec->hsv) &&
604+ (val >= (alev = prec->high) ||
605+ ((lalm == alev) && (val >= alev - hyst))))
606+ alarmRange = range_High;
607+ else
608+ if ((asev = prec->lsv) &&
609+ (val <= (alev = prec->low) ||
610+ ((lalm == alev) && (val <= alev + hyst))))
611+ alarmRange = range_Low;
612+ else {
613+ alev = val;
614+ asev = NO_ALARM;
615+ alarmRange = range_Normal;
616+ }
617+
618+ aftc = prec->aftc;
619+ afvl = 0;
620+
621+ if (aftc > 0) {
622+ /* Apply level filtering */
623+ afvl = prec->afvl;
624+ if (afvl == 0) {
625+ afvl = (double)alarmRange;
626+ } else {
627+ double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
628+ double alpha = aftc / (t + aftc);
629+
630+ /* The sign of afvl indicates whether the result should be
631+ * rounded up or down. This gives the filter hysteresis.
632+ * If afvl > 0 the floor() function rounds to a lower alarm
633+ * level, otherwise to a higher.
634+ */
635+ afvl = alpha * afvl +
636+ ((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
637+ if (afvl - floor(afvl) > THRESHOLD)
638+ afvl = -afvl; /* reverse rounding */
639+
640+ alarmRange = abs((int)floor(afvl));
641+ switch (alarmRange) {
642+ case range_Hihi:
643+ asev = prec->hhsv;
644+ alev = prec->hihi;
645+ break;
646+ case range_High:
647+ asev = prec->hsv;
648+ alev = prec->high;
649+ break;
650+ case range_Normal:
651+ asev = NO_ALARM;
652+ break;
653+ case range_Low:
654+ asev = prec->lsv;
655+ alev = prec->low;
656+ break;
657+ case range_Lolo:
658+ asev = prec->llsv;
659+ alev = prec->lolo;
660+ break;
661+ }
662+ }
663+ }
664+ prec->afvl = afvl;
665+
666+ if (asev) {
667+ /* Report alarm condition, store LALM for future HYST calculations */
668+ if (recGblSetSevr(prec, range_stat[alarmRange], asev))
669+ prec->lalm = alev;
670+ } else {
671+ /* No alarm condition, reset LALM */
672+ prec->lalm = val;
673+ }
674 }
675
676
677 /* DELTA calculates the absolute difference between its arguments
678
679=== modified file 'src/rec/longinRecord.dbd'
680--- src/rec/longinRecord.dbd 2002-07-12 21:35:43 +0000
681+++ src/rec/longinRecord.dbd 2010-05-28 09:22:29 +0000
682@@ -93,6 +93,16 @@
683 promptgroup(GUI_ALARMS)
684 interest(1)
685 }
686+ field(AFTC, DBF_DOUBLE) {
687+ prompt("Alarm Filter Time Constant")
688+ promptgroup(GUI_ALARMS)
689+ interest(1)
690+ }
691+ field(AFVL, DBF_DOUBLE) {
692+ prompt("Alarm Filter Value")
693+ special(SPC_NOMOD)
694+ interest(3)
695+ }
696 field(ADEL,DBF_LONG) {
697 prompt("Archive Deadband")
698 promptgroup(GUI_DISPLAY)
699
700=== modified file 'src/rec/mbbiRecord.c'
701--- src/rec/mbbiRecord.c 2010-04-05 18:49:18 +0000
702+++ src/rec/mbbiRecord.c 2010-05-28 09:22:29 +0000
703@@ -36,6 +36,9 @@
704 #include "mbbiRecord.h"
705 #undef GEN_SIZE_OFFSET
706 #include "epicsExport.h"
707+
708+/* Hysterisis for alarm filtering: 1-1/e */
709+#define THRESHOLD 0.6321
710 /* Create RSET - Record Support Entry Table*/
711 #define report NULL
712 #define initialize NULL
713@@ -84,7 +87,7 @@
714 DEVSUPFUN get_ioint_info;
715 DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/
716 };
717-static void checkAlarms(mbbiRecord *);
718+static void checkAlarms(mbbiRecord *, epicsTimeStamp *);
719 static void monitor(mbbiRecord *);
720 static long readValue(mbbiRecord *);
721
722
723@@ -146,6 +149,7 @@
724 struct mbbidset *pdset = (struct mbbidset *)(prec->dset);
725 long status;
726 unsigned char pact=prec->pact;
727+ epicsTimeStamp timeLast;
728
729 if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) {
730 prec->pact=TRUE;
731@@ -153,6 +157,8 @@
732 return(S_dev_missingSup);
733 }
734
735+ timeLast = prec->time;
736+
737 status=readValue(prec); /* read the new value */
738 /* check if device support set pact */
739 if ( !pact && prec->pact ) return(0);
740@@ -184,7 +190,7 @@
741 else if(status == 2) status = 0;
742
743 /* check for alarms */
744- checkAlarms(prec);
745+ checkAlarms(prec, &timeLast);
746
747 /* check event list */
748 monitor(prec);
749@@ -274,14 +280,20 @@
750 return(S_db_badChoice);
751 }
752
753-static void checkAlarms(mbbiRecord *prec)
754+static void checkAlarms(mbbiRecord *prec, epicsTimeStamp *timeLast)
755 {
756+
757+ double aftc, afvl;
758+
759+ unsigned short alarm;
760+ epicsEnum16 asev;
761 unsigned short *severities;
762 unsigned short val=prec->val;
763
764 /* check for udf alarm */
765 if(prec->udf == TRUE ){
766 recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM);
767+ prec->afvl = 0;
768 }
769
770 /* check for state alarm */
771@@ -291,8 +303,36 @@
772 } else {
773 /* in a state which is an error */
774 severities = (unsigned short *)&(prec->zrsv);
775+ /*
776 recGblSetSevr(prec,STATE_ALARM,severities[prec->val]);
777- }
778+ */
779+ alarm = severities[prec->val];
780+
781+ }
782+
783+ aftc = prec->aftc;
784+ afvl = 0.;
785+
786+ if(aftc > 0.) {
787+ afvl = prec->afvl;
788+ if(afvl == 0.) {
789+ afvl = (double) alarm;
790+ } else {
791+ double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
792+ double alpha = aftc / (t + aftc);
793+
794+ afvl = alpha * afvl +
795+ ((afvl>0.)?(1.-alpha):(alpha-1.)) * alarm;
796+ if(afvl - floor(afvl) > THRESHOLD)
797+ afvl = -afvl;
798+
799+ alarm = abs((int)floor(afvl));
800+ }
801+ }
802+
803+
804+ asev = alarm;
805+ recGblSetSevr(prec, STATE_ALARM, asev);
806
807 /* check for cos alarm */
808 if(val == prec->lalm) return;
809
810=== modified file 'src/rec/mbbiRecord.dbd'
811--- src/rec/mbbiRecord.dbd 2009-06-08 19:55:49 +0000
812+++ src/rec/mbbiRecord.dbd 2010-05-28 09:22:29 +0000
813@@ -394,6 +394,16 @@
814 interest(1)
815 menu(menuAlarmSevr)
816 }
817+ field(AFTC, DBF_DOUBLE) {
818+ prompt("Alarm Filter Time Constant")
819+ promptgroup(GUI_ALARMS)
820+ interest(1)
821+ }
822+ field(AFVL, DBF_DOUBLE) {
823+ prompt("Alarm Filter Value")
824+ special(SPC_NOMOD)
825+ interest(3)
826+ }
827 field(UNSV,DBF_MENU) {
828 prompt("Unknown State Severity")
829 promptgroup(GUI_MBB)

Subscribers

People subscribed via source and target branches