# After another Event

`after(a)` holds once an Event of Rule `a` has Ended. On its own this stays true forever, so it is narrowed:

- `.within(duration)`: the most recent Event of `a` Ended at most that long ago.
- `.unless(b, c, ...)`: no Event of `b` or `c` has been Confirmed since the most recent Event of `a` Ended.

Like every reference to another Rule, `a` must be added before the Rule that refers to it.

## A window after the door

A cold room's door is open from minute 10 to minute 12, and once more at minute 60. Its temperature is above 8 °C from minute 15 to minute 25, soon after the first opening, and from minute 100 to minute 110, long after the second. `ExcursionAfterDoor` is an excursion within a window after the door was open.

Parameters (in the page address as ?window=…):

- Window after DoorOpen (`window`): 0 to 60 min in steps of 5, default 30

The site cannot run the default parameters: (JavaScript) TypeError: Cannot read properties of undefined (reading 'length')

Java source at the default parameters:

```java
import static io.github.romanhaendler.rbed.Conditions.*;

import io.github.romanhaendler.rbed.*;
import java.time.Duration;
import java.util.Map;

Channel door = Channel.named("door");
Channel temp = Channel.named("temp");

Rule doorOpen = Rule.named("DoorOpen").when(door.above(0.5));
Rule excursionAfterDoor = Rule.named("ExcursionAfterDoor")
    .when(after(doorOpen).within(Duration.ofMinutes(30)).and(temp.above(8)));
Detector detector = Detector.builder()
    .rules(doorOpen, excursionAfterDoor)
    .build();

for (int m = 0; m <= 150; m++) {   // one Sample a minute
  detector.push(m * 60_000L, Map.of(
      door, m >= 10 && m < 12 || m == 60 ? 1.0 : 0.0,
      temp, m >= 15 && m < 25 || m >= 100 && m < 110 ? 10.0 : 4.0));
}
detector.close();
```

The window is measured from where `DoorOpen` Ended, not from where it started. At the default of 30 minutes, the first door opening Ended at minute 12, so the window lasts until minute 42 and covers the first excursion: `ExcursionAfterDoor` runs from 15 to 25. The second door opening Ended at minute 61, 39 minutes before the second excursion starts, so that excursion is not attributed to the door.

### Things to try

- Widen the window to 45 minutes: the second excursion counts too, but only until minute 107. From then on the door opening lies more than 45 minutes back, and `ExcursionAfterDoor` Ends although the temperature stays above 8 °C until minute 110.
- Narrow the window to 5 minutes: the first excursion is cut short in the same way, at minute 18.

## Interruptions: unless

```java
Rule lift = Rule.named("Lift").when(weight.above(500));
Rule maintenance = Rule.named("Maintenance").when(serviceMode.above(0.5));
Rule liftSinceService = Rule.named("LiftSinceService").when(after(lift).unless(maintenance));
```

`LiftSinceService` holds once a `Lift` has Ended, until a `Maintenance` Event is Confirmed. Interrupting Events are named explicitly with `unless` rather than implied, so a sequence means exactly what it says. [ADR-0002](https://github.com/romanhaendler/rbed/blob/main/docs/adr/0002-explicit-unless-for-sequences.md) explains why.

The [Pulse Channels](https://romanhaendler.github.io/rbed/pulse-channels/) page uses `after(...).within(...)` for errors after a deployment, and the [Building services](https://romanhaendler.github.io/rbed/building-services/) example for a compressor that restarts too soon.

## Reference

- [after: sequences](https://github.com/romanhaendler/rbed#after-sequences) in the README, the canonical text
- [After](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/After.html) in the Javadoc

Methods used: [`Channel.named`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Channel.html#named%28java.lang.String%29), [`Rule.named`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#named%28java.lang.String%29), [`Rule.when`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#when%28io.github.romanhaendler.rbed.Condition%29), [`Expression.above`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Expression.html#above%28double%29), [`Conditions.after`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Conditions.html#after%28io.github.romanhaendler.rbed.Rule%29), [`After.within`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/After.html#within%28java.time.Duration%29), [`Condition.and`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Condition.html#and%28io.github.romanhaendler.rbed.Condition%29), [`Detector.builder`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.html#builder%28%29), [`Detector.Builder.rules`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.Builder.html#rules%28io.github.romanhaendler.rbed.Rule...%29), [`Detector.Builder.build`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.Builder.html#build%28%29), [`Detector.push`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.html#push%28long,java.util.Map%29), [`Detector.close`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.html#close%28%29), [`After.unless`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/After.html#unless%28io.github.romanhaendler.rbed.Rule...%29).
