[
  {
    "model": "pages.usermanual",
    "fields": {
      "is_seed_data": true,
      "is_user_data": false,
      "is_deleted": false,
      "slug": "ocpp-16",
      "title": "OCPP 1.6 User Manual",
      "description": "Implementation details for the Arthexis OCPP 1.6 central system.",
      "languages": "en",
      "content_html": "<h1>OCPP 1.6 User Manual</h1>\n<p>This manual documents how the Arthexis control system implements each Open Charge Point Protocol (OCPP) 1.6 call that is currently supported. The focus is on the behaviour of the WebSocket consumer that represents our central system (CSMS) and the HTTP endpoints that emit CSMS initiated calls.</p>\n<h2>Charge point → CSMS calls</h2>\n<h3>BootNotification</h3>\n<p>When a charge point connects it immediately sends a <code>BootNotification</code> request. The CSMS replies with the current UTC timestamp, a 300 second heartbeat interval, and an <code>Accepted</code> status. No persistence is performed beyond registering the live connection for logging and monitoring purposes.【F:ocpp/consumers.py†L718-L740】</p>\n<h3>Heartbeat</h3>\n<p>Heartbeat requests reuse the same UTC timestamp response body. Each heartbeat records the reception time on the associated <code>Charger</code> row so the admin UI can surface the last communication time of the device.【F:ocpp/consumers.py†L741-L755】</p>\n<h3>StatusNotification</h3>\n<p>Status notifications update both the aggregate (connector-less) <code>Charger</code> row and any connector specific row. The handler stores the status, error code, vendor info, and timestamp, mirrors those values on the in-memory objects, and logs the processed payload. It also derives an availability state (<code>Operative</code> or <code>Inoperative</code>) from the status text and persists the effective availability timestamp for downstream dashboards.【F:ocpp/consumers.py†L756-L808】【F:ocpp/models.py†L120-L176】【F:ocpp/models.py†L232-L258】</p>\n<h3>Authorize</h3>\n<p>Authorization requests are resolved against active <code>CustomerAccount</code> records that have an allowed RFID credential. When a charger enforces RFID checks we only accept the transaction if the associated account is authorised. Otherwise the charger responds with <code>Accepted</code> regardless of the RFID contents.【F:ocpp/consumers.py†L189-L207】【F:ocpp/consumers.py†L809-L823】</p>\n<h3>MeterValues</h3>\n<p>Meter value payloads are parsed into <code>MeterValue</code> rows tied to the active transaction. The handler maps common measurands (energy, voltage, current, temperature, state-of-charge) into dedicated fields, updates transaction start/stop readings when the context marks a begin or end event, infers the initial meter start when available, and records the charger temperature. Each reading is stored with its timestamp and connector so historical analysis retains the original sampling context.【F:ocpp/consumers.py†L208-L418】</p>\n<h3>DiagnosticsStatusNotification</h3>\n<p>Diagnostics updates synchronise the latest status, timestamp, and upload location across the active connector specific <code>Charger</code> record and its aggregate counterpart. A succinct log entry captures the status label and location for later review.【F:ocpp/consumers.py†L824-L881】</p>\n<h3>StartTransaction</h3>\n<p>A <code>StartTransaction</code> call may arrive with an optional RFID. The CSMS looks up the corresponding account, automatically creates the RFID entry if it is new, and verifies authorisation when the charger requires RFID checks. Accepted transactions create a <code>Transaction</code> row, start a live session log, broadcast periodic consumption summaries via the NetMessage system, and return the transaction identifier with <code>idTagInfo.status</code> set to <code>Accepted</code>. Rejected transactions respond with <code>Invalid</code> authorisation status without altering persistence.【F:ocpp/consumers.py†L189-L207】【F:ocpp/consumers.py†L882-L915】【F:ocpp/consumers.py†L500-L603】</p>\n<h3>StopTransaction</h3>\n<p><code>StopTransaction</code> finalises the active transaction, ensuring a placeholder exists if the charger references an unknown identifier. The handler stores the meter stop reading and timestamp, stops consumption broadcasting, ends the session log, and responds with <code>Accepted</code>. This guarantees consistent transaction state even when the stop arrives without a preceding start message.【F:ocpp/consumers.py†L916-L942】【F:ocpp/consumers.py†L500-L603】</p>\n<h3>FirmwareStatusNotification</h3>\n<p>Firmware status notifications persist the reported status, optional info string, and timestamp on all <code>Charger</code> identities associated with the connection. Each update is logged so operators can trace firmware upgrade progress.【F:ocpp/consumers.py†L429-L498】【F:ocpp/consumers.py†L943-L970】</p>\n<h3>DataTransfer</h3>\n<p>Incoming <code>DataTransfer</code> requests are written to the <code>DataTransferMessage</code> table so every payload, vendor identifier, and follow-up status is auditable. The consumer stores the raw request with a <code>cp_to_csms</code> direction marker, invokes any vendor-specific handler, and records the final status or error metadata alongside the response timestamp before returning the call result to the charger.【F:ocpp/consumers.py†L583-L782】【F:ocpp/models.py†L767-L810】</p>\n<h2>CSMS → charge point calls</h2>\n<p>Outgoing calls originate from the control endpoint that proxies UI and admin actions to the live WebSocket session.</p>\n<h3>RemoteStartTransaction</h3>\n<p>A remote start request requires an <code>idTag</code> and optionally a connector id or charging profile. The view validates the payload, injects defaults from the current connector context, and asynchronously sends the <code>RemoteStartTransaction</code> message to the active connection.【F:ocpp/views.py†L909-L950】</p>\n<h3>RemoteStopTransaction</h3>\n<p>Remote stop requests must target an active transaction. The view fetches the current session from the in-memory store and, if present, sends <code>RemoteStopTransaction</code> with the transaction identifier back to the charger.【F:ocpp/views.py†L900-L934】</p>\n<h3>Reset</h3>\n<p>Reset commands always request a soft reset and are dispatched as asynchronous WebSocket sends. Additional metadata is not persisted because reset responses do not currently update model state.【F:ocpp/views.py†L984-L993】</p>\n<h3>ChangeAvailability</h3>\n<p>Change availability requests enforce <code>Operative</code>/<code>Inoperative</code> validation, normalise the connector id, and send the <code>ChangeAvailability</code> call with a unique message id. The view registers pending-call metadata so the WebSocket consumer can reconcile the eventual response, and resets the request tracking fields on the affected <code>Charger</code> record to indicate a new change is in progress.【F:ocpp/views.py†L951-L983】【F:ocpp/store.py†L1-L88】【F:ocpp/models.py†L120-L176】</p>\n<h3>DataTransfer</h3>\n<p>Operators can issue a <code>DataTransfer</code> via the control endpoint by providing the vendor id, optional message id, and payload data. The view validates the request, persists a <code>DataTransferMessage</code> row flagged as <code>csms_to_cp</code>, sends the call to the charger, and registers a pending call keyed to that record. When the charger replies, the consumer matches the message id, updates the stored status, response data, or error details, and timestamps the outcome so outbound diagnostics remain traceable.【F:ocpp/views.py†L900-L1040】【F:ocpp/consumers.py†L583-L782】【F:ocpp/models.py†L767-L810】</p>\n<h3>Handling responses</h3>\n<p>When a charger replies to <code>ChangeAvailability</code>, the consumer matches the message id against the pending-call registry. Call results mark the request as accepted and update the requested state, while call errors flag the request as rejected and capture the error details. The stored status feeds the admin interface so operators can track whether the requested availability state took effect.【F:ocpp/consumers.py†L500-L603】【F:ocpp/consumers.py†L604-L672】</p>\n<h2>Session logging and consumption telemetry</h2>\n<p>The CSMS keeps per-session logs and broadcasts consumption summaries for active transactions. When a transaction starts, the consumer creates a NetMessage broadcast and schedules periodic refreshes. Stopping the transaction cancels the task and closes the session log to prevent stale updates from leaking after the charge point disconnects.【F:ocpp/consumers.py†L429-L498】【F:ocpp/consumers.py†L500-L603】</p>\n<h2>In-memory coordination</h2>\n<p>All active WebSocket connections, transactions, logs, and pending CSMS calls are tracked in the shared <code>ocpp.store</code> module. Helper functions provide consistent identity keys per charger/connector pair, enforce a two-connection-per-IP rate limit, and expose lookups used by both the WebSocket consumer and the control endpoint. This lightweight store allows the manual operations above to find the correct charger session instantly without hitting the database.【F:ocpp/store.py†L1-L116】</p>",
      "content_pdf": "[encoded data, 9352 characters]",
      "pdf_orientation": "landscape"
    }
  }
]
