Merge lp:~khkim/epics-base/alarm-filter into lp:~epics-core/epics-base/3.14
- alarm-filter
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
Review via email: mp+26278@code.launchpad.net |
Commit message
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 : | # |
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) |
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?