44 lines
3.2 KiB
Markdown
44 lines
3.2 KiB
Markdown
# OCPP Forwarder Architecture
|
|
|
|
The OCPP forwarder (`charger/ocpp/forwarder.go`) is a hybrid proxy that lets a charger talk to evcc and an upstream OCPP server at the same time. Chargers connect directly to evcc's central system on the normal port. For each charger with a matching `ForwarderRule`, a "sidecar" WebSocket connection to the upstream server is opened and kept in parallel for the lifetime of the charger connection.
|
|
|
|
The forwarder is opt-in: hooks (`chargerConnectHook`, `chargerDisconnectHook`, `chargerMessageHook` in `instance.go`) are nil unless a rule matches, so a charger without a rule behaves exactly as before.
|
|
|
|
## Forwarding modes
|
|
|
|
Two modes apply at the same time, selected per message by its action.
|
|
|
|
### Transparent relay (billing-critical)
|
|
|
|
For the actions in `actionsRelayedToUpstream` (`Authorize`, `StartTransaction`, `StopTransaction`, `DataTransfer`), upstream is the authoritative Central System:
|
|
|
|
1. Charger sends the Call to evcc.
|
|
2. The message hook forwards it to the upstream sidecar and bypasses evcc's OCPP handler.
|
|
3. Upstream's `CallResult`/`CallError` is relayed back to the charger.
|
|
|
|
evcc's handler is never invoked for these. This lets the pay backend control authorization, issue its own transaction IDs, and see consistent Start/Stop pairs.
|
|
|
|
### Sidecar observation (informational)
|
|
|
|
For all other messages (`BootNotification`, `StatusNotification`, `MeterValues`, `Heartbeat`, etc.):
|
|
|
|
1. Charger sends the Call to evcc, which processes it normally.
|
|
2. The same frame is also mirrored to the upstream sidecar.
|
|
|
|
Upstream observes the session while evcc manages the charger as usual.
|
|
|
|
## Upstream to charger (commands)
|
|
|
|
Calls (type 2) initiated by upstream are injected into the charger via `CS.Write`. The charger's `CallResult`/`CallError` is routed back to upstream. Examples: `RemoteStartTransaction`, `RemoteStopTransaction`, `GetConfiguration`, `ChangeConfiguration`, `TriggerMessage`, `SetChargingProfile`.
|
|
|
|
`ChangeConfiguration` for `MeterValueSampleInterval` is intercepted: the forwarder absorbs it as a local throttle on `MeterValues` forwarded to upstream and replies `Accepted` without touching the charger's own config. evcc still processes every `MeterValues` frame for energy management.
|
|
|
|
## Read-only mode
|
|
|
|
When a rule sets `ReadOnly`, upstream may observe but cannot control the charger. Any incoming Call from upstream is answered with a `SecurityError` and not forwarded. `ReadOnly` is applied live per message, so toggling it does not require reconnecting the sidecar.
|
|
|
|
## Connection lifecycle
|
|
|
|
Frames that arrive from a charger before its sidecar finishes dialling are buffered (`pendingMsgs`) and flushed in order once the sidecar connects, so early messages such as `BootNotification` still reach upstream. If the dial fails or upstream drops mid-session, any buffered or in-flight relay Calls are answered to the charger with a `CallError` so it is not left hanging, and the failure is surfaced to the UI via `forwarderErrors`.
|
|
|
|
Rules can be changed at runtime through `ApplyForwarderRules`. Sidecars for removed rules are closed, rules with changed connection parameters are re-dialled, and rules for chargers that are not connected are test-dialled to surface unreachable hosts immediately.
|