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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# The contributor harness hashes provenance inputs byte-for-byte. Keep text
# files in this boundary on LF even when Windows enables core.autocrlf.
harness/ruview/** text=auto eol=lf
harness/homecore/** text=auto eol=lf
20 changes: 19 additions & 1 deletion firmware/esp32-csi-node/provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,16 @@ def _default_state_dir() -> str:
return os.path.join(base, "wifi-densepose", "esp32-provision-state")


_RESERVED_DEVICE_RE = re.compile(
r"^(con|prn|aux|nul|com[1-9]|lpt[1-9])$", re.IGNORECASE
)


def _state_path_for(port: str, state_dir: str) -> str:
"""File path for a given serial port. Sanitize the port for filesystem use."""
safe = port.replace("/", "_").replace(":", "_").replace("\\", "_")
if _RESERVED_DEVICE_RE.match(safe):
safe = f"_{safe}"
return os.path.join(state_dir, f"{safe}.json")


Expand Down Expand Up @@ -306,7 +313,13 @@ def load_state(port: str, state_dir: str) -> dict:
"""Return the merged-state dict for `port`, or `{}` if absent / unreadable."""
path = _state_path_for(port, state_dir)
if not os.path.isfile(path):
return {}
legacy = os.path.join(
state_dir, f"{port.replace('/', '_').replace(':', '_').replace('\\', '_')}.json"
)
if legacy != path and os.path.isfile(legacy):
path = legacy
else:
return {}
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
Expand Down Expand Up @@ -443,6 +456,11 @@ def load_board_state(port: str, mac, state_dir: str):
legacy = _state_path_for(port, state_dir)
if os.path.isfile(legacy):
return load_state(port, state_dir), legacy
unprefixed = os.path.join(
state_dir, f"{port.replace('/', '_').replace(':', '_').replace('\\', '_')}.json"
)
if unprefixed != legacy and os.path.isfile(unprefixed):
return load_state(port, state_dir), unprefixed
return {}, None


Expand Down
11 changes: 10 additions & 1 deletion firmware/esp32-csi-node/tests/test_provision_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,16 @@ def test_slashes_in_port_are_safe(self):

def test_windows_com_port_is_safe(self):
path = provision._state_path_for("COM7", "/tmp/x")
self.assertTrue(path.endswith("COM7.json"))
self.assertTrue(path.endswith("_COM7.json"))

def test_reserved_dos_device_names_are_prefixed(self):
for name in ["CON", "PRN", "AUX", "NUL", "COM1", "COM9", "LPT1"]:
path = provision._state_path_for(name, "/tmp/x")
self.assertEqual(os.path.basename(path), f"_{name}.json")

def test_non_reserved_ports_remain_unprefixed(self):
path = provision._state_path_for("/dev/ttyUSB0", "/tmp/x")
self.assertEqual(os.path.basename(path), "_dev_ttyUSB0.json")


if __name__ == "__main__":
Expand Down