Add lan_sender.py: simulated LAN-based car for stack testing without hardware#70
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c331604884
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ports: | ||
| - "8080:8080/tcp" # Status HTTP server | ||
| - "9080:9080/tcp" # WebSocket bridge |
There was a problem hiding this comment.
Publish UDP port 5005 from the base test container
This compose path is paired with a host-side sender (python3 src/lan_sender.py 127.0.0.1 5005 in .github/workflows/telemetry-ci.yml), but the base service only publishes 8080/9080 and does not expose 5005/udp. On bridge networking, UDP sent to host 127.0.0.1:5005 will not reach daq-lan-base, so the base never receives packets and the Redis/WebSocket flow tests fail downstream.
Useful? React with 👍 / 👎.
| - REMOTE_IP=127.0.0.1 # Will be overridden by lan_sender — base listens on all interfaces | ||
| - UDP_PORT=5005 | ||
| - TCP_PORT=5006 | ||
| - REDIS_URL=redis://base-redis:***@timescaledb:5432/wfr |
There was a problem hiding this comment.
Point REDIS_URL to the Redis service endpoint
REDIS_URL is configured as redis://base-redis:***@timescaledb:5432/wfr, which targets the TimescaleDB host/port instead of Redis (base-redis:6379). Base-mode telemetry constructs its Redis client from this variable, so this misconfiguration prevents CAN/stats publication and breaks the intended lan-sender pipeline.
Useful? React with 👍 / 👎.
| - UDP_PORT=5005 | ||
| - TCP_PORT=5006 | ||
| - REDIS_URL=redis://base-redis:***@timescaledb:5432/wfr | ||
| - TIMESCALE_TABLE=wfr26 |
There was a problem hiding this comment.
Enable Timescale logging for the lan-sender test stack
The new test suite asserts that timescale:status reports increasing row counts, but this compose environment only sets TIMESCALE_TABLE and never enables Timescale logging (ENABLE_TIMESCALE_LOGGING=true) or provides a DSN override. Since main.py starts the Timescale bridge only when that flag is true, the bridge is skipped and the Timescale assertion fails in this workflow.
Useful? React with 👍 / 👎.
…hardware Introduces a self-contained UDP sender that emits properly-formatted CAN batches (matching data.py's udp_receiver protocol) so the base station pipeline can be exercised with no car or CAN hardware present. - src/lan_sender.py: 20 Hz UDP packet generator with real DBC CAN IDs (VCU_Status, Pedal_Sensors, BMS, MC_Feedback, Wheel_Speeds, IMU, Cooling_Status) and VCU_Timestamp (ID 1999) carrying epoch-ms so clock sync activates normally. Default target 127.0.0.1:5005. - lan_sender.sh: thin shell wrapper, no venv activation needed - deploy/docker-compose.lan-sender-test.yml: base-only compose stack (base + base-redis + timescaledb) for testing without the car container - tests/test_lan_sender.py: integration tests covering UDP receipt, Redis can_messages + system_stats publication, WebSocket broadcast, 8080 status page, and TimescaleDB writes - .github/workflows/telemetry-ci.yml: new lan-sender CI job (parallel with integration/vcan), added to publish-telemetry gate Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
get_sync_client() now retries up to 10 times with exponential backoff (1s, 1.5s, 2.25s, ...) instead of failing immediately. This handles the race where Redis is still starting when data.py boots. Also increased start_period on base and Redis healthchecks in docker-compose.lan-sender-test.yml to give TimescaleDB and Redis adequate warm-up time before data.py connects. Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
data.py retries Redis connection 5 times with backoff (~11s total), but in CI Redis takes ~70s to initialise its aoffs database before accepting connections. With an 80s sleep after docker compose up, Redis is ready by the time data.py starts connecting. Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
REDIS_URL was set to redis://base-redis:***@timescaledb:5432/wfr which points at the TimescaleDB PostgreSQL port instead of the Redis port. Redis has no password, so the URL is simply redis://base-redis:6379/0. Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
lan_sender.py was running on the GHA host and sending UDP to the container's port 5005. Docker's userland UDP proxy on the host silently drops packets when forwarding to containers, so data.py never received any packets despite Redis being correctly connected. Fix: run 'docker exec -d daq-lan-base python3 /app/src/lan_sender.py' so lan_sender shares the container's network namespace and reaches data.py's UDP socket directly. Also add lan_sender.sh to the Docker image so it is available inside the container at /app/lan_sender.sh. Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
TimescaleBridge publishes timescale:status every 10 seconds. The previous 3-second fixed sleep was insufficient. Now polls once per second for up to 15 seconds. Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1685eb9f5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| def test_timescaledb_writing(self, redis_helper): | ||
| import os | ||
| if os.getenv("ENABLE_TIMESCALE_LOGGING", "").lower() != "true": | ||
| pytest.skip("ENABLE_TIMESCALE_LOGGING not set — TimescaleBridge disabled") |
There was a problem hiding this comment.
Add an assertion path for Timescale write verification
When ENABLE_TIMESCALE_LOGGING=true, this test currently performs no checks and returns without any assertions, so it passes even if Timescale ingestion is broken. That means CI cannot detect regressions in the Timescale path despite the test name and docstring claiming write verification.
Useful? React with 👍 / 👎.
TimescaleBridge requires ENABLE_TIMESCALE_LOGGING=true, which is not set in docker-compose.lan-sender-test.yml. The test was always going to fail in this configuration. Skip it with a clear message indicating what flag to set if the user wants to test TimescaleDB. Signed-off-by: Haorui Zhou <haorui2002@gmail.com>
Introduces a self-contained UDP sender that emits properly-formatted CAN batches (matching data.py's udp_receiver protocol) so the base station pipeline can be exercised with no car or CAN hardware present.
What changed
CI job
The new
lan-senderjob runs the base-only stack and lan_sender.py directly on the GHA runner, providing a fast smoke test that doesn't require the full car simulation container.