Patrick Schurig 41d59de971 docs(optimiseur): versions allemande et anglaise de la section et de « Comment le moteur décide »
Prose traduite pour Présentation, Protocole, Contrat, Écrire un
optimiseur et la page du moteur ; ancres de titres identiques dans les
trois langues. Les blocs générés sont identiques d'une langue à l'autre.
Les exemples gardent un motif en français, comme l'exige le contrat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:09:09 +02:00

100 lines
4.9 KiB
Markdown

# Writing an optimizer
An optimiser is a **server** that speaks the [protocol](protocole.md): it answers the
handshake, then every `GetPlan` with a plan of one or more slots, or with an abstention.
Any language will do. A minimal optimiser fits in about fifty lines of Python.
Minimal session, for illustration (the exact shape is in the [contract](contrat.md)):
```text
→ {"jsonrpc":"2.0","id":1,"method":"Handshake","params":{"protocolVersion":"1.1","engine":"etm-powersync-energy-plugin-etm/…"}}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"1.1","name":"my-optimiser","planning":false}}
→ {"jsonrpc":"2.0","id":2,"method":"GetPlan","params":{ …context… }}
← {"jsonrpc":"2.0","id":2,"result":{"abstain":true,"reason":"premier calcul en cours"}}
```
Three things not to miss:
- **one action per available load** in the current slot, each with its **reason** as
text: otherwise the whole plan is rejected (the reason text is in French);
- **answer quickly**: serve the last cached plan, or abstain, and compute in the
background;
- **fetch nothing elsewhere**: everything the optimiser needs, forecasts included, is in
the context.
## The reference optimiser {#reference}
The engine repository contains a free reference optimiser (GPL-3.0), in Python, standard
library only:
[`tools/etm-powersync-ref-optimizer/`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/tree/main/tools/etm-powersync-ref-optimizer).
It answers a one-slot plan that reproduces the cascade of the engine's rules, reading only
the context. At handshake it announces itself as `powersync-optimizer`.
It is a starting point for writing your own, and a point of comparison: it listens on the
local socket by default, or on `tcp://` with a shared secret read from a file. It can also
inject faults (abstention, slowness, invalid plan, different major version…) to test the
engine's fallback. Instructions:
[its `README.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tools/etm-powersync-ref-optimizer/README.md).
## Pointing the engine at your optimiser {#brancher}
Three JSON-RPC methods of the box, reserved to the administrator, described in
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md):
- `NymeaEnergy.SetOptimizerEndpoint` — where to connect (`unix:///…` or `tcp://host:port`);
- `NymeaEnergy.SetOptimizerToken` — the shared secret, the same as the optimiser's;
- `NymeaEnergy.GetOptimizerStatus` — connection status: optimiser alive, active strategy,
announced name, last error code.
Over `tcp://` without encryption, the secret travels in clear text on the local network.
## The conformance suite {#conformite}
The `etm-powersync-optimizer-conformance` suite starts **your** optimiser and connects the
**real** engine to it, on simulated devices. It checks, one function per point:
1. the handshake is accepted;
2. every `GetPlan` is answered within the engine's timeout;
3. the whole session, both ways, conforms to the contract embedded in the engine,
validation rules included;
4. every plan is applied: the optimiser is in control;
5. the safety loop stays on top: an out-of-bounds setpoint is clamped, a phase overload is
shed.
It does not judge the **quality** of the plans.
### Running it against your optimiser
Build the engine with its tests, then give the command that starts your server;
`{endpoint}` in it is replaced by the address to listen on:
```sh
export ETM_POWERSYNC_OPTIMIZER_CMD='exec /path/to/my-optimiser --listen {endpoint}'
export ETM_POWERSYNC_CONFORMANCE_EXPECT=plan # or abstain
```
Each function runs in its own process. The full command (network isolation recommended),
the other variables (shared secret, start-up delay…) and how to read failures are in the
[suite's `README.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tests/auto/conformance/README.md).
### Expected mode: `plan` or `abstain` {#mode-attendu}
`ETM_POWERSYNC_CONFORMANCE_EXPECT` tells the suite what your optimiser does:
- **`plan`** (default): it is in control. Every cycle must apply its plan.
- **`abstain`**: it abstains on every cycle — that is allowed, and conformant. The suite
then checks that the engine drives by its rules on every cycle, without counting a
failure, and that safety still holds; the clamping check is skipped, as there is no
setpoint to clamp.
Any other value makes the suite fail. **The mode is not inferred from the handshake**:
`planning` and `capabilities` describe the shape of plans, not whether any are produced.
The mode is given, not guessed.
## Comparing on the same inputs {#traces}
The engine can record, on the box, a trace of every cycle (off by default,
`NymeaEnergy.SetOptimizerTrace`). The `tools/etm-powersync-trace-replay` tool replays these
traces against a candidate optimiser and summarises the differences from the recorded plan.
Traces stay on site.