Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ rather than copying them.
| `dbc/` | Wire formats of the shared nodes: `bench.dbc` (classic), `genset.dbc` (J1939), and `full.dbc`, both merged, the one file tools load; `lib/codec_<name>/` and `full.dbc` are generated by `just dbc` |
| `arxml/` | `can-full.arxml`, the can-full bench as an AUTOSAR R19-11 System Template; the DC-DC's AUTOSAR tables are generated from it (`just arxml`) |
| `toolchain/` | The firmware build image |
| `testing/` | Host-side pytest suites and fixtures, run against any SocketCAN channel |
| `testing/` | Host-side pytest suites and fixtures, run against any SocketCAN channel; `suites/v2g` brings its own EVerest SIL (`just test-v2g`) |
| `bench/` | A Renode bench for any set of nodes, and the agent image; each bench builds on it, and `just test-bench` runs a suite on it |
| `tools/` | Host scripts: filling a node's A2L with one build's addresses (the build runs it), generating the DC-DC's AUTOSAR tables from `arxml/` (`just arxml`) |
| `justfile` | Shared recipes: building nodes, `twister`, `test-bench`, and `hil` for a flashed board |
Expand Down
33 changes: 33 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ TOOLCHAIN := "zelos-examples-toolchain:" + ZEPHYR + "-" + `shasum -a 256 toolcha
BOARD := "nucleo_h753zi"
# The part on that board, for probe-rs.
CHIP := "STM32H753ZITx"
# The V2G SIL image: EVerest pinned by digest, plus the suite's tools. Tagged by
# its Dockerfile, as TOOLCHAIN is.
V2G_SIL := "zelos-examples-v2g-sil:" + `shasum -a 256 testing/suites/v2g/Dockerfile | cut -c1-12`
# The SIL's container: the suite mounted, the capabilities its link needs, and
# IPv6, which V2G runs on.
V2G_RUN := "--cap-add NET_ADMIN --cap-add SYS_ADMIN --sysctl net.ipv6.conf.all.disable_ipv6=0 -v \"$PWD/testing/suites/v2g:/suite:ro\""
# The Docker volume holding the west workspace, or an absolute host path to
# bind instead. Override to build two trees at once.
WORKSPACE := env("ZELOS_WEST_VOLUME", "zxws")
Expand Down Expand Up @@ -140,6 +146,28 @@ hil ELF CHANNEL +SUITES:
# --durations: the slowest tests, timed, after the results.
cd testing && uv run --frozen pytest "${suites[@]}" --channel {{ CHANNEL }} --a2l "${elf%.elf}.a2l" --durations=10

# The SIL needs CAP_NET_ADMIN for the veth and CAP_SYS_ADMIN for the EV's
# network namespace. ARGS go to pytest, e.g. `-k din`.
#
# Run the V2G suite: EVerest's EVSE and EV charge over a veth pair, captured.
test-v2g *ARGS: _v2g_sil
docker run --rm {{ V2G_RUN }} {{ V2G_SIL }} /suite {{ ARGS }}

# Record the V2G demo captures and their message sidecars again, ISO 15118-2 and DIN 70121.
v2g-record: _v2g_sil
#!/usr/bin/env bash
set -euo pipefail
out=$(mktemp -d)
trap 'rm -rf "$out"' EXIT
docker run --rm {{ V2G_RUN }} -v "$out:/out" --entrypoint bash {{ V2G_SIL }} -c \
"python3 -m pytest -p no:cacheprovider -q /suite/test_session.py::test_session --record /out; \
status=\$?; chown -R $(id -u):$(id -g) /out; exit \$status"
for p in iso2 din; do
cp "$out/test_session[$p]/$p.pcapng" "testing/suites/v2g/captures/$p-dc.pcapng"
cp "$out/test_session[$p]/$p.messages.csv" "testing/suites/v2g/captures/$p-dc.messages.csv"
done
ls -l testing/suites/v2g/captures/

# Remove the cached Zephyr workspace. Frees about 2 GB.
clean-workspace:
docker volume rm {{ WORKSPACE }}
Expand All @@ -161,6 +189,11 @@ _fresh ELFS +SOURCES:
exit 1
fi

# Build the V2G SIL image. Cached by Docker after the first run.
_v2g_sil:
docker image inspect {{ V2G_SIL }} > /dev/null 2>&1 || \
docker build -q -t {{ V2G_SIL }} testing/suites/v2g > /dev/null

# Build the firmware toolchain image. Cached by Docker after the first run.
_toolchain:
docker image inspect {{ TOOLCHAIN }} > /dev/null 2>&1 || \
Expand Down
12 changes: 12 additions & 0 deletions testing/suites/v2g/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# The V2G SIL: EVerest's demo image, pinned, plus the broker, the capture
# tools and pytest. EVerest 2025.10.0 (everest-demo tag 2025.10.0-single-phase).
FROM ghcr.io/everest/everest-demo/manager@sha256:9165d0937323ae61ee2e496f68f3c4a465ed9099740e489df1f0161265492756

RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
mosquitto mosquitto-clients iproute2 tshark \
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir --break-system-packages pytest==9.1.1

WORKDIR /suite
ENTRYPOINT ["python3", "-m", "pytest", "-p", "no:cacheprovider"]
60 changes: 60 additions & 0 deletions testing/suites/v2g/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# V2G suite

DC charging sessions between an EVerest EVSE and an EVerest EV, ISO 15118-2 and
DIN 70121, over a veth pair, each captured on the link. Software in the loop:
no modems, no CP line.

```bash
just test-v2g # from the repository root; Linux Docker
just test-v2g -k din # pytest arguments pass through
just v2g-record # record captures/ again
```

## The SIL

| Piece | What it is |
|---|---|
| Image | `everest-demo/manager` 2025.10.0-single-phase, pinned by digest (`Dockerfile`), plus Mosquitto, tshark and pytest |
| EVSE | `EvseV2G` on `v2g-evse`, no TLS, with EVerest's simulated DC supply, isolation monitor and board |
| EV | `PyEvJosev` (Josev, EXIficient codec) driven by `EvManager`, standalone in its own network namespace on `v2g-ev` |
| SLAC | `SlacSimulator`, in process: no HomePlug frames on the link |
| Broker | Mosquitto on a Unix socket, reachable from both namespaces |
| Capture | dumpcap on `v2g-evse`: SDP, the V2G TCP connection, neighbour discovery |

The container needs `CAP_NET_ADMIN` (the veth) and `CAP_SYS_ADMIN` (the EV's
namespace), which `just test-v2g` grants.

## The checks

Truth is the link. Each capture is walked at the V2GTP layer: SDP request and
response, the response's address and port the ones the EV connected to, then
each direction of the one TCP connection split into V2GTP frames with nothing
left over, requests and responses alternating. EXI is not decoded. Frames are
named by the EVSE's record of them (`v2g_messages`), matched on bytes.

| Test | EV does | Asserts |
|---|---|---|
| `test_session[iso2]`, `[din]` | charges 5 s, then stops | protocol selected; SessionSetup first, CableCheck, PreCharge, PowerDelivery, a CurrentDemand loop, PowerDelivery, WeldingDetection, SessionStop last; FIN, no RST |
| `test_ev_unplug_mid_current_demand` | unplugs in CurrentDemand | ends in CurrentDemand: no PowerDelivery stop, no WeldingDetection, no SessionStop |
| `test_isolation_fault` | charges; the isolation monitor reads 10 kOhm | CableCheck, then no PreCharge and no CurrentDemand |
| `test_capture_fixture` | - | the committed captures pass the same link checks, and each sidecar row points at a V2GTP frame's start, in order and direction; needs only tshark |

## Captures

`captures/iso2-dc.pcapng` and `captures/din-dc.pcapng`: one full session each,
from `test_session`, about 20 KB. Link-local addresses from random MACs.

Each has a sidecar, `<name>.messages.csv`: one row per V2GTP frame, `frame`
(the capture's frame number of the segment holding its first byte),
`direction` (`ev` or `evse`) and `message` (e.g. `CurrentDemandReq`), named
from the EVSE's record matched to the wire. No EXI bytes: the record's
responses are not the wire's (below). `just v2g-record` writes both.

## Known EVerest behaviour

- EvseV2G 2025.10 records each response in its request's buffer: the request's
V2GTP header and length, the response's EXI cut or padded to it. Requests are
recorded exactly. The suite matches responses on their common prefix.
- Real SLAC (`EvseSlac`, `EvSlac`) over the veth does not match: without a
modem no `CM_SET_KEY.CNF` comes back, so `EvseSlac` stays in reset and
`EvSlac` fails. The HomePlug AV frames do cross the link.
65 changes: 65 additions & 0 deletions testing/suites/v2g/captures/din-dc.messages.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
frame,direction,message
11,ev,SupportedAppProtocolReq
13,evse,SupportedAppProtocolRes
15,ev,SessionSetupReq
17,evse,SessionSetupRes
19,ev,ServiceDiscoveryReq
21,evse,ServiceDiscoveryRes
23,ev,ServicePaymentSelectionReq
25,evse,ServicePaymentSelectionRes
27,ev,ContractAuthenticationReq
30,evse,ContractAuthenticationRes
32,ev,ContractAuthenticationReq
34,evse,ContractAuthenticationRes
37,ev,ChargeParameterDiscoveryReq
39,evse,ChargeParameterDiscoveryRes
41,ev,CableCheckReq
43,evse,CableCheckRes
45,ev,CableCheckReq
47,evse,CableCheckRes
49,ev,CableCheckReq
51,evse,CableCheckRes
53,ev,CableCheckReq
55,evse,CableCheckRes
57,ev,CableCheckReq
59,evse,CableCheckRes
61,ev,CableCheckReq
63,evse,CableCheckRes
65,ev,CableCheckReq
67,evse,CableCheckRes
69,ev,PreChargeReq
71,evse,PreChargeRes
73,ev,PreChargeReq
75,evse,PreChargeRes
77,ev,PowerDeliveryReq
79,evse,PowerDeliveryRes
81,ev,CurrentDemandReq
83,evse,CurrentDemandRes
85,ev,CurrentDemandReq
87,evse,CurrentDemandRes
89,ev,CurrentDemandReq
91,evse,CurrentDemandRes
93,ev,CurrentDemandReq
95,evse,CurrentDemandRes
97,ev,CurrentDemandReq
99,evse,CurrentDemandRes
101,ev,CurrentDemandReq
103,evse,CurrentDemandRes
105,ev,CurrentDemandReq
107,evse,CurrentDemandRes
109,ev,CurrentDemandReq
111,evse,CurrentDemandRes
113,ev,CurrentDemandReq
115,evse,CurrentDemandRes
117,ev,PowerDeliveryReq
119,evse,PowerDeliveryRes
121,ev,WeldingDetectionReq
123,evse,WeldingDetectionRes
126,ev,WeldingDetectionReq
128,evse,WeldingDetectionRes
130,ev,WeldingDetectionReq
132,evse,WeldingDetectionRes
134,ev,WeldingDetectionReq
136,evse,WeldingDetectionRes
138,ev,SessionStopReq
140,evse,SessionStopRes
Binary file added testing/suites/v2g/captures/din-dc.pcapng
Binary file not shown.
69 changes: 69 additions & 0 deletions testing/suites/v2g/captures/iso2-dc.messages.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
frame,direction,message
13,ev,SupportedAppProtocolReq
15,evse,SupportedAppProtocolRes
18,ev,SessionSetupReq
20,evse,SessionSetupRes
22,ev,ServiceDiscoveryReq
24,evse,ServiceDiscoveryRes
26,ev,PaymentServiceSelectionReq
28,evse,PaymentServiceSelectionRes
30,ev,AuthorizationReq
32,evse,AuthorizationRes
34,ev,AuthorizationReq
36,evse,AuthorizationRes
38,ev,ChargeParameterDiscoveryReq
40,evse,ChargeParameterDiscoveryRes
42,ev,CableCheckReq
44,evse,CableCheckRes
46,ev,CableCheckReq
48,evse,CableCheckRes
50,ev,CableCheckReq
52,evse,CableCheckRes
54,ev,CableCheckReq
56,evse,CableCheckRes
58,ev,CableCheckReq
60,evse,CableCheckRes
62,ev,CableCheckReq
64,evse,CableCheckRes
66,ev,CableCheckReq
68,evse,CableCheckRes
70,ev,CableCheckReq
72,evse,CableCheckRes
74,ev,CableCheckReq
76,evse,CableCheckRes
78,ev,PreChargeReq
80,evse,PreChargeRes
83,ev,PreChargeReq
85,evse,PreChargeRes
87,ev,PowerDeliveryReq
89,evse,PowerDeliveryRes
92,ev,CurrentDemandReq
94,evse,CurrentDemandRes
96,ev,CurrentDemandReq
98,evse,CurrentDemandRes
100,ev,CurrentDemandReq
102,evse,CurrentDemandRes
104,ev,CurrentDemandReq
106,evse,CurrentDemandRes
108,ev,CurrentDemandReq
110,evse,CurrentDemandRes
112,ev,CurrentDemandReq
114,evse,CurrentDemandRes
116,ev,CurrentDemandReq
118,evse,CurrentDemandRes
120,ev,CurrentDemandReq
122,evse,CurrentDemandRes
124,ev,CurrentDemandReq
126,evse,CurrentDemandRes
128,ev,PowerDeliveryReq
130,evse,PowerDeliveryRes
132,ev,WeldingDetectionReq
134,evse,WeldingDetectionRes
136,ev,WeldingDetectionReq
138,evse,WeldingDetectionRes
140,ev,WeldingDetectionReq
142,evse,WeldingDetectionRes
144,ev,WeldingDetectionReq
146,evse,WeldingDetectionRes
148,ev,SessionStopReq
150,evse,SessionStopRes
Binary file added testing/suites/v2g/captures/iso2-dc.pcapng
Binary file not shown.
Loading
Loading