# Logistics

A refrigerated truck carries vaccines, which must stay between 2 and 8 °C; its temperature and door sensors report every minute. Regulations limit the total time above 8 °C over the trip to 45 minutes, across all excursions, and an excursion soon after the door was open is probably caused by it.

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

- Too warm above (`limit`): 2 to 12 °C in steps of 0.5, default 8
- Excursion after (`excursionMinutes`): 1 to 30 min in steps of 1, default 15
- Caused by the door within (`doorMinutes`): 5 to 120 min in steps of 5, default 30
- Total time limit (`totalMinutes`): 15 to 120 min in steps of 5, default 45

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 excursion = Rule.named("Excursion")
    .when(temp.above(8)).forDuration(Duration.ofMinutes(15))
    .endWhen(temp.above(8).not());
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)));
Rule cumulativeLimit = Rule.named("CumulativeLimit")
    .when(totalDuration(excursion).above(Duration.ofMinutes(45).toMillis()));
Detector detector = Detector.builder()
    .rules(excursion, doorOpen, excursionAfterDoor, cumulativeLimit)
    .build();

for (int m = 0; m < 480; m++) {   // every minute
  double celsius = m >= 62 && m < 90 ? 10 : m >= 300 && m < 330 ? 9 : 5;
  detector.push(m * 60_000L, Map.of(temp, celsius, door, m >= 60 && m < 65 ? 1.0 : 0.0));
}
detector.close();
```

## The Rules

- **Excursion**: above 8 °C for 15 minutes, over as soon as it is back at 8 °C or below.
- **DoorOpen**: the door sensor reads 1.
- **ExcursionAfterDoor**: above 8 °C within 30 minutes after the door closed, to attribute the cause.
- **CumulativeLimit**: the total time of the Excursions above 45 minutes.

At the defaults, the door is open from minute 60 to 65, and the temperature is above 8 °C from minute 62 to 90 and from 300 to 330. The first `Excursion` runs from 62 to 90, Confirmed at 77. `ExcursionAfterDoor` starts at 65, the moment the door closed, and runs to 90; the second Excursion, from 300, is too long after the door. The first Excursion adds 28 minutes; the second, Confirmed at 315, brings the total to 46 at 318, where `CumulativeLimit` starts. It holds until the data ends, so its end is Truncated.

### Things to try

- Raise *Total time limit* to 60 minutes: the two Excursions add up to 58 minutes above 8 °C, and there is no `CumulativeLimit`.

## Reference

The example is [ColdChainExample](https://github.com/romanhaendler/rbed/blob/main/src/test/java/io/github/romanhaendler/rbed/examples/ColdChainExample.java), a test with its expected Events, listed in the README's [Examples](https://github.com/romanhaendler/rbed#examples).

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), [`Rule.forDuration`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#forDuration%28java.time.Duration%29), [`Rule.endWhen`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#endWhen%28io.github.romanhaendler.rbed.Condition%29), [`Condition.not`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Condition.html#not%28%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), [`Conditions.totalDuration`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Conditions.html#totalDuration%28io.github.romanhaendler.rbed.Rule%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).

The concepts at work, each on a page of its own: [After another Event](https://romanhaendler.github.io/rbed/after/), [Time spent in Events](https://romanhaendler.github.io/rbed/total-duration/), [End Conditions and hysteresis](https://romanhaendler.github.io/rbed/end-conditions/).
