The Event lifecycle
An Event does not appear all at once. It goes Pending when its Rule's Conditions start holding, becomes Confirmed once they have held for the required duration, and Ends later. A Pending Event whose Conditions stop holding too early is Discarded and never counts.
Four callbacks
The Detector tells its EventListener about each stage. All four methods are optional, so implement only the ones you need:
onPending: the Conditions started holding; the required duration is not reached yet.onConfirmed: the Conditions held long enough. Only Confirmed Events count as detected.onEnded: a Confirmed Event is over. It now has its end, and its Aggregates cover the whole Event.onDiscarded: a Pending Event stopped holding before it was Confirmed.
Pending, then Confirmed or Discarded
Overheat requires temp to stay above 80 for a duration, two seconds by default.
The live chart and its controls need JavaScript. The tables show the Events at the default parameters.
The site cannot run the default parameters: (JavaScript) TypeError: Cannot read properties of undefined (reading 'length')
import io.github.romanhaendler.rbed.*;
import java.time.Duration;
import java.util.Map;
Channel temp = Channel.named("temp");
Rule overheat = Rule.named("Overheat")
.when(temp.above(80)).forDuration(Duration.ofSeconds(2));
Detector detector = Detector.builder()
.rules(overheat)
.listener(new EventListener() {
@Override public void onPending(Event e) {
System.out.println("pending at " + e.start());
}
@Override public void onConfirmed(Event e) {
System.out.println("confirmed at " + e.confirmed().getAsLong());
}
@Override public void onEnded(Event e) {
System.out.println("ended at " + e.end().getAsLong());
}
@Override public void onDiscarded(Event e) {
System.out.println("discarded");
}
})
.build();
double[] values = {70, 72, 75, 85, 88, 91, 89, 86, 84, 76, 72, 74, 84, 87, 83, 75, 72, 90, 74, 71, 70};
for (int i = 0; i < values.length; i++) {
detector.push(i * 1_000L, Map.of(temp, values[i])); // one Sample per second
}
detector.close();
At the default duration of two seconds:
- The first Event goes Pending at 3 s, is Confirmed at 5 s and Ends at 9 s, when
tempfalls to 76. - The second goes Pending at 12 s and is Confirmed at 14 s, just in time: at 15 s
tempis back at 75. - The spike at 17 s lasts a single Sample. At 18 s the Conditions no longer hold, so the Pending Event is Discarded. It never counts as detected: it is not in the Event table, only among the Discarded Pending Events.
Things to try
- Raise the duration to 3 s: the second Event is Discarded as well. Its three seconds would be reached at 15 s, but by then
temphas dropped. - Raise it to 6 s: not even the first Event is Confirmed.
- Set it to 0 s: every Event is Confirmed the moment it starts, and the spike counts, too.
The start lies before the confirmation
An Event's start() is where its Conditions started holding, not where it was Confirmed: the first Event starts at 3 s, although it is only known at 5 s. confirmed() returns the moment of confirmation, which the Event table shows in a column of its own.
A Detector only sees what its Samples show. The Discarded moment on this page is the Sample at which the Conditions were found no longer holding. The Event passed to onDiscarded has no such timestamp, only its start.
Durations: forDuration and forSamples
forDuration measures from the Event's start, so the required duration is reached at start() plus the duration, and the Event is Confirmed at the first Sample or advance from then on. forSamples(n) instead requires at least n consecutive Samples; with both, both must be met. Each applies to what comes right before it: when(...), or endWhen(...) for an End Condition. Without either, an Event is Pending and Confirmed at the same moment.
One Event at a time
Each Rule has at most one Event in progress. After an Event, the next one only starts once the Rule has seen when not holding. When an Event Ends because when stopped holding, or a Pending Event is Discarded, that Sample already counts, so the next Sample can start a new Event. While a Confirmed Event runs on its End Condition, however, when is not evaluated, and after the Event has Ended, when must be seen not holding at a later Sample. After a data gap longer than maxGap, the next Sample can start an Event at once, with a Truncated start. So a Rule with an End Condition does not flicker: see End Conditions.
What an Event carries
An Event is an immutable record, and the listener receives a fresh snapshot at each stage. It carries:
name(): the name of the Rule that produced it, for example to store the Event.is(rule): whetherruleproduced it.start(): the timestamp (ms) of the first moment at whichwhenheld.confirmed(): anOptionalLong, when it was Confirmed; empty while Pending or if Discarded.end(): anOptionalLong, when it Ended; empty until then.truncatedStart(): it may have begun earlier thanstart().truncatedEnd(): observation stopped while it ran, byclose()or a data gap.aggregates(x): the Aggregates of Channelxover the Event so far.
The duration of an Ended Event is e.end().getAsLong() - e.start().
Truncated Events
Two flags mark Events whose true extent is not known. The Event table shows both in its Truncated column.
truncatedEnd(): observation stopped while the Event was running.close()Ends a Confirmed Event this way (and Discards a Pending one), and so does a data gap.truncatedStart(): the Event may have begun before itsstart(), because its Rule had not yet seen its Conditions not holding: the very first Sample already satisfies them, a moving average is still warming up, or a data gap has just ended.
The first Rule on the front page shows a Truncated start: lower its threshold to 70, and its Event starts at the very first Sample.
Reference
- The Event lifecycle, Events and Durations: forDuration and forSamples in the README, the canonical text
- EventListener and Event in the Javadoc
Methods used: Channel.named, Rule.named, Rule.when, Expression.above, Rule.forDuration, Detector.builder, Detector.Builder.rules, Detector.Builder.listener, EventListener.onPending, Event.start, EventListener.onConfirmed, Event.confirmed, EventListener.onEnded, Event.end, EventListener.onDiscarded, Detector.Builder.build, Detector.push, Detector.close.