From e4e339ce1273ef248f94ffef103f075a75d236c1 Mon Sep 17 00:00:00 2001 From: Paul Barrett Date: Fri, 25 Sep 2026 20:16:41 +0100 Subject: [PATCH] Install cs---engine-shed-2, and stop the tests snapshotting INSTALLED Goods Shed's detector is out of service, so cs---goods-shed leaves INSTALLED. The config tests now check properties of whatever is installed, so a bring-up no longer turns the suite red and blocks deploy.sh. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 5 +++-- README.md | 4 ++-- docs/pin-allocation.md | 12 +++++++++--- src/apps/io-node/config.py | 9 +++++---- tests/test_io_node_config.py | 35 ++++++++++++++++++++--------------- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2128e92..f49cd6f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -109,8 +109,9 @@ bench check that covers it instead. Do not leave it silently untested. ## Current state (2026-08) -The IO node is **deployed and working end to end** (#1, #2, #3). Both Goods Shed sensors -publish contract readings that the orchestrator trusts, and the block follows them. +The IO node is **deployed and working end to end** (#1, #2, #3). Installed: `cs---engine-shed-2` +(a `bazauto/block-detection` channel) and `ir---goods-shed`. Goods Shed has lost its detector, +so that block can go occupied but not clear (`docs/pin-allocation.md`). Point position feedback on board 3 (`0x22`) is **built but publishes nothing yet** (#15): the expander answers on the bus but `POINTS_INSTALLED` is empty, and **the feedback source is diff --git a/README.md b/README.md index be6df80..dedb856 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,8 @@ means it is running. The codes are in `docs/startup-and-status-led.md`. ## Status -The IO node is deployed and working end to end. Both Goods Shed sensors, one current-sensing -and one IR, publish contract readings that the orchestrator trusts. +The IO node is deployed and working end to end. Two sensors publish contract readings: the +Engine Shed 2 current detector and the Goods Shed IR beam. Still open: diff --git a/docs/pin-allocation.md b/docs/pin-allocation.md index b86d0ec..f61e82e 100644 --- a/docs/pin-allocation.md +++ b/docs/pin-allocation.md @@ -164,20 +164,26 @@ wire it to its allocated pin, add its id to the installed list, deploy. ## Currently installed -Two, both on the **Goods Shed** block: +Two, on different blocks (2026-09-25): | Board | Pin | Port | Chip pin | `sensorId` | |---|---|---|---|---| -| 1 (`0x20`) | 8 | GPB0 | 1 | `cs---goods-shed` | +| 1 (`0x20`) | 7 | GPA7 | 28 | `cs---engine-shed-2` | | 2 (`0x21`) | 8 | GPB0 | 1 | `ir---goods-shed` | +`cs---goods-shed` came out of `INSTALLED` when its LM-iD.1 was removed. Goods Shed now has +only its beam, and an `ir_position` `clear` is a no-op downstream, so **that block can go +occupied but can never clear** until a detector is back on board 1 pin 8. The orchestrator +also stops hearing `cs---goods-shed`, marks it untrusted, and treats the block as `unknown` +(occupied). Both are the fail-safe direction. + **No points.** Board 3 is fitted and answers on the bus, but no feedback source has been chosen or wired, so `POINTS_INSTALLED` is empty and nothing is published on any `point/*/reading`. Bring points up **one at a time**: every point fault kind Safe-Stops the whole layout, including a fault on a point no route holds, so flipping all six to `positionFeedback: "required"` at once produces a layout that halts on the first flaky contact with five other unproven points to rule out. -Two things follow from this being the pair brought up first, both of which paid off: +Goods Shed's two sensors were the pair brought up first, and two things followed from that, both of which paid off: - **Goods Shed is the only block with both a CS and an IR sensor installed**, so the bring-up exercised the occupancy derivation rather than one sensor in isolation. That proved its diff --git a/src/apps/io-node/config.py b/src/apps/io-node/config.py index b9ceeec..3f1d956 100644 --- a/src/apps/io-node/config.py +++ b/src/apps/io-node/config.py @@ -111,13 +111,14 @@ "active_low": WAVESHARE_IR_ACTIVE_LOW}, ) -# Physically wired, and therefore the only sensors published. Both on Goods Shed, which -# is the one block with both a detector and a beam — so the bring-up exercises the -# occupancy derivation rather than one sensor in isolation. Every board 1 entry above is +# Physically wired, and therefore the only sensors published. The Goods Shed detector +# was taken out of service 2026-09-25 when its LM-iD.1 was removed; its beam stays. A +# block whose only live sensor is IR can go occupied but never clear, because an +# `ir_position` `clear` is a no-op downstream. Every board 1 entry above is # already on the bazauto/block-detection polarity, so bringing another detector online is # only a matter of adding its id here. INSTALLED = ( - "cs---goods-shed", + "cs---engine-shed-2", "ir---goods-shed", ) diff --git a/tests/test_io_node_config.py b/tests/test_io_node_config.py index 80f795b..8bd7125 100644 --- a/tests/test_io_node_config.py +++ b/tests/test_io_node_config.py @@ -27,18 +27,21 @@ def test_the_layout_id_is_real(): assert config.LAYOUT_ID == "c4e587aa-478d-46ab-a1df-9dd8359fc040" -def test_both_installed_sensors_are_the_goods_shed_pair(): - """The one block with both a detector and a beam, so the bring-up exercises the - occupancy derivation rather than one sensor in isolation.""" - assert set(config.INSTALLED) == {"cs---goods-shed", "ir---goods-shed"} +def test_every_installed_sensor_is_on_the_board_its_prefix_names(): + """`cs---` is current sensing on board 1, `ir---` is IR on board 2. A sensor on the + wrong board would be read with the other device's polarity, upside down. + Deliberately not a snapshot of INSTALLED: bringing a sensor online is a config change + and should not also need a test edit.""" + board_for = {"cs---": config.EXPANDER_CS, "ir---": config.EXPANDER_IR} + for entry in select_installed(config.SENSORS, config.INSTALLED): + prefix = entry["sensor_id"][:len("cs---")] + assert prefix in board_for, entry["sensor_id"] + assert entry["expander"] == board_for[prefix], entry["sensor_id"] -def test_installed_sensors_resolve_to_the_documented_pins(): - """docs/pin-allocation.md: both on pin 8, board 1 for CS and board 2 for IR.""" - by_id = {e["sensor_id"]: e for e in select_installed(config.SENSORS, config.INSTALLED)} - assert (by_id["cs---goods-shed"]["expander"], by_id["cs---goods-shed"]["pin"]) == (0x20, 8) - assert (by_id["ir---goods-shed"]["expander"], by_id["ir---goods-shed"]["pin"]) == (0x21, 8) +def test_installed_sensors_are_not_duplicated(): + assert len(set(config.INSTALLED)) == len(config.INSTALLED) def test_pin_n_is_the_same_block_on_both_boards(): @@ -55,12 +58,14 @@ def test_pin_n_is_the_same_block_on_both_boards(): def test_the_installed_sensors_have_their_devices_polarity(): - """Goods Shed's detector is the bazauto/block-detection board, occupied reads 1; its - beam is a Waveshare IR switch to ground, triggered reads 0.""" - by_id = {e["sensor_id"]: e for e in select_installed(config.SENSORS, config.INSTALLED)} - - assert by_id["cs---goods-shed"]["active_low"] is config.BAZAUTO_BD_ACTIVE_LOW is False - assert by_id["ir---goods-shed"]["active_low"] is config.WAVESHARE_IR_ACTIVE_LOW is True + """Detectors are the bazauto/block-detection board, occupied reads 1; beams are + Waveshare IR switches to ground, triggered reads 0.""" + expected = {config.EXPANDER_CS: config.BAZAUTO_BD_ACTIVE_LOW, + config.EXPANDER_IR: config.WAVESHARE_IR_ACTIVE_LOW} + assert config.BAZAUTO_BD_ACTIVE_LOW is False + assert config.WAVESHARE_IR_ACTIVE_LOW is True + for entry in select_installed(config.SENSORS, config.INSTALLED): + assert entry["active_low"] is expected[entry["expander"]], entry["sensor_id"] def test_every_current_sensor_uses_the_bazauto_detector_polarity():