# Building services

An air conditioner's compressor power is measured every 10 s. Short cycling, starting again shortly after stopping, wears compressors out, so the Rules look for Runs that begin soon after the previous one and for many starts within an hour.

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

- Running above (`running`): 100 to 1900 W in steps of 100, default 500
- Running for (`runSeconds`): 10 to 120 s in steps of 10, default 30
- Quick restart within (`restartMinutes`): 1 to 15 min in steps of 1, default 3
- Frequent: more than (`starts`): 1 to 4 Runs an hour in steps of 1, default 2
- Short cycle under (`shortMinutes`): 1 to 15 min in steps of 1, default 5

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 power = Channel.named("power");

Rule run = Rule.named("Run")
    .when(power.above(500)).forDuration(Duration.ofSeconds(30))
    .endWhen(power.below(100)).forDuration(Duration.ofSeconds(30));
Rule quickRestart = Rule.named("QuickRestart")
    .when(after(run).within(Duration.ofMinutes(3)).and(power.above(500)))
    .forDuration(Duration.ofSeconds(30))
    .endWhen(power.below(100));
Rule frequentStarts = Rule.named("FrequentStarts")
    .when(count(run).within(Duration.ofHours(1)).above(2));
Detector detector = Detector.builder()
    .rules(run, quickRestart, frequentStarts)
    .listener(new EventListener() {
      @Override public void onEnded(Event e) {
        if (e.is(run) && shortCycle(e)) System.out.println("short cycle from " + e.start());
      }
    })
    .build();

for (int s = 0; s < 2_400; s += 10) {   // the power every 10 s
  boolean on = s >= 100 && s < 700 || s >= 800 && s < 920 || s >= 1_600 && s < 2_200;
  detector.push(s * 1_000L, Map.of(power, on ? 2_000.0 : 20.0));
}
detector.close();

// In the enclosing class:
// A plain method classifies the Ended Runs
static boolean shortCycle(Event run) {
  return run.end().getAsLong() - run.start() < Duration.ofMinutes(5).toMillis();
}
```

## The Rules

- **Run**: above 500 W for 30 s, over below 100 W for 30 s. A plain method, which the listener calls for each Ended Run, classifies a Run shorter than 5 minutes as a short cycle, shown as a note in the table.
- **QuickRestart**: power above 500 W again for 30 s, within 3 minutes after the previous Run's end. It has no duration on its End Condition, so it is over at the first Sample below 100 W.
- **FrequentStarts**: more than 2 Runs Confirmed within an hour.

At the defaults, the compressor runs from 100 s to 700 s, from 800 s to 920 s and from 1600 s to 2200 s, and each Run is Confirmed 30 s after its start, at 130, 830 and 1630 s. The second one starts 100 s after the first one's end, so it is a `QuickRestart` as well, and at 2 minutes it is a short cycle. `QuickRestart` Ends at once at the first Sample below 100 W (920 s). The Run has the same end, but it is over only once its End Condition has held for 30 s, at 950 s. With the third Run, three are Confirmed within an hour: `FrequentStarts` starts at 1630 s, and the window keeps all three until the data ends at 2390 s, so its end is Truncated.

### Things to try

- Lower *Quick restart within* to 2 minutes: the second Run begins 100 s after the first one's end, inside the window, but `QuickRestart` needs its 30 s above 500 W within it too. The window closes first, and the Pending `QuickRestart` is Discarded.

## Reference

The example is [HvacShortCyclingExample](https://github.com/romanhaendler/rbed/blob/main/src/test/java/io/github/romanhaendler/rbed/examples/HvacShortCyclingExample.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), [`Expression.below`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Expression.html#below%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), [`Conditions.count`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Conditions.html#count%28io.github.romanhaendler.rbed.Rule%29), [`EventCount.within`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/EventCount.html#within%28java.time.Duration%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.listener`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.Builder.html#listener%28io.github.romanhaendler.rbed.EventListener%29), [`EventListener.onEnded`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/EventListener.html#onEnded%28io.github.romanhaendler.rbed.Event%29), [`Event.is`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Event.html#is%28io.github.romanhaendler.rbed.Rule%29), [`Event.start`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Event.html#start%28%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), [`Event.end`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Event.html#end%28%29).

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