From 26c497c9bf6e5f20b9cab1a961ce302814a755cc Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Thu, 16 Jul 2026 23:44:38 +0300 Subject: [PATCH 1/4] feat(schema): seed the v1 record schema (format v1.1) The JSON Schema from the v1.1 proof of concept, unchanged except for $id, which now points at this organization's pages domain. Signed-off-by: sepehr-safari --- schema/trace-v1.schema.json | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 schema/trace-v1.schema.json diff --git a/schema/trace-v1.schema.json b/schema/trace-v1.schema.json new file mode 100644 index 0000000..e3cfa4a --- /dev/null +++ b/schema/trace-v1.schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://open-ocpp-trace.github.io/schema/trace-v1.schema.json", + "title": "OcppTraceRecord", + "type": "object", + "required": [ + "schemaVersion", + "timestamp", + "transport", + "direction", + "messageType" + ], + "additionalProperties": true, + "properties": { + "schemaVersion": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" }, + "ocppVersion": { "type": "string" }, + "transport": { "type": "string", "enum": ["json", "soap"] }, + "chargePointId": { "type": "string" }, + "connectorId": { "type": "integer", "minimum": 0 }, + "direction": { "type": "string", "enum": ["cp-to-csms", "csms-to-cp"] }, + "messageType": { + "type": "string", + "enum": ["CALL", "CALLRESULT", "CALLERROR"] + }, + "messageId": { "type": "string" }, + "action": { "type": "string" }, + "payload": {}, + "raw": { "type": "string" }, + "error": { + "type": "object", + "additionalProperties": true, + "properties": { + "code": { "type": "string" }, + "description": { "type": "string" }, + "details": {} + } + }, + "meta": { "type": "object" } + }, + "allOf": [ + { + "if": { "properties": { "messageType": { "const": "CALL" } } }, + "then": { "required": ["action"] } + }, + { + "if": { "properties": { "messageType": { "const": "CALLERROR" } } }, + "then": { "required": ["error"] }, + "else": { "properties": { "error": false } } + } + ] +} From 46ed9fba3569eb5588d82ebf136828c8b74cd582 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Thu, 16 Jul 2026 23:44:38 +0300 Subject: [PATCH 2/4] feat(fixtures): add 15 OCPP 1.6J scenario fixtures with expected consumer views Each fixture pairs a trace (one record per line, raw present on every record) with the consumer view a conformant implementation derives from it: correlation pairs, effective actions, unanswered calls, and orphan responses. Responses omit the optional action field on purpose, so reproducing expected.json requires implementing correlation. All data is synthetic. Known coverage gaps are listed in fixtures/README.md. Signed-off-by: sepehr-safari --- fixtures/README.md | 56 +++++++ fixtures/connector-fault/expected.json | 143 ++++++++++++++++ fixtures/connector-fault/trace.jsonl | 20 +++ fixtures/diagnostics-failure/expected.json | 65 ++++++++ fixtures/diagnostics-failure/trace.jsonl | 8 + fixtures/failed-auth/expected.json | 104 ++++++++++++ fixtures/failed-auth/trace.jsonl | 14 ++ fixtures/heartbeat-irregular/expected.json | 65 ++++++++ fixtures/heartbeat-irregular/trace.jsonl | 8 + fixtures/invalid-stop-reason/expected.json | 130 +++++++++++++++ fixtures/invalid-stop-reason/trace.jsonl | 18 ++ fixtures/meter-anomaly/expected.json | 104 ++++++++++++ fixtures/meter-anomaly/trace.jsonl | 14 ++ fixtures/meter-value-gap/expected.json | 117 +++++++++++++ fixtures/meter-value-gap/trace.jsonl | 16 ++ fixtures/normal-session/expected.json | 156 ++++++++++++++++++ fixtures/normal-session/trace.jsonl | 22 +++ fixtures/short-session/expected.json | 91 ++++++++++ fixtures/short-session/trace.jsonl | 12 ++ fixtures/slow-csms-response/expected.json | 39 +++++ fixtures/slow-csms-response/trace.jsonl | 4 + fixtures/station-offline/expected.json | 104 ++++++++++++ fixtures/station-offline/trace.jsonl | 14 ++ .../status-transition-violation/expected.json | 78 +++++++++ .../status-transition-violation/trace.jsonl | 10 ++ fixtures/unexpected-start/expected.json | 65 ++++++++ fixtures/unexpected-start/trace.jsonl | 8 + fixtures/unexpected-stop-reason/expected.json | 130 +++++++++++++++ fixtures/unexpected-stop-reason/trace.jsonl | 18 ++ fixtures/unresponsive-csms/expected.json | 34 ++++ fixtures/unresponsive-csms/trace.jsonl | 3 + 31 files changed, 1670 insertions(+) create mode 100644 fixtures/README.md create mode 100644 fixtures/connector-fault/expected.json create mode 100644 fixtures/connector-fault/trace.jsonl create mode 100644 fixtures/diagnostics-failure/expected.json create mode 100644 fixtures/diagnostics-failure/trace.jsonl create mode 100644 fixtures/failed-auth/expected.json create mode 100644 fixtures/failed-auth/trace.jsonl create mode 100644 fixtures/heartbeat-irregular/expected.json create mode 100644 fixtures/heartbeat-irregular/trace.jsonl create mode 100644 fixtures/invalid-stop-reason/expected.json create mode 100644 fixtures/invalid-stop-reason/trace.jsonl create mode 100644 fixtures/meter-anomaly/expected.json create mode 100644 fixtures/meter-anomaly/trace.jsonl create mode 100644 fixtures/meter-value-gap/expected.json create mode 100644 fixtures/meter-value-gap/trace.jsonl create mode 100644 fixtures/normal-session/expected.json create mode 100644 fixtures/normal-session/trace.jsonl create mode 100644 fixtures/short-session/expected.json create mode 100644 fixtures/short-session/trace.jsonl create mode 100644 fixtures/slow-csms-response/expected.json create mode 100644 fixtures/slow-csms-response/trace.jsonl create mode 100644 fixtures/station-offline/expected.json create mode 100644 fixtures/station-offline/trace.jsonl create mode 100644 fixtures/status-transition-violation/expected.json create mode 100644 fixtures/status-transition-violation/trace.jsonl create mode 100644 fixtures/unexpected-start/expected.json create mode 100644 fixtures/unexpected-start/trace.jsonl create mode 100644 fixtures/unexpected-stop-reason/expected.json create mode 100644 fixtures/unexpected-stop-reason/trace.jsonl create mode 100644 fixtures/unresponsive-csms/expected.json create mode 100644 fixtures/unresponsive-csms/trace.jsonl diff --git a/fixtures/README.md b/fixtures/README.md new file mode 100644 index 0000000..a59d2a5 --- /dev/null +++ b/fixtures/README.md @@ -0,0 +1,56 @@ +# Fixtures + +Reference traces in the Open OCPP Trace Format, each paired with the consumer +view a conformant implementation must derive from it. + +Layout per fixture: + +``` +/ + trace.jsonl # the trace: one format record per line + expected.json # the consumer view derived from it (see ../conformance/README.md) +``` + +## Corpus + +| Fixture | Records | Calls | Results | Errors | Unanswered | Situation | +| --- | ---: | ---: | ---: | ---: | ---: | --- | +| [`normal-session`](./normal-session) | 22 | 11 | 11 | 0 | 0 | Complete charging session: boot, authorize, start transaction, meter values, stop transaction. | +| [`failed-auth`](./failed-auth) | 14 | 7 | 7 | 0 | 0 | Authorization rejected by the CSMS; no transaction is started. | +| [`connector-fault`](./connector-fault) | 20 | 10 | 10 | 0 | 0 | Connector faults mid-session; the transaction stops with a fault reason. | +| [`station-offline`](./station-offline) | 14 | 7 | 7 | 0 | 0 | Transaction starts but the trace ends with no StopTransaction. | +| [`unexpected-stop-reason`](./unexpected-stop-reason) | 18 | 9 | 9 | 0 | 0 | StopTransaction carries an unusual but legal stop reason. | +| [`meter-value-gap`](./meter-value-gap) | 16 | 8 | 8 | 0 | 0 | Transaction with no MeterValues between start and stop. | +| [`invalid-stop-reason`](./invalid-stop-reason) | 18 | 9 | 9 | 0 | 0 | StopTransaction carries a stop reason outside the OCPP 1.6 enumeration. | +| [`unexpected-start`](./unexpected-start) | 8 | 4 | 4 | 0 | 0 | StartTransaction with no preceding BootNotification or Authorize. | +| [`status-transition-violation`](./status-transition-violation) | 10 | 5 | 5 | 0 | 0 | Connector status jumps from Available directly to Finishing. | +| [`diagnostics-failure`](./diagnostics-failure) | 8 | 4 | 4 | 0 | 0 | DiagnosticsStatusNotification reports a failed diagnostics run. | +| [`slow-csms-response`](./slow-csms-response) | 4 | 2 | 2 | 0 | 0 | CSMS answers a BootNotification after a 15 second delay. | +| [`meter-anomaly`](./meter-anomaly) | 14 | 7 | 7 | 0 | 0 | Meter readings decrease during an active transaction. | +| [`short-session`](./short-session) | 12 | 6 | 6 | 0 | 0 | Full session lasting only a few seconds. | +| [`heartbeat-irregular`](./heartbeat-irregular) | 8 | 4 | 4 | 0 | 0 | Heartbeat cadence deviates from the interval the CSMS requested. | +| [`unresponsive-csms`](./unresponsive-csms) | 3 | 2 | 1 | 0 | 1 | BootNotification is never answered; a later Heartbeat is. | + +## Provenance and properties + +- All data is synthetic. Station identifiers, transaction ids, and idTag + values are invented and carry no real-world information. +- The corpus was seeded from the OCPP DebugKit scenario suite (OCPP 1.6J), + converted record by record into this format. +- Responses deliberately omit the optional `action` field, so a consumer must + derive it by correlation to reproduce `expected.json`. +- `raw` is present on every record and decodes to exactly the frame the + record decomposes. +- `connectorId` is populated on requests whose OCPP 1.6 payload carries a + top-level `connectorId`. + +## Known coverage gaps + +Contributions are welcome for what this corpus does not yet exercise: + +- CALLERROR records (the `error` object rules are specified but uncovered) +- `messageId` reuse within one trace +- traces spanning multiple charge points +- OCPP 2.0.1 sessions +- SOAP transport records +- malformed frames, once their representation is settled in the specification diff --git a/fixtures/connector-fault/expected.json b/fixtures/connector-fault/expected.json new file mode 100644 index 0000000..aee60bf --- /dev/null +++ b/fixtures/connector-fault/expected.json @@ -0,0 +1,143 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 20, + "calls": 10, + "callResults": 10, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "StatusNotification" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "StatusNotification", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "Authorize" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "Authorize", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "StartTransaction" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "StartTransaction", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-006", + "action": "StatusNotification" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "StatusNotification", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 12 + }, + { + "index": 14, + "messageType": "CALL", + "messageId": "msg-007", + "action": "MeterValues" + }, + { + "index": 15, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "MeterValues", + "correlatesWith": 14 + }, + { + "index": 16, + "messageType": "CALL", + "messageId": "msg-008", + "action": "StatusNotification" + }, + { + "index": 17, + "messageType": "CALLRESULT", + "messageId": "msg-008", + "action": "StatusNotification", + "correlatesWith": 16 + }, + { + "index": 18, + "messageType": "CALL", + "messageId": "msg-009", + "action": "StopTransaction" + }, + { + "index": 19, + "messageType": "CALLRESULT", + "messageId": "msg-009", + "action": "StopTransaction", + "correlatesWith": 18 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/connector-fault/trace.jsonl b/fixtures/connector-fault/trace.jsonl new file mode 100644 index 0000000..e24405f --- /dev/null +++ b/fixtures/connector-fault/trace.jsonl @@ -0,0 +1,20 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-003","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-003\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T12:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T12:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"StatusNotification","payload":{"connectorId":1,"status":"Preparing","errorCode":"NoError"},"raw":"[2,\"msg-003\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Preparing\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{},"raw":"[3,\"msg-003\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:15.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-002"},"raw":"[2,\"msg-004\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-002\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:15.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"idTagInfo":{"status":"Accepted","expiryDate":"2024-12-31T23:59:59.000Z"}},"raw":"[3,\"msg-004\",{\"idTagInfo\":{\"status\":\"Accepted\",\"expiryDate\":\"2024-12-31T23:59:59.000Z\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-002","meterStart":0,"timestamp":"2024-01-15T12:02:30.000Z"},"raw":"[2,\"msg-005\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-002\",\"meterStart\":0,\"timestamp\":\"2024-01-15T12:02:30.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{"transactionId":100002,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-005\",{\"transactionId\":100002,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:31.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-006\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:02:31.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{},"raw":"[3,\"msg-006\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T12:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T12:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:15:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"MeterValues","payload":{"connectorId":1,"transactionId":100002,"meterValue":[{"timestamp":"2024-01-15T12:15:00.000Z","sampledValue":[{"value":"3500","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-007\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100002,\"meterValue\":[{\"timestamp\":\"2024-01-15T12:15:00.000Z\",\"sampledValue\":[{\"value\":\"3500\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:15:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{},"raw":"[3,\"msg-007\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:18:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-008","action":"StatusNotification","payload":{"connectorId":1,"status":"Faulted","errorCode":"ConnectorLockFailure","info":"Connector lock mechanism failure detected"},"raw":"[2,\"msg-008\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Faulted\",\"errorCode\":\"ConnectorLockFailure\",\"info\":\"Connector lock mechanism failure detected\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:18:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-008","payload":{},"raw":"[3,\"msg-008\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:18:05.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-009","action":"StopTransaction","payload":{"transactionId":100002,"idTag":"SYNTHETIC-TAG-002","meterStop":3500,"timestamp":"2024-01-15T12:18:05.000Z","reason":"Other"},"raw":"[2,\"msg-009\",\"StopTransaction\",{\"transactionId\":100002,\"idTag\":\"SYNTHETIC-TAG-002\",\"meterStop\":3500,\"timestamp\":\"2024-01-15T12:18:05.000Z\",\"reason\":\"Other\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:18:05.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-003","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-009","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-009\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} diff --git a/fixtures/diagnostics-failure/expected.json b/fixtures/diagnostics-failure/expected.json new file mode 100644 index 0000000..419e850 --- /dev/null +++ b/fixtures/diagnostics-failure/expected.json @@ -0,0 +1,65 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 8, + "calls": 4, + "callResults": 4, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-003", + "action": "DiagnosticsStatusNotification" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "DiagnosticsStatusNotification", + "correlatesWith": 6 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/diagnostics-failure/trace.jsonl b/fixtures/diagnostics-failure/trace.jsonl new file mode 100644 index 0000000..83d7e27 --- /dev/null +++ b/fixtures/diagnostics-failure/trace.jsonl @@ -0,0 +1,8 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-010","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-010\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T06:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T06:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T06:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T06:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:12:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"DiagnosticsStatusNotification","payload":{"status":"DiagnosisFailed"},"raw":"[2,\"msg-003\",\"DiagnosticsStatusNotification\",{\"status\":\"DiagnosisFailed\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:12:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-010","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{},"raw":"[3,\"msg-003\",{}]"} diff --git a/fixtures/failed-auth/expected.json b/fixtures/failed-auth/expected.json new file mode 100644 index 0000000..4b79185 --- /dev/null +++ b/fixtures/failed-auth/expected.json @@ -0,0 +1,104 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 14, + "calls": 7, + "callResults": 7, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "StatusNotification" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "StatusNotification", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "Authorize" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "Authorize", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "Authorize" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "Authorize", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-006", + "action": "Authorize" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "Authorize", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-007", + "action": "StatusNotification" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "StatusNotification", + "correlatesWith": 12 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/failed-auth/trace.jsonl b/fixtures/failed-auth/trace.jsonl new file mode 100644 index 0000000..84e0d0b --- /dev/null +++ b/fixtures/failed-auth/trace.jsonl @@ -0,0 +1,14 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-002","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-002\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T11:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T11:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"StatusNotification","payload":{"connectorId":1,"status":"Preparing","errorCode":"NoError"},"raw":"[2,\"msg-003\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Preparing\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{},"raw":"[3,\"msg-003\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:15.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-INVALID"},"raw":"[2,\"msg-004\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-INVALID\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:15.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"idTagInfo":{"status":"Invalid"}},"raw":"[3,\"msg-004\",{\"idTagInfo\":{\"status\":\"Invalid\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:20.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-INVALID"},"raw":"[2,\"msg-005\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-INVALID\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:20.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{"idTagInfo":{"status":"Invalid"}},"raw":"[3,\"msg-005\",{\"idTagInfo\":{\"status\":\"Invalid\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-INVALID"},"raw":"[2,\"msg-006\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-INVALID\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{"idTagInfo":{"status":"Invalid"}},"raw":"[3,\"msg-006\",{\"idTagInfo\":{\"status\":\"Invalid\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:40.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"StatusNotification","payload":{"connectorId":1,"status":"Faulted","errorCode":"OtherError","info":"Authorization failed after 3 attempts"},"raw":"[2,\"msg-007\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Faulted\",\"errorCode\":\"OtherError\",\"info\":\"Authorization failed after 3 attempts\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T11:02:40.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-002","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{},"raw":"[3,\"msg-007\",{}]"} diff --git a/fixtures/heartbeat-irregular/expected.json b/fixtures/heartbeat-irregular/expected.json new file mode 100644 index 0000000..8332789 --- /dev/null +++ b/fixtures/heartbeat-irregular/expected.json @@ -0,0 +1,65 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 8, + "calls": 4, + "callResults": 4, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-hb-2", + "action": "Heartbeat" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-hb-2", + "action": "Heartbeat", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-hb-3", + "action": "Heartbeat" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-hb-3", + "action": "Heartbeat", + "correlatesWith": 6 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/heartbeat-irregular/trace.jsonl b/fixtures/heartbeat-irregular/trace.jsonl new file mode 100644 index 0000000..bde593d --- /dev/null +++ b/fixtures/heartbeat-irregular/trace.jsonl @@ -0,0 +1,8 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-014","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-014\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T12:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T12:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:05:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:05:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T12:05:00.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T12:05:00.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:15:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-2","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-2\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:15:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-2","payload":{"currentTime":"2024-01-15T12:15:00.500Z"},"raw":"[3,\"msg-hb-2\",{\"currentTime\":\"2024-01-15T12:15:00.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:25:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-3","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-3\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T12:25:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-014","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-3","payload":{"currentTime":"2024-01-15T12:25:00.500Z"},"raw":"[3,\"msg-hb-3\",{\"currentTime\":\"2024-01-15T12:25:00.500Z\"}]"} diff --git a/fixtures/invalid-stop-reason/expected.json b/fixtures/invalid-stop-reason/expected.json new file mode 100644 index 0000000..61b725f --- /dev/null +++ b/fixtures/invalid-stop-reason/expected.json @@ -0,0 +1,130 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 18, + "calls": 9, + "callResults": 9, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "Authorize" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "Authorize", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "StartTransaction" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "StartTransaction", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "StatusNotification" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "StatusNotification", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-006", + "action": "MeterValues" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "MeterValues", + "correlatesWith": 12 + }, + { + "index": 14, + "messageType": "CALL", + "messageId": "msg-007", + "action": "StopTransaction" + }, + { + "index": 15, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "StopTransaction", + "correlatesWith": 14 + }, + { + "index": 16, + "messageType": "CALL", + "messageId": "msg-008", + "action": "StatusNotification" + }, + { + "index": 17, + "messageType": "CALLRESULT", + "messageId": "msg-008", + "action": "StatusNotification", + "correlatesWith": 16 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/invalid-stop-reason/trace.jsonl b/fixtures/invalid-stop-reason/trace.jsonl new file mode 100644 index 0000000..8d68574 --- /dev/null +++ b/fixtures/invalid-stop-reason/trace.jsonl @@ -0,0 +1,18 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-007","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-007\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T20:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T20:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-007"},"raw":"[2,\"msg-003\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-007\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{"idTagInfo":{"status":"Accepted","expiryDate":"2024-12-31T23:59:59.000Z"}},"raw":"[3,\"msg-003\",{\"idTagInfo\":{\"status\":\"Accepted\",\"expiryDate\":\"2024-12-31T23:59:59.000Z\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-007","meterStart":0,"timestamp":"2024-01-15T20:02:30.000Z"},"raw":"[2,\"msg-004\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-007\",\"meterStart\":0,\"timestamp\":\"2024-01-15T20:02:30.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"transactionId":100007,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-004\",{\"transactionId\":100007,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:02:31.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-005\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:02:31.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{},"raw":"[3,\"msg-005\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T20:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T20:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:15:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"MeterValues","payload":{"connectorId":1,"transactionId":100007,"meterValue":[{"timestamp":"2024-01-15T20:15:00.000Z","sampledValue":[{"value":"5000","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-006\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100007,\"meterValue\":[{\"timestamp\":\"2024-01-15T20:15:00.000Z\",\"sampledValue\":[{\"value\":\"5000\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:15:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{},"raw":"[3,\"msg-006\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:30:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"StopTransaction","payload":{"transactionId":100007,"idTag":"SYNTHETIC-TAG-007","meterStop":5000,"timestamp":"2024-01-15T20:30:00.000Z","reason":"PowerSurge"},"raw":"[2,\"msg-007\",\"StopTransaction\",{\"transactionId\":100007,\"idTag\":\"SYNTHETIC-TAG-007\",\"meterStop\":5000,\"timestamp\":\"2024-01-15T20:30:00.000Z\",\"reason\":\"PowerSurge\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:30:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-007\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:30:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-008","action":"StatusNotification","payload":{"connectorId":1,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-008\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T20:30:01.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-007","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-008","payload":{},"raw":"[3,\"msg-008\",{}]"} diff --git a/fixtures/meter-anomaly/expected.json b/fixtures/meter-anomaly/expected.json new file mode 100644 index 0000000..ef98d0f --- /dev/null +++ b/fixtures/meter-anomaly/expected.json @@ -0,0 +1,104 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 14, + "calls": 7, + "callResults": 7, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "Authorize" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "Authorize", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "StartTransaction" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "StartTransaction", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "MeterValues" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "MeterValues", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "MeterValues" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "MeterValues", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-006", + "action": "Heartbeat" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "Heartbeat", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-007", + "action": "StopTransaction" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "StopTransaction", + "correlatesWith": 12 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/meter-anomaly/trace.jsonl b/fixtures/meter-anomaly/trace.jsonl new file mode 100644 index 0000000..09e0c32 --- /dev/null +++ b/fixtures/meter-anomaly/trace.jsonl @@ -0,0 +1,14 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-012","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-012\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T14:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T14:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-012"},"raw":"[2,\"msg-002\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-012\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-002\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-012","meterStart":0},"raw":"[2,\"msg-003\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-012\",\"meterStart\":0}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{"idTagInfo":{"status":"Accepted"},"transactionId":100012},"raw":"[3,\"msg-003\",{\"idTagInfo\":{\"status\":\"Accepted\"},\"transactionId\":100012}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:05:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"MeterValues","payload":{"connectorId":1,"transactionId":100012,"meterValue":[{"sampledValue":[{"value":"5000"}]}]},"raw":"[2,\"msg-004\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100012,\"meterValue\":[{\"sampledValue\":[{\"value\":\"5000\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:05:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{},"raw":"[3,\"msg-004\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:06:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"MeterValues","payload":{"connectorId":1,"transactionId":100012,"meterValue":[{"sampledValue":[{"value":"3000"}]}]},"raw":"[2,\"msg-005\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100012,\"meterValue\":[{\"sampledValue\":[{\"value\":\"3000\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:06:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{},"raw":"[3,\"msg-005\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:10:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"Heartbeat","payload":{},"raw":"[2,\"msg-006\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:10:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{"currentTime":"2024-01-15T14:10:00.500Z"},"raw":"[3,\"msg-006\",{\"currentTime\":\"2024-01-15T14:10:00.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:30:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"StopTransaction","payload":{"transactionId":100012,"idTag":"SYNTHETIC-TAG-012","meterStop":3000,"reason":"EVDisconnected"},"raw":"[2,\"msg-007\",\"StopTransaction\",{\"transactionId\":100012,\"idTag\":\"SYNTHETIC-TAG-012\",\"meterStop\":3000,\"reason\":\"EVDisconnected\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:30:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-012","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-007\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} diff --git a/fixtures/meter-value-gap/expected.json b/fixtures/meter-value-gap/expected.json new file mode 100644 index 0000000..5b32d76 --- /dev/null +++ b/fixtures/meter-value-gap/expected.json @@ -0,0 +1,117 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 16, + "calls": 8, + "callResults": 8, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "Authorize" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "Authorize", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "StartTransaction" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "StartTransaction", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "StatusNotification" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "StatusNotification", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-006", + "action": "StopTransaction" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "StopTransaction", + "correlatesWith": 12 + }, + { + "index": 14, + "messageType": "CALL", + "messageId": "msg-007", + "action": "StatusNotification" + }, + { + "index": 15, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "StatusNotification", + "correlatesWith": 14 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/meter-value-gap/trace.jsonl b/fixtures/meter-value-gap/trace.jsonl new file mode 100644 index 0000000..33f1a3b --- /dev/null +++ b/fixtures/meter-value-gap/trace.jsonl @@ -0,0 +1,16 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-006","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-006\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T18:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T18:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-006"},"raw":"[2,\"msg-003\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-006\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{"idTagInfo":{"status":"Accepted","expiryDate":"2024-12-31T23:59:59.000Z"}},"raw":"[3,\"msg-003\",{\"idTagInfo\":{\"status\":\"Accepted\",\"expiryDate\":\"2024-12-31T23:59:59.000Z\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-006","meterStart":0,"timestamp":"2024-01-15T18:02:30.000Z"},"raw":"[2,\"msg-004\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-006\",\"meterStart\":0,\"timestamp\":\"2024-01-15T18:02:30.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"transactionId":100006,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-004\",{\"transactionId\":100006,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:02:31.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-005\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:02:31.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{},"raw":"[3,\"msg-005\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T18:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T18:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:30:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"StopTransaction","payload":{"transactionId":100006,"idTag":"SYNTHETIC-TAG-006","meterStop":0,"timestamp":"2024-01-15T18:30:00.000Z","reason":"EVDisconnected"},"raw":"[2,\"msg-006\",\"StopTransaction\",{\"transactionId\":100006,\"idTag\":\"SYNTHETIC-TAG-006\",\"meterStop\":0,\"timestamp\":\"2024-01-15T18:30:00.000Z\",\"reason\":\"EVDisconnected\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:30:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-006\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:30:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"StatusNotification","payload":{"connectorId":1,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-007\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T18:30:01.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-006","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{},"raw":"[3,\"msg-007\",{}]"} diff --git a/fixtures/normal-session/expected.json b/fixtures/normal-session/expected.json new file mode 100644 index 0000000..df44d67 --- /dev/null +++ b/fixtures/normal-session/expected.json @@ -0,0 +1,156 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 22, + "calls": 11, + "callResults": 11, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "StatusNotification" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "StatusNotification", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "Authorize" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "Authorize", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "StartTransaction" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "StartTransaction", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-006", + "action": "StatusNotification" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "StatusNotification", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 12 + }, + { + "index": 14, + "messageType": "CALL", + "messageId": "msg-007", + "action": "MeterValues" + }, + { + "index": 15, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "MeterValues", + "correlatesWith": 14 + }, + { + "index": 16, + "messageType": "CALL", + "messageId": "msg-008", + "action": "MeterValues" + }, + { + "index": 17, + "messageType": "CALLRESULT", + "messageId": "msg-008", + "action": "MeterValues", + "correlatesWith": 16 + }, + { + "index": 18, + "messageType": "CALL", + "messageId": "msg-009", + "action": "StopTransaction" + }, + { + "index": 19, + "messageType": "CALLRESULT", + "messageId": "msg-009", + "action": "StopTransaction", + "correlatesWith": 18 + }, + { + "index": 20, + "messageType": "CALL", + "messageId": "msg-010", + "action": "StatusNotification" + }, + { + "index": 21, + "messageType": "CALLRESULT", + "messageId": "msg-010", + "action": "StatusNotification", + "correlatesWith": 20 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/normal-session/trace.jsonl b/fixtures/normal-session/trace.jsonl new file mode 100644 index 0000000..37de35b --- /dev/null +++ b/fixtures/normal-session/trace.jsonl @@ -0,0 +1,22 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-001","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-001\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T10:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T10:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"StatusNotification","payload":{"connectorId":1,"status":"Preparing","errorCode":"NoError"},"raw":"[2,\"msg-003\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Preparing\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{},"raw":"[3,\"msg-003\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:15.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-001"},"raw":"[2,\"msg-004\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-001\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:15.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"idTagInfo":{"status":"Accepted","expiryDate":"2024-12-31T23:59:59.000Z","parentIdTag":"SYNTHETIC-PARENT-001"}},"raw":"[3,\"msg-004\",{\"idTagInfo\":{\"status\":\"Accepted\",\"expiryDate\":\"2024-12-31T23:59:59.000Z\",\"parentIdTag\":\"SYNTHETIC-PARENT-001\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-001","meterStart":0,"timestamp":"2024-01-15T10:02:30.000Z"},"raw":"[2,\"msg-005\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-001\",\"meterStart\":0,\"timestamp\":\"2024-01-15T10:02:30.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{"transactionId":100001,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-005\",{\"transactionId\":100001,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:31.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-006\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:02:31.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{},"raw":"[3,\"msg-006\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T10:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T10:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:15:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"MeterValues","payload":{"connectorId":1,"transactionId":100001,"meterValue":[{"timestamp":"2024-01-15T10:15:00.000Z","sampledValue":[{"value":"5000","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-007\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100001,\"meterValue\":[{\"timestamp\":\"2024-01-15T10:15:00.000Z\",\"sampledValue\":[{\"value\":\"5000\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:15:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{},"raw":"[3,\"msg-007\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:30:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-008","action":"MeterValues","payload":{"connectorId":1,"transactionId":100001,"meterValue":[{"timestamp":"2024-01-15T10:30:00.000Z","sampledValue":[{"value":"10000","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-008\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100001,\"meterValue\":[{\"timestamp\":\"2024-01-15T10:30:00.000Z\",\"sampledValue\":[{\"value\":\"10000\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:30:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-008","payload":{},"raw":"[3,\"msg-008\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:35:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-009","action":"StopTransaction","payload":{"transactionId":100001,"idTag":"SYNTHETIC-TAG-001","meterStop":10000,"timestamp":"2024-01-15T10:35:00.000Z","reason":"EVDisconnected"},"raw":"[2,\"msg-009\",\"StopTransaction\",{\"transactionId\":100001,\"idTag\":\"SYNTHETIC-TAG-001\",\"meterStop\":10000,\"timestamp\":\"2024-01-15T10:35:00.000Z\",\"reason\":\"EVDisconnected\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:35:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-009","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-009\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:35:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-010","action":"StatusNotification","payload":{"connectorId":1,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-010\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:35:01.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-001","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-010","payload":{},"raw":"[3,\"msg-010\",{}]"} diff --git a/fixtures/short-session/expected.json b/fixtures/short-session/expected.json new file mode 100644 index 0000000..133daad --- /dev/null +++ b/fixtures/short-session/expected.json @@ -0,0 +1,91 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 12, + "calls": 6, + "callResults": 6, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "Authorize" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "Authorize", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "StartTransaction" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "StartTransaction", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "MeterValues" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "MeterValues", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "Heartbeat" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "Heartbeat", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-006", + "action": "StopTransaction" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "StopTransaction", + "correlatesWith": 10 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/short-session/trace.jsonl b/fixtures/short-session/trace.jsonl new file mode 100644 index 0000000..358801a --- /dev/null +++ b/fixtures/short-session/trace.jsonl @@ -0,0 +1,12 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-013","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-013\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T09:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T09:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-013"},"raw":"[2,\"msg-002\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-013\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:01.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-002\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:02.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-013","meterStart":0},"raw":"[2,\"msg-003\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-013\",\"meterStart\":0}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:02.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{"idTagInfo":{"status":"Accepted"},"transactionId":100013},"raw":"[3,\"msg-003\",{\"idTagInfo\":{\"status\":\"Accepted\"},\"transactionId\":100013}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:03.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"MeterValues","payload":{"connectorId":1,"transactionId":100013,"meterValue":[{"sampledValue":[{"value":"100"}]}]},"raw":"[2,\"msg-004\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100013,\"meterValue\":[{\"sampledValue\":[{\"value\":\"100\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:03.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{},"raw":"[3,\"msg-004\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:04.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"Heartbeat","payload":{},"raw":"[2,\"msg-005\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:04.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{"currentTime":"2024-01-15T09:00:04.500Z"},"raw":"[3,\"msg-005\",{\"currentTime\":\"2024-01-15T09:00:04.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:10.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"StopTransaction","payload":{"transactionId":100013,"idTag":"SYNTHETIC-TAG-013","meterStop":100,"reason":"EVDisconnected"},"raw":"[2,\"msg-006\",\"StopTransaction\",{\"transactionId\":100013,\"idTag\":\"SYNTHETIC-TAG-013\",\"meterStop\":100,\"reason\":\"EVDisconnected\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T09:00:10.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-013","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-006\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} diff --git a/fixtures/slow-csms-response/expected.json b/fixtures/slow-csms-response/expected.json new file mode 100644 index 0000000..0a5e39d --- /dev/null +++ b/fixtures/slow-csms-response/expected.json @@ -0,0 +1,39 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 4, + "calls": 2, + "callResults": 2, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "Heartbeat" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "Heartbeat", + "correlatesWith": 2 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/slow-csms-response/trace.jsonl b/fixtures/slow-csms-response/trace.jsonl new file mode 100644 index 0000000..2609a9d --- /dev/null +++ b/fixtures/slow-csms-response/trace.jsonl @@ -0,0 +1,4 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-011","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-011","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-011\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:00:15.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-011","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T06:00:15.000Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T06:00:15.000Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:00:16.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-011","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"Heartbeat","payload":{},"raw":"[2,\"msg-002\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T06:00:16.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-011","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{"currentTime":"2024-01-15T06:00:16.500Z"},"raw":"[3,\"msg-002\",{\"currentTime\":\"2024-01-15T06:00:16.500Z\"}]"} diff --git a/fixtures/station-offline/expected.json b/fixtures/station-offline/expected.json new file mode 100644 index 0000000..cfded5a --- /dev/null +++ b/fixtures/station-offline/expected.json @@ -0,0 +1,104 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 14, + "calls": 7, + "callResults": 7, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "Authorize" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "Authorize", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "StartTransaction" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "StartTransaction", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "StatusNotification" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "StatusNotification", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-006", + "action": "MeterValues" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "MeterValues", + "correlatesWith": 12 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/station-offline/trace.jsonl b/fixtures/station-offline/trace.jsonl new file mode 100644 index 0000000..c63c891 --- /dev/null +++ b/fixtures/station-offline/trace.jsonl @@ -0,0 +1,14 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-004","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-004\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T14:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T14:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-004"},"raw":"[2,\"msg-003\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-004\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{"idTagInfo":{"status":"Accepted","expiryDate":"2024-12-31T23:59:59.000Z"}},"raw":"[3,\"msg-003\",{\"idTagInfo\":{\"status\":\"Accepted\",\"expiryDate\":\"2024-12-31T23:59:59.000Z\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-004","meterStart":0,"timestamp":"2024-01-15T14:02:30.000Z"},"raw":"[2,\"msg-004\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-004\",\"meterStart\":0,\"timestamp\":\"2024-01-15T14:02:30.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"transactionId":100004,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-004\",{\"transactionId\":100004,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:31.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-005\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:02:31.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{},"raw":"[3,\"msg-005\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T14:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T14:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:15:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"MeterValues","payload":{"connectorId":1,"transactionId":100004,"meterValue":[{"timestamp":"2024-01-15T14:15:00.000Z","sampledValue":[{"value":"2000","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-006\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100004,\"meterValue\":[{\"timestamp\":\"2024-01-15T14:15:00.000Z\",\"sampledValue\":[{\"value\":\"2000\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T14:15:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-004","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{},"raw":"[3,\"msg-006\",{}]"} diff --git a/fixtures/status-transition-violation/expected.json b/fixtures/status-transition-violation/expected.json new file mode 100644 index 0000000..07219b9 --- /dev/null +++ b/fixtures/status-transition-violation/expected.json @@ -0,0 +1,78 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 10, + "calls": 5, + "callResults": 5, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "StatusNotification" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "StatusNotification", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-004", + "action": "StatusNotification" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "StatusNotification", + "correlatesWith": 8 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/status-transition-violation/trace.jsonl b/fixtures/status-transition-violation/trace.jsonl new file mode 100644 index 0000000..52ba1c9 --- /dev/null +++ b/fixtures/status-transition-violation/trace.jsonl @@ -0,0 +1,10 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-009","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-009\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T08:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T08:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"StatusNotification","payload":{"connectorId":1,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-003\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{},"raw":"[3,\"msg-003\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T08:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T08:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:12:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"StatusNotification","payload":{"connectorId":1,"status":"Finishing","errorCode":"NoError"},"raw":"[2,\"msg-004\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Finishing\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:12:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-009","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{},"raw":"[3,\"msg-004\",{}]"} diff --git a/fixtures/unexpected-start/expected.json b/fixtures/unexpected-start/expected.json new file mode 100644 index 0000000..b810f80 --- /dev/null +++ b/fixtures/unexpected-start/expected.json @@ -0,0 +1,65 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 8, + "calls": 4, + "callResults": 4, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "StartTransaction" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "StartTransaction", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "MeterValues" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "MeterValues", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "StopTransaction" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "StopTransaction", + "correlatesWith": 6 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/unexpected-start/trace.jsonl b/fixtures/unexpected-start/trace.jsonl new file mode 100644 index 0000000..3c44d30 --- /dev/null +++ b/fixtures/unexpected-start/trace.jsonl @@ -0,0 +1,8 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-008","meterStart":0,"timestamp":"2024-01-15T22:00:00.000Z"},"raw":"[2,\"msg-001\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-008\",\"meterStart\":0,\"timestamp\":\"2024-01-15T22:00:00.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"transactionId":100008,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-001\",{\"transactionId\":100008,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:00:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:00:01.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:00:10.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"MeterValues","payload":{"connectorId":1,"transactionId":100008,"meterValue":[{"timestamp":"2024-01-15T22:00:10.000Z","sampledValue":[{"value":"100","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-003\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100008,\"meterValue\":[{\"timestamp\":\"2024-01-15T22:00:10.000Z\",\"sampledValue\":[{\"value\":\"100\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:00:10.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{},"raw":"[3,\"msg-003\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:01:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"StopTransaction","payload":{"transactionId":100008,"idTag":"SYNTHETIC-TAG-008","meterStop":100,"timestamp":"2024-01-15T22:01:30.000Z","reason":"EVDisconnected"},"raw":"[2,\"msg-004\",\"StopTransaction\",{\"transactionId\":100008,\"idTag\":\"SYNTHETIC-TAG-008\",\"meterStop\":100,\"timestamp\":\"2024-01-15T22:01:30.000Z\",\"reason\":\"EVDisconnected\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T22:01:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-008","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-004\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} diff --git a/fixtures/unexpected-stop-reason/expected.json b/fixtures/unexpected-stop-reason/expected.json new file mode 100644 index 0000000..61b725f --- /dev/null +++ b/fixtures/unexpected-stop-reason/expected.json @@ -0,0 +1,130 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 18, + "calls": 9, + "callResults": 9, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALL", + "messageId": "msg-002", + "action": "StatusNotification" + }, + { + "index": 3, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "StatusNotification", + "correlatesWith": 2 + }, + { + "index": 4, + "messageType": "CALL", + "messageId": "msg-003", + "action": "Authorize" + }, + { + "index": 5, + "messageType": "CALLRESULT", + "messageId": "msg-003", + "action": "Authorize", + "correlatesWith": 4 + }, + { + "index": 6, + "messageType": "CALL", + "messageId": "msg-004", + "action": "StartTransaction" + }, + { + "index": 7, + "messageType": "CALLRESULT", + "messageId": "msg-004", + "action": "StartTransaction", + "correlatesWith": 6 + }, + { + "index": 8, + "messageType": "CALL", + "messageId": "msg-005", + "action": "StatusNotification" + }, + { + "index": 9, + "messageType": "CALLRESULT", + "messageId": "msg-005", + "action": "StatusNotification", + "correlatesWith": 8 + }, + { + "index": 10, + "messageType": "CALL", + "messageId": "msg-hb-1", + "action": "Heartbeat" + }, + { + "index": 11, + "messageType": "CALLRESULT", + "messageId": "msg-hb-1", + "action": "Heartbeat", + "correlatesWith": 10 + }, + { + "index": 12, + "messageType": "CALL", + "messageId": "msg-006", + "action": "MeterValues" + }, + { + "index": 13, + "messageType": "CALLRESULT", + "messageId": "msg-006", + "action": "MeterValues", + "correlatesWith": 12 + }, + { + "index": 14, + "messageType": "CALL", + "messageId": "msg-007", + "action": "StopTransaction" + }, + { + "index": 15, + "messageType": "CALLRESULT", + "messageId": "msg-007", + "action": "StopTransaction", + "correlatesWith": 14 + }, + { + "index": 16, + "messageType": "CALL", + "messageId": "msg-008", + "action": "StatusNotification" + }, + { + "index": 17, + "messageType": "CALLRESULT", + "messageId": "msg-008", + "action": "StatusNotification", + "correlatesWith": 16 + } + ], + "unansweredCalls": [], + "orphanResponses": [] +} diff --git a/fixtures/unexpected-stop-reason/trace.jsonl b/fixtures/unexpected-stop-reason/trace.jsonl new file mode 100644 index 0000000..04d2dbb --- /dev/null +++ b/fixtures/unexpected-stop-reason/trace.jsonl @@ -0,0 +1,18 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-005","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-005\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T16:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T16:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:01:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","connectorId":0,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"StatusNotification","payload":{"connectorId":0,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-002\",\"StatusNotification\",{\"connectorId\":0,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:01:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{},"raw":"[3,\"msg-002\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:02:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-003","action":"Authorize","payload":{"idTag":"SYNTHETIC-TAG-005"},"raw":"[2,\"msg-003\",\"Authorize\",{\"idTag\":\"SYNTHETIC-TAG-005\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:02:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-003","payload":{"idTagInfo":{"status":"Accepted","expiryDate":"2024-12-31T23:59:59.000Z"}},"raw":"[3,\"msg-003\",{\"idTagInfo\":{\"status\":\"Accepted\",\"expiryDate\":\"2024-12-31T23:59:59.000Z\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:02:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-004","action":"StartTransaction","payload":{"connectorId":1,"idTag":"SYNTHETIC-TAG-005","meterStart":0,"timestamp":"2024-01-15T16:02:30.000Z"},"raw":"[2,\"msg-004\",\"StartTransaction\",{\"connectorId\":1,\"idTag\":\"SYNTHETIC-TAG-005\",\"meterStart\":0,\"timestamp\":\"2024-01-15T16:02:30.000Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:02:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-004","payload":{"transactionId":100005,"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-004\",{\"transactionId\":100005,\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:02:31.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-005","action":"StatusNotification","payload":{"connectorId":1,"status":"Charging","errorCode":"NoError"},"raw":"[2,\"msg-005\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Charging\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:02:31.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-005","payload":{},"raw":"[3,\"msg-005\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:07:30.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-hb-1","action":"Heartbeat","payload":{},"raw":"[2,\"msg-hb-1\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:07:30.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-hb-1","payload":{"currentTime":"2024-01-15T16:07:30.500Z"},"raw":"[3,\"msg-hb-1\",{\"currentTime\":\"2024-01-15T16:07:30.500Z\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:20:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-006","action":"MeterValues","payload":{"connectorId":1,"transactionId":100005,"meterValue":[{"timestamp":"2024-01-15T16:20:00.000Z","sampledValue":[{"value":"4000","measurand":"Energy.Active.Import.Register","unit":"Wh"}]}]},"raw":"[2,\"msg-006\",\"MeterValues\",{\"connectorId\":1,\"transactionId\":100005,\"meterValue\":[{\"timestamp\":\"2024-01-15T16:20:00.000Z\",\"sampledValue\":[{\"value\":\"4000\",\"measurand\":\"Energy.Active.Import.Register\",\"unit\":\"Wh\"}]}]}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:20:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-006","payload":{},"raw":"[3,\"msg-006\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:25:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-007","action":"StopTransaction","payload":{"transactionId":100005,"idTag":"SYNTHETIC-TAG-005","meterStop":4000,"timestamp":"2024-01-15T16:25:00.000Z","reason":"Other"},"raw":"[2,\"msg-007\",\"StopTransaction\",{\"transactionId\":100005,\"idTag\":\"SYNTHETIC-TAG-005\",\"meterStop\":4000,\"timestamp\":\"2024-01-15T16:25:00.000Z\",\"reason\":\"Other\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:25:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-007","payload":{"idTagInfo":{"status":"Accepted"}},"raw":"[3,\"msg-007\",{\"idTagInfo\":{\"status\":\"Accepted\"}}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:25:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","connectorId":1,"direction":"cp-to-csms","messageType":"CALL","messageId":"msg-008","action":"StatusNotification","payload":{"connectorId":1,"status":"Available","errorCode":"NoError"},"raw":"[2,\"msg-008\",\"StatusNotification\",{\"connectorId\":1,\"status\":\"Available\",\"errorCode\":\"NoError\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T16:25:01.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-005","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-008","payload":{},"raw":"[3,\"msg-008\",{}]"} diff --git a/fixtures/unresponsive-csms/expected.json b/fixtures/unresponsive-csms/expected.json new file mode 100644 index 0000000..a52c24e --- /dev/null +++ b/fixtures/unresponsive-csms/expected.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 3, + "calls": 2, + "callResults": 1, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALL", + "messageId": "msg-002", + "action": "Heartbeat" + }, + { + "index": 2, + "messageType": "CALLRESULT", + "messageId": "msg-002", + "action": "Heartbeat", + "correlatesWith": 1 + } + ], + "unansweredCalls": [ + 0 + ], + "orphanResponses": [] +} diff --git a/fixtures/unresponsive-csms/trace.jsonl b/fixtures/unresponsive-csms/trace.jsonl new file mode 100644 index 0000000..3945b15 --- /dev/null +++ b/fixtures/unresponsive-csms/trace.jsonl @@ -0,0 +1,3 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-015","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100","chargePointSerialNumber":"CS-SYNTHETIC-015","firmwareVersion":"1.0.0"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\",\"chargePointSerialNumber\":\"CS-SYNTHETIC-015\",\"firmwareVersion\":\"1.0.0\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:00:05.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-015","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-002","action":"Heartbeat","payload":{},"raw":"[2,\"msg-002\",\"Heartbeat\",{}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T08:00:05.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-015","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-002","payload":{"currentTime":"2024-01-15T08:00:05.500Z"},"raw":"[3,\"msg-002\",{\"currentTime\":\"2024-01-15T08:00:05.500Z\"}]"} From eb41049fd2dcdd136323d4bba899f9cb79598b37 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Thu, 16 Jul 2026 23:44:38 +0300 Subject: [PATCH 3/4] feat(conformance): add corpus self-check, conformance guide, and CI conformance/README.md defines producer and consumer conformance and the correlation rule the fixtures pin down. validate.mjs is the corpus self-check and reference consumer: schema validation, raw fidelity, and exact recomputation of every expected.json. npm test runs it; CI runs it on every push and pull request. Signed-off-by: sepehr-safari --- .github/workflows/ci.yml | 18 ++++ .gitignore | 1 + README.md | 24 +++++ conformance/README.md | 82 +++++++++++++++++ conformance/validate.mjs | 188 +++++++++++++++++++++++++++++++++++++++ package-lock.json | 90 +++++++++++++++++++ package.json | 13 +++ 7 files changed, 416 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 conformance/README.md create mode 100644 conformance/validate.mjs create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..22fd539 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,18 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + conformance: + name: Validate schema and fixtures + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: 22 + - run: npm ci + - run: npm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/README.md b/README.md index a23e255..3aa27dc 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,27 @@ consume it (analyzers, debuggers, CI pipelines, reproducible bug reports). This repository is neutral ground: the format lives here so that no single project's release cadence or roadmap governs it. + +## Repository layout + +| Path | Contents | +| --- | --- | +| [`schema/`](./schema) | The machine-readable record schema (major version 1, currently v1.1). | +| [`fixtures/`](./fixtures) | Reference traces, each with the consumer view a conformant implementation derives from it. | +| [`conformance/`](./conformance) | What conformance means for producers and consumers, and the corpus self-check. | + +## Validating the corpus + +``` +npm ci +npm test +``` + +The self-check validates every fixture record against the schema, verifies +`raw` fidelity, and recomputes each fixture's expected consumer view. CI runs +it on every push and pull request. + +The format's current definition (v1.1) was designed in +[shiv3/ocpp-cp-simulator#188](https://github.com/shiv3/ocpp-cp-simulator/issues/188); +its prose specification lives in that repository's `docs/trace-format.md` +until it migrates here. diff --git a/conformance/README.md b/conformance/README.md new file mode 100644 index 0000000..bbbaca4 --- /dev/null +++ b/conformance/README.md @@ -0,0 +1,82 @@ +# Conformance + +How to check an implementation against this repository, and the rules the +fixtures pin down. A JSON Schema tells you a record is shaped legally; these +fixtures tell you two implementations agree on what a trace means. + +## What conformance means + +A **producer** is conformant when the records it emits: + +1. validate against [`../schema/trace-v1.schema.json`](../schema/trace-v1.schema.json); +2. carry the verbatim frame text in `raw` whenever the original bytes are + available, and that text decodes to the same frame the record decomposes; +3. set `action` on every CALL, and, when setting the optional `action` on a + CALLRESULT or CALLERROR, set it equal to the action of the CALL it + correlates with; +4. keep extensions inside `meta`, never in undeclared top-level fields. + +A **consumer** is conformant when it: + +1. accepts any record that validates against the schema and ignores unknown + fields (forward compatibility within a major version); +2. derives the consumer view defined below, reproducing each fixture's + `expected.json` exactly. + +## Correlation rule + +A CALLRESULT or CALLERROR correlates with the most recent preceding CALL in +the trace that satisfies all three conditions: + +1. same `messageId`; +2. opposite `direction`; +3. not already correlated with an earlier response. + +A response with no such CALL is an **orphan response**. A CALL that no +response has correlated with by the end of the trace is an **unanswered +call**. The effective `action` of a correlated response is its CALL's +`action`; an orphan response has no effective action unless its record +carries one explicitly. + +## The consumer view (`expected.json`) + +| Field | Meaning | +| --- | --- | +| `schemaVersion` | Schema version of the trace's records. | +| `counts.records` | Total records in the trace. | +| `counts.calls` / `counts.callResults` / `counts.callErrors` | Records per message type. | +| `records[]` | One entry per record, in trace order. | +| `records[].index` | Zero-based position in the trace. | +| `records[].messageType` | Echoed from the record. | +| `records[].messageId` | Echoed from the record. | +| `records[].action` | The effective action: explicit for a CALL, derived by correlation for a response. Absent when underivable. | +| `records[].correlatesWith` | For a correlated response, the index of its CALL. Absent otherwise. | +| `unansweredCalls` | Indexes of CALLs never correlated, ascending. | +| `orphanResponses` | Indexes of responses that correlate with nothing, ascending. | + +Fields the view does not repeat (timestamps, payloads, directions, identity +fields) are covered by the schema and by the `raw` fidelity rule; the view +pins only what a consumer must compute. + +## Checking an implementation + +For each directory under [`../fixtures`](../fixtures): parse `trace.jsonl` +(one record per line), derive the consumer view, and compare it structurally +to `expected.json`. Any mismatch is a conformance failure. Producers can +additionally round-trip: emit a trace, feed it to a conformant consumer, and +confirm the view is what they intended. + +## Checking this repository + +``` +npm ci +npm test +``` + +[`validate.mjs`](./validate.mjs) is the corpus self-check and the reference +consumer for the rules above. For every fixture it validates each record +against the schema, verifies `raw` decodes to the fields the record +decomposes, recomputes the consumer view with the correlation rule, and +requires an exact match with `expected.json`. CI runs it on every push and +pull request, so a fixture and its expected view cannot drift apart +unnoticed. diff --git a/conformance/validate.mjs b/conformance/validate.mjs new file mode 100644 index 0000000..900aadc --- /dev/null +++ b/conformance/validate.mjs @@ -0,0 +1,188 @@ +// Corpus self-check for the Open OCPP Trace fixtures. +// +// This script is the reference consumer for the format's derivation rules: +// it validates every fixture record against the JSON Schema, checks the +// invariants the schema alone cannot express (raw fidelity, action +// consistency), recomputes the consumer view from trace.jsonl using the +// correlation rule in conformance/README.md, and requires it to match +// expected.json exactly. +// +// Usage: npm test (or: node conformance/validate.mjs) + +import Ajv2020 from 'ajv/dist/2020.js'; +import addFormats from 'ajv-formats'; +import { readFileSync, readdirSync, statSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..'); +const schema = JSON.parse(readFileSync(join(root, 'schema', 'trace-v1.schema.json'), 'utf8')); + +const ajv = new Ajv2020({ allErrors: true }); +addFormats(ajv); +const validateRecord = ajv.compile(schema); + +const MESSAGE_TYPE = { 2: 'CALL', 3: 'CALLRESULT', 4: 'CALLERROR' }; + +let failures = 0; +function problem(fixture, message) { + failures += 1; + console.error(`FAIL ${fixture}: ${message}`); +} + +function deepEqual(a, b) { + if (Object.is(a, b)) return true; + if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false; + if (Array.isArray(a) !== Array.isArray(b)) return false; + const keysA = Object.keys(a); + const keysB = Object.keys(b); + if (keysA.length !== keysB.length) return false; + return keysA.every((k) => Object.hasOwn(b, k) && deepEqual(a[k], b[k])); +} + +// The normative correlation rule: a CALLRESULT or CALLERROR correlates with +// the most recent preceding CALL that has the same messageId, travels in the +// opposite direction, and is not yet answered. +function buildConsumerView(records) { + const view = records.map((r, index) => { + const entry = { index, messageType: r.messageType, messageId: r.messageId }; + if (r.messageType === 'CALL') entry.action = r.action; + return entry; + }); + const answered = new Set(); + const orphanResponses = []; + for (let i = 0; i < records.length; i++) { + const r = records[i]; + if (r.messageType === 'CALL') continue; + let match = -1; + for (let j = i - 1; j >= 0; j--) { + const c = records[j]; + if ( + c.messageType === 'CALL' && + c.messageId === r.messageId && + c.direction !== r.direction && + !answered.has(j) + ) { + match = j; + break; + } + } + if (match === -1) { + orphanResponses.push(i); + } else { + answered.add(match); + view[i].action = records[match].action; + view[i].correlatesWith = match; + } + } + const unansweredCalls = records + .map((r, i) => ({ r, i })) + .filter(({ r, i }) => r.messageType === 'CALL' && !answered.has(i)) + .map(({ i }) => i); + const counts = { + records: records.length, + calls: records.filter((r) => r.messageType === 'CALL').length, + callResults: records.filter((r) => r.messageType === 'CALLRESULT').length, + callErrors: records.filter((r) => r.messageType === 'CALLERROR').length, + }; + return { + schemaVersion: records[0]?.schemaVersion ?? 'unknown', + counts, + records: view, + unansweredCalls, + orphanResponses, + }; +} + +function checkRawFidelity(fixture, record, line) { + if (record.raw === undefined) return; + let frame; + try { + frame = JSON.parse(record.raw); + } catch { + problem(fixture, `line ${line}: raw is present but does not parse as JSON`); + return; + } + if (!Array.isArray(frame)) { + problem(fixture, `line ${line}: raw does not decode to an OCPP-J array`); + return; + } + if (MESSAGE_TYPE[frame[0]] !== record.messageType) { + problem(fixture, `line ${line}: raw frame kind ${frame[0]} contradicts messageType ${record.messageType}`); + } + if (record.messageId !== undefined && frame[1] !== record.messageId) { + problem(fixture, `line ${line}: raw messageId contradicts record messageId`); + } + if (record.messageType === 'CALL') { + if (frame[2] !== record.action) { + problem(fixture, `line ${line}: raw action contradicts record action`); + } + if (record.payload !== undefined && !deepEqual(frame[3], record.payload)) { + problem(fixture, `line ${line}: raw payload contradicts record payload`); + } + } else if (record.messageType === 'CALLRESULT') { + if (record.payload !== undefined && !deepEqual(frame[2], record.payload)) { + problem(fixture, `line ${line}: raw payload contradicts record payload`); + } + } else if (record.messageType === 'CALLERROR') { + if (record.error?.code !== undefined && frame[2] !== record.error.code) { + problem(fixture, `line ${line}: raw error code contradicts record error.code`); + } + } +} + +const fixturesDir = join(root, 'fixtures'); +const fixtureNames = readdirSync(fixturesDir).filter((name) => + statSync(join(fixturesDir, name)).isDirectory(), +); + +if (fixtureNames.length === 0) { + console.error('FAIL: no fixtures found'); + process.exit(1); +} + +for (const name of fixtureNames.sort()) { + const dir = join(fixturesDir, name); + const lines = readFileSync(join(dir, 'trace.jsonl'), 'utf8').split('\n').filter(Boolean); + const expected = JSON.parse(readFileSync(join(dir, 'expected.json'), 'utf8')); + + const records = []; + lines.forEach((text, i) => { + let record; + try { + record = JSON.parse(text); + } catch { + problem(name, `line ${i + 1}: not valid JSON`); + return; + } + if (!validateRecord(record)) { + problem(name, `line ${i + 1}: schema violation ${ajv.errorsText(validateRecord.errors)}`); + } + checkRawFidelity(name, record, i + 1); + records.push(record); + }); + + const view = buildConsumerView(records); + + // A response that carries its own action must agree with its correlated CALL. + view.records.forEach((entry, i) => { + const own = records[i]?.action; + if (entry.correlatesWith !== undefined && own !== undefined && own !== entry.action) { + problem(name, `record ${i}: explicit action ${own} contradicts correlated CALL action ${entry.action}`); + } + }); + + if (!deepEqual(view, expected)) { + problem(name, 'recomputed consumer view does not match expected.json'); + } + + if (failures === 0) { + console.log(`ok ${name} (${view.counts.records} records, ${view.unansweredCalls.length} unanswered, ${view.orphanResponses.length} orphans)`); + } +} + +if (failures > 0) { + console.error(`\n${failures} problem(s) found`); + process.exit(1); +} +console.log(`\nall ${fixtureNames.length} fixtures conform`); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2bd108b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,90 @@ +{ + "name": "open-ocpp-trace-specification", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "open-ocpp-trace-specification", + "devDependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a90e525 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "open-ocpp-trace-specification", + "private": true, + "type": "module", + "description": "Schema, fixtures, and conformance material for the Open OCPP Trace Format", + "scripts": { + "test": "node conformance/validate.mjs" + }, + "devDependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1" + } +} From f00e1323bc46da7c789728856f7055cfc4a49bab Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Wed, 22 Jul 2026 14:08:58 +0300 Subject: [PATCH 4/4] feat(conformance): add orphan-response fixture and complete CALLERROR raw fidelity Addresses shiv3's review on #3: - checkRawFidelity() now checks error.description and error.details against the raw CALLERROR frame, matching the full-payload check for CALL/CALLRESULT, so the first CALLERROR fixture cannot slip a partial mismatch. - A new orphan-response fixture produces a non-empty orphanResponses (a response whose CALL is not in the trace), the one consumer-view concept the corpus did not previously exercise. - The per-fixture ok line is gated on that fixture's own result rather than the global failure count, so a red run no longer suppresses later passing lines. Signed-off-by: sepehr-safari --- conformance/validate.mjs | 9 ++++++- fixtures/README.md | 1 + fixtures/orphan-response/expected.json | 33 ++++++++++++++++++++++++++ fixtures/orphan-response/trace.jsonl | 3 +++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 fixtures/orphan-response/expected.json create mode 100644 fixtures/orphan-response/trace.jsonl diff --git a/conformance/validate.mjs b/conformance/validate.mjs index 900aadc..3d41c10 100644 --- a/conformance/validate.mjs +++ b/conformance/validate.mjs @@ -128,6 +128,12 @@ function checkRawFidelity(fixture, record, line) { if (record.error?.code !== undefined && frame[2] !== record.error.code) { problem(fixture, `line ${line}: raw error code contradicts record error.code`); } + if (record.error?.description !== undefined && frame[3] !== record.error.description) { + problem(fixture, `line ${line}: raw error description contradicts record error.description`); + } + if (record.error?.details !== undefined && !deepEqual(frame[4], record.error.details)) { + problem(fixture, `line ${line}: raw error details contradicts record error.details`); + } } } @@ -142,6 +148,7 @@ if (fixtureNames.length === 0) { } for (const name of fixtureNames.sort()) { + const problemsBefore = failures; const dir = join(fixturesDir, name); const lines = readFileSync(join(dir, 'trace.jsonl'), 'utf8').split('\n').filter(Boolean); const expected = JSON.parse(readFileSync(join(dir, 'expected.json'), 'utf8')); @@ -176,7 +183,7 @@ for (const name of fixtureNames.sort()) { problem(name, 'recomputed consumer view does not match expected.json'); } - if (failures === 0) { + if (failures === problemsBefore) { console.log(`ok ${name} (${view.counts.records} records, ${view.unansweredCalls.length} unanswered, ${view.orphanResponses.length} orphans)`); } } diff --git a/fixtures/README.md b/fixtures/README.md index a59d2a5..24d466f 100644 --- a/fixtures/README.md +++ b/fixtures/README.md @@ -30,6 +30,7 @@ Layout per fixture: | [`short-session`](./short-session) | 12 | 6 | 6 | 0 | 0 | Full session lasting only a few seconds. | | [`heartbeat-irregular`](./heartbeat-irregular) | 8 | 4 | 4 | 0 | 0 | Heartbeat cadence deviates from the interval the CSMS requested. | | [`unresponsive-csms`](./unresponsive-csms) | 3 | 2 | 1 | 0 | 1 | BootNotification is never answered; a later Heartbeat is. | +| [`orphan-response`](./orphan-response) | 3 | 1 | 2 | 0 | 0 | A response whose CALL is not in the trace (capture started mid-session), so it correlates with nothing. | ## Provenance and properties diff --git a/fixtures/orphan-response/expected.json b/fixtures/orphan-response/expected.json new file mode 100644 index 0000000..2df0bae --- /dev/null +++ b/fixtures/orphan-response/expected.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": "1.1", + "counts": { + "records": 3, + "calls": 1, + "callResults": 2, + "callErrors": 0 + }, + "records": [ + { + "index": 0, + "messageType": "CALL", + "messageId": "msg-001", + "action": "BootNotification" + }, + { + "index": 1, + "messageType": "CALLRESULT", + "messageId": "msg-001", + "action": "BootNotification", + "correlatesWith": 0 + }, + { + "index": 2, + "messageType": "CALLRESULT", + "messageId": "msg-pre-capture" + } + ], + "unansweredCalls": [], + "orphanResponses": [ + 2 + ] +} diff --git a/fixtures/orphan-response/trace.jsonl b/fixtures/orphan-response/trace.jsonl new file mode 100644 index 0000000..d529507 --- /dev/null +++ b/fixtures/orphan-response/trace.jsonl @@ -0,0 +1,3 @@ +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:00:00.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-016","direction":"cp-to-csms","messageType":"CALL","messageId":"msg-001","action":"BootNotification","payload":{"chargePointVendor":"SyntheticVendor","chargePointModel":"SM-100"},"raw":"[2,\"msg-001\",\"BootNotification\",{\"chargePointVendor\":\"SyntheticVendor\",\"chargePointModel\":\"SM-100\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:00:00.500Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-016","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-001","payload":{"currentTime":"2024-01-15T10:00:00.500Z","interval":300,"status":"Accepted"},"raw":"[3,\"msg-001\",{\"currentTime\":\"2024-01-15T10:00:00.500Z\",\"interval\":300,\"status\":\"Accepted\"}]"} +{"schemaVersion":"1.1","timestamp":"2024-01-15T10:00:01.000Z","ocppVersion":"1.6","transport":"json","chargePointId":"CS-SYNTHETIC-016","direction":"csms-to-cp","messageType":"CALLRESULT","messageId":"msg-pre-capture","payload":{"currentTime":"2024-01-15T10:00:01.000Z"},"raw":"[3,\"msg-pre-capture\",{\"currentTime\":\"2024-01-15T10:00:01.000Z\"}]"}