1 package org.lsst.ccs.bus;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Map.Entry;
8 import java.util.NoSuchElementException;
9 import java.util.concurrent.ConcurrentHashMap;
10 import java.util.concurrent.ConcurrentLinkedQueue;
11 import java.util.regex.Pattern;
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public class StatusAggregator implements KeyValueStatusListener {
48
49 Map<String, StatusAggregateConfig> config = new ConcurrentHashMap<String, StatusAggregateConfig>();
50
51 Map<String, StatusAggregateConfig> patternConfig = new ConcurrentHashMap<String, StatusAggregateConfig>();
52
53 Map<String, ConcurrentLinkedQueue<TimedValue>> history = new ConcurrentHashMap<String, ConcurrentLinkedQueue<TimedValue>>();
54 Map<String, TimedValue> last = new ConcurrentHashMap<String, TimedValue>();
55 Map<String, TimedValueStats> stats = new ConcurrentHashMap<String, TimedValueStats>();
56
57 List<Pattern> patterns = new ArrayList<Pattern>();
58
59 public StatusAggregator() {
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public void setAggregate(String key, int historyDuration,
76 int aggregateWindow) {
77 config.put(key, new StatusAggregateConfig(key, historyDuration,
78 aggregateWindow));
79 if (historyDuration > 0 || aggregateWindow > 0) {
80 history.put(key,
81 new ConcurrentLinkedQueue<StatusAggregator.TimedValue>());
82 }
83
84 if (aggregateWindow > 0) {
85 stats.put(key, new TimedValueStats());
86 }
87
88 }
89
90 public void setAggregatePattern(String pattern, int historyDuration,
91 int aggregateWindow) {
92 setAggregatePattern(Pattern.compile(pattern), historyDuration,
93 aggregateWindow);
94 }
95
96 public void setAggregatePattern(Pattern pattern, int historyDuration,
97 int aggregateWindow) {
98 StatusAggregateConfig c = new StatusAggregateConfig(pattern,
99 historyDuration, aggregateWindow);
100 patternConfig.put(c.name, c);
101 }
102
103
104
105
106
107
108
109
110 public void clearAggregate(String key) {
111 config.remove(key);
112 history.remove(key);
113 stats.remove(key);
114 last.remove(key);
115 }
116
117 protected StatusAggregateConfig getConfig(String key) {
118 StatusAggregateConfig c = config.get(key);
119 if (c != null)
120 return c;
121
122 for (Entry<String, StatusAggregateConfig> e : patternConfig.entrySet()) {
123 c = e.getValue();
124 if (!c.pattern.matcher(key).matches())
125 continue;
126 c = new StatusAggregateConfig(key, c.historyDuration,
127 c.aggregateWindow);
128 config.put(key, c);
129 if (c.historyDuration > 0 || c.aggregateWindow > 0) {
130 history.put(
131 key,
132 new ConcurrentLinkedQueue<StatusAggregator.TimedValue>());
133 }
134
135 if (c.aggregateWindow > 0) {
136 stats.put(key, new TimedValueStats());
137 }
138
139 return c;
140 }
141
142 return null;
143 }
144
145 @Override
146 public void onKeyValueStatusDecomposition(String source, long timeStamp,
147 String key, Object value, int commonID) {
148 String k = source + '/' + key;
149
150 StatusAggregateConfig c = getConfig(k);
151 if (c == null)
152 return;
153
154 last.put(k, new TimedValue(k, timeStamp, value));
155
156 int kept = c.historyDuration > c.aggregateWindow ? c.historyDuration
157 : c.aggregateWindow;
158 if (kept < 0)
159 return;
160
161
162
163
164
165 long aggregateBound = c.aggregateWindow < 0 ? Long.MAX_VALUE
166 : timeStamp - c.aggregateWindow;
167
168
169 long stopBound = timeStamp - kept;
170
171
172 ConcurrentLinkedQueue<TimedValue> q = history.get(k);
173
174 if (q == null) {
175 q = new ConcurrentLinkedQueue<TimedValue>();
176 history.put(k, q);
177 }
178
179 synchronized (q) {
180 q.add(new TimedValue(k, timeStamp, value));
181 if (c.aggregateWindow > 0) {
182 TimedValueStats st = stats.get(k);
183 if (st == null) {
184 st = new TimedValueStats();
185 stats.put(k, st);
186 }
187 if (value instanceof Number)
188 st.add(((Number) value).doubleValue());
189
190
191 if (aggregateBound > st.firstSample) {
192 long firstRemaining = Long.MAX_VALUE;
193 for (TimedValue v : q) {
194 if (v.tStamp < aggregateBound
195 && v.tStamp >= st.firstSample
196 && v.value instanceof Number) {
197 st.remove(((Number) v.value).doubleValue(), q,
198 aggregateBound);
199 } else {
200 if (v.tStamp < firstRemaining
201 && v.tStamp >= aggregateBound)
202 firstRemaining = v.tStamp;
203 }
204 }
205 st.firstSample = firstRemaining;
206 }
207 }
208
209 while (true) {
210 TimedValue v = q.peek();
211 if (v != null && v.tStamp < stopBound) {
212
213
214
215 q.poll();
216 } else
217 break;
218
219 }
220 }
221
222 }
223
224
225
226
227
228
229
230
231 public Object getLast(String key) {
232 TimedValue v = last.get(key);
233 return (v == null) ? null : v.value;
234 }
235
236 public TimedValue getLastTV(String key) {
237 return last.get(key);
238 }
239
240
241
242
243
244
245
246
247
248
249 public double getAverage(String key) {
250 TimedValueStats st = stats.get(key);
251 if (st == null)
252 throw new NoSuchElementException();
253 if (st.n == 0)
254 return 0;
255
256
257 return st.average();
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271
272 public double getStdDev(String key) {
273 TimedValueStats st = stats.get(key);
274 if (st == null)
275 throw new NoSuchElementException();
276 if (st.n == 0)
277 return 0;
278
279 return st.stddev();
280 }
281
282
283
284
285
286
287
288
289
290
291 public double getMin(String key) {
292 TimedValueStats st = stats.get(key);
293 if (st == null)
294 throw new NoSuchElementException();
295 if (st.n == 0)
296 return 0;
297
298 return st.min;
299 }
300
301
302
303
304
305
306
307
308
309
310 public double getMax(String key) {
311 TimedValueStats st = stats.get(key);
312 if (st == null)
313 throw new NoSuchElementException();
314 if (st.n == 0)
315 return 0;
316
317 return st.max;
318 }
319
320
321
322
323
324
325
326
327
328 public List<TimedValue> getHistory(String key) {
329
330 ConcurrentLinkedQueue<TimedValue> hh = history.get(key);
331 if (hh == null)
332 return null;
333
334 ArrayList<TimedValue> l = new ArrayList<TimedValue>();
335 StatusAggregateConfig cf = config.get(key);
336 long lastSample = last.get(key).tStamp;
337 long firstSample = lastSample - cf.historyDuration;
338
339 for (TimedValue tv : hh) {
340 if (tv.tStamp >= firstSample)
341 l.add(tv);
342 }
343
344 return l.size() == 0 ? null : l;
345 }
346
347 public Statistics getStatistics(String key) {
348 ConcurrentLinkedQueue<TimedValue> q = history.get(key);
349 Statistics s;
350 synchronized (q) {
351 s = new Statistics(getMax(key), getMax(key), getAverage(key),
352 getStdDev(key));
353 }
354 return s;
355 }
356
357 public Map<String, Object> getAllLast() {
358 HashMap<String, Object> m = new HashMap<String, Object>();
359
360 for (String k : stats.keySet()) {
361 m.put(k, getLast(k));
362 }
363
364 return m;
365 }
366
367 public Map<String, TimedValue> getAllLastTV() {
368 HashMap<String, TimedValue> m = new HashMap<String, TimedValue>();
369
370 for (String k : stats.keySet()) {
371 m.put(k, getLastTV(k));
372 }
373
374 return m;
375 }
376
377 public Map<String, Statistics> getAllStatistics() {
378 HashMap<String, Statistics> m = new HashMap<String, Statistics>();
379
380 for (String k : stats.keySet()) {
381 m.put(k, getStatistics(k));
382 }
383
384 return m;
385 }
386
387 public static class Statistics {
388
389 public Statistics(double min, double max, double average, double stddev) {
390 super();
391 this.min = min;
392 this.max = max;
393 this.average = average;
394 this.stddev = stddev;
395 }
396
397 public double getMin() {
398 return min;
399 }
400
401 public double getMax() {
402 return max;
403 }
404
405 public double getAverage() {
406 return average;
407 }
408
409 public double getStddev() {
410 return stddev;
411 }
412
413 private double min, max, average, stddev;
414 }
415
416 private static class StatusAggregateConfig {
417
418 public StatusAggregateConfig(String name, int historyDuration,
419 int aggregateWindow) {
420 this.name = name;
421 this.historyDuration = historyDuration;
422 this.aggregateWindow = aggregateWindow;
423 }
424
425 public StatusAggregateConfig(Pattern pattern, int historyDuration,
426 int aggregateWindow) {
427 this.pattern = pattern;
428 this.name = pattern.toString();
429 this.historyDuration = historyDuration;
430 this.aggregateWindow = aggregateWindow;
431 }
432
433 public String name;
434 public Pattern pattern;
435 public int historyDuration;
436 public int aggregateWindow;
437 }
438
439 public static class TimedValue {
440
441 public TimedValue(String name, long tStamp, Object value) {
442 super();
443 this.name = name;
444 this.tStamp = tStamp;
445 this.value = value;
446 }
447
448 public String getName() {
449 return name;
450 }
451
452 public long gettStamp() {
453 return tStamp;
454 }
455
456 public Object getValue() {
457 return value;
458 }
459
460 private String name;
461 private long tStamp;
462 private Object value;
463 }
464
465 private static class TimedValueStats {
466 public String name;
467 public int n = 0;
468 public double sum = 0;
469 public double sum2 = 0;
470 public double min = Double.MAX_VALUE;
471 public double max = Double.MIN_NORMAL;
472
473 public long firstSample;
474
475 public double average() {
476 return (n > 0) ? (sum / n) : 0;
477 }
478
479 public double stddev() {
480 if (n == 0)
481 return 0;
482 return Math.sqrt(sum2 / n - sum * sum / (n * n));
483 }
484
485 public void add(double value) {
486 sum += value;
487 sum2 += value * value;
488 n++;
489 updateMinMaxIn(value);
490 }
491
492 public void remove(double value, ConcurrentLinkedQueue<TimedValue> h,
493 long bound) {
494 sum -= value;
495 sum2 -= value * value;
496 n--;
497 updateMinMaxOut(value, h, bound);
498 }
499
500 private void updateMinMaxIn(double newValue) {
501 if (newValue < min)
502 min = newValue;
503 if (newValue > max)
504 max = newValue;
505 }
506
507 private void updateMinMaxOut(double removedValue,
508 ConcurrentLinkedQueue<TimedValue> h, long bound) {
509 if (removedValue <= min || removedValue >= max) {
510 min = Double.MAX_VALUE;
511 max = Double.MIN_NORMAL;
512 for (TimedValue v : h) {
513 if (v.tStamp >= bound && v.value instanceof Number) {
514 double x = ((Number) v.value).doubleValue();
515 if (x > max)
516 max = x;
517 if (x < min)
518 min = x;
519 }
520 }
521 }
522 }
523
524 }
525
526 }