OCPP protocol specifics
- Charge point is a WebSocket client, not a server
- CSMS URL includes CP identity:
ws://<csms>/ocpp/1.6/<cpId> ocpp1.6subprotocol header is required — CSMS rejects without it- Text frames only, no binary
Go patterns
Infinite for loop for read loop — not iterating a collection; blocking on each Read waiting for the next message.
Buffered channels between goroutines — prevents the read loop from blocking if the consumer is slow.
Non-blocking send with select/default — drop the message rather than block.
sync.Mutex around the connection — lock only around the assignment, not during network calls. Locking during network I/O causes deadlocks.
context.WithTimeout for dial and write — bounds operations that could hang indefinitely.
Nil guard before using a connection that may not be initialised.
Warning
Always unlock on all code paths — An early
returnon error inside a lock will deadlock. Lock only the critical section, not the surrounding network call.
Design principles
- Transport layer is dumb —
websocket.godeals in raw[]byteonly, knows nothing about OCPP message structure - Shutdown order — cancel context before closing the connection so the read loop exits cleanly rather than receiving an unexpected error
- Channel ownership — channel lives on the struct; callers just read from it