Fall back to the node's own access point when a join fails #50
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/wifi-ap-fallback"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A join that has not reached state 2 within 30 s starts the node's own open access point and reports state 3, so a dead bridge or bad credentials leaves the bus as the only thing that has to tell you the node is reachable. CCS-UF-7.
Two decisions a reviewer should check:
The SSID now comes from the node's own label rather than a string in each
main.cpp:van-plus the label lowercased, with each run of anything but a letter or digit collapsed to one-. A label that is absent or normalises to nothing givesvan-node-<id>in hex. That drops theapSsidconstructor argument. Both nodes keep the SSIDs they had.One existing test changed:
test_a_failure_on_the_air_is_reported_once_and_times_outasserted the radio idled out 60 s after a failure on the air. The fallback now fires at 30 s and owns that moment, so it stops before then, and a state 5 session idling out is pinned instead bytest_a_link_lost_after_joining_does_not_fall_back_and_times_out. The state 5 reporting itself is unchanged.Spec section 11 gains the naming rule, the 15 minute timeout and the no-join case. No
WiFi.hchange was needed —WiFi.softAP(ssid)with no key is already open.Tested with
pio test -e native(316 cases), both node environments andhost_sim.ae8ee3b72198d7b4eef1Five points, the first is the only one that matters.
@ -987,1 +987,4 @@The node name is the node's own label from `SYS_ENDPOINT_LABEL` index `0xFF`,lowercased with runs of anything but a letter or digit as one `-`, or`van-node-<id>` in lower-case hex where a node carries no label. No controlTwo gaps against the code.
nameApdrops leading and trailing separators, so" Host simulator (2) "givesvan-host-simulator-2, not thevan--host-simulator-2-this wording describes. And the id form is always two hex digits (van-node-10for0x10), which "in lower-case hex" leaves open.@ -153,0 +181,4 @@uint8_t length = apPrefixLength;const char* label = node.label();for (uint8_t i = 0; label != nullptr && i < can::labelMaxLength && label[i] != '\0'; ++i) {can::encodeLabelcuts a label at its first non-printable byte; this maps that byte to-and keeps going. So a label containing one gives an SSID carrying text thatSYS_ENDPOINT_LABELnever broadcasts, while the spec says the name is that label. No current label is affected, but stopping the loop at the first non-printable would make the two agree.@ -435,0 +537,4 @@node.loop(t0 + 2 * ctrl::wifiJoinFallbackMs);TEST_ASSERT_EQUAL_UINT8(0, radio.apCalls);assertState(can::wifi_state::failed);}Coverage dropped here. The half deleted from
test_a_failure_on_the_air_is_reported_once_and_times_outwas the only assertion that the radio idles off from statefailed; nothing took it over. Adding&& state_ != can::wifi_state::failedto the deadline check inwifi_manager.cpp:93leaves all 41 tests in this directory green. Extend this test — it is now the only path that reaches a lastingfailedstate — with a loop past the session timeout assertingoffCalls == 1and state off.@ -435,0 +540,4 @@}// The access point replaces the station session, so the update it was joining// for is abandoned with it.Delete this comment — the test name already says the update is abandoned, and it is a near-duplicate of the one on
test_an_access_point_started_mid_transfer_ends_the_session. Same for the comment ontest_a_restarted_join_gets_the_full_thirty_seconds, which restates its name, and the one ontest_the_fallback_access_point_takes_its_own_timeout, which repeats thestartApdeclaration comment in the header.@ -435,0 +582,4 @@TEST_ASSERT_EQUAL_UINT8(1, radio.apCalls);}void test_a_node_label_becomes_the_access_point_name() {Drop this:
test_action_2_starts_the_access_point_under_the_nodes_own_namealready assertsvan-lightingfrom the labelsetUpapplies, so the only new thing here is a second label.The 30 s arithmetic, the
joinPending_lifecycle andnameAp's buffer handling all hold up; the AP is open and the host/Arduino split is clean. Four things below.The PR body says 313 cases;
pio test -e nativereports 316.@ -987,1 +987,4 @@The node name is the node's own label from `SYS_ENDPOINT_LABEL` index `0xFF`,lowercased with runs of anything but a letter or digit as one `-`, or`van-node-<id>` in lower-case hex where a node carries no label. No controlTwo ways this wording is narrower than the code, and the spec is authoritative.
The rule as written — "runs of anything but a letter or digit as one
-" — givesvan--host-simulator-2-for" Host simulator (2) ". The code also drops the run that would lead (it collapses into the prefix's own dash) and trims the trailing one, givingvan-host-simulator-2. Say so.van-node-<id>is not only for a node that "carries no label": the code also uses it for an empty label and for one that normalises away entirely ("!!! ... ###"givesvan-node-10). Widen the clause.@ -435,0 +520,4 @@// A link that drops after joining is not the case this recovers: the session// worked once, so the radio idles out as it always did.void test_a_link_lost_after_joining_does_not_fall_back() {The coverage dropped from
test_a_failure_on_the_air_is_reported_once_and_times_outis not re-established.test_the_fallback_access_point_takes_its_own_timeoutpins the AP's 15 minute idle-out, not the station session timing out while the state is 5 — a different branch. Guarding the deadline check inloopwith&& state_ != can::wifi_state::failedpasses the whole suite, so nothing catches that regression now.This test is already in exactly that state. Add a loop at the session deadline asserting
offCalls == 1and state 0, which also makes the comment above ("the radio idles out as it always did") something the test checks rather than asserts in prose.@ -435,0 +559,4 @@assertState(can::wifi_state::apActive);}// A frame carrying later credentials is a new attempt, and gets its own 30 sDelete this comment. The test name and the two
hold/queuepairs beneath it already say a restarted join gets its own 30 s.@ -435,0 +608,4 @@TEST_ASSERT_EQUAL_STRING("van-host-simulator-2", radio.lastSsid);}void test_a_node_with_no_label_is_named_from_its_id() {The normalises-to-nothing branch is only reached here via
nullptr. Add a label that is all punctuation — it lands onvan-node-10too, and that is the case most likely to be broken by a later change to the collapsing loop.Fixed in
75b2fa7.test_a_link_lost_after_joining_does_not_fall_backnow loops on to the session deadline and assertsoffCalls == 1and state 0, so a state 5 session timing out is pinned again. Your mutation (&& state_ != can::wifi_state::failedon the deadline check) now fails that test and nothing else.-, with no leading or trailing-, andvan-node-<id>covers a label that is absent or leaves nothing after normalising.test_a_node_with_no_label_is_named_from_its_idbecame..._no_usable_label_...and now covers"!!! ###"alongsidenullptr.Not changing: that moving
joinPending_ = trueabove the credentials check inbeginJoinstill passes. It does, but only becausefail()→disable()→endSession()clears the flag andloopreturns early while the state is 0, so the flag is unreachable on that path. The behaviour is pinned; the mutation survives because a second guard catches it, which is not the same as missing coverage. Writing a test that fails for it would mean asserting on private state.75b2fa7clears all four. Confirmed independently:&& state_ != can::wifi_state::failedon the deadline check now fails onlytest_a_link_lost_after_joining_does_not_fall_back, and narrowing the id fallback toif (label == nullptr)fails onlytest_a_node_with_no_usable_label_is_named_from_its_id. Agreed on thejoinPending_ordering point — no action needed.Code is clean. One thing left, in the PR body, which is now stale in three places:
test_the_fallback_access_point_takes_its_own_timeout" — it lives intest_a_link_lost_after_joining_does_not_fall_back, which is the point of the second commit.van-node-<id>" — also a label that normalises to nothing, per the reworded spec.The assertion belongs where you put it: same scenario carried forward in time, and splitting it would duplicate the whole join-then-drop setup. The name no longer covers the second half, though — the UF-4 test it replaces said
_and_times_outfor exactly that reason.