Panel calibrate UX, check_concentrator extras, power summary AC hint
- Panel calibrate: Ctrl-C saves map during patch panel; skip/quit aliases (s/skip/., q/quit/exit); clearer prompts and help. - PCIe prompt: s/skip keeps metadata; note Ctrl-C in calibrate. - check_concentrator: --panel-calibrate, --inrush and options; compact Power line with 120V/220V AC amp hint from total W. - docs/brainstem-sdk.md: find How_To_Build.txt examples. Made-with: Cursor
This commit is contained in:
parent
a488dc2ed0
commit
93cd346a07
|
|
@ -36,7 +36,13 @@ That installs udev rules (typically requires `sudo`) and adds the current user t
|
||||||
|
|
||||||
## Build C/C++ examples from source
|
## Build C/C++ examples from source
|
||||||
|
|
||||||
Sample projects live under `api/examples/c_cpp/`. Each example includes `How_To_Build.txt` or `How_to_Build.txt` with Acroname’s steps. In general:
|
Sample projects live under `api/examples/c_cpp/`. Each example includes `How_To_Build.txt` or `How_to_Build.txt` with Acroname’s steps (note the full filename — `find -name How` will not match). From the directory that contains `api/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
find api/examples/c_cpp -maxdepth 2 -iname 'how*build*.txt' -print
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: `api/examples/c_cpp/BrainStem2-Cpp-Example/How_To_Build.txt`. In general:
|
||||||
|
|
||||||
1. Open that file in the example directory you care about.
|
1. Open that file in the example directory you care about.
|
||||||
2. Copy the **`lib`** directory from the SDK into the example tree where the readme says (headers and `BrainStem2` shared library).
|
2. Copy the **`lib`** directory from the SDK into the example tree where the readme says (headers and `BrainStem2` shared library).
|
||||||
|
|
|
||||||
|
|
@ -321,6 +321,7 @@ def main() -> int:
|
||||||
" fiwi.py panel calibrate [merge] [<N>] [--ssh user@host] …\n"
|
" fiwi.py panel calibrate [merge] [<N>] [--ssh user@host] …\n"
|
||||||
" calibrate: local hub ports first, then each --ssh host, calibrate_remotes in JSON, and/or\n"
|
" calibrate: local hub ports first, then each --ssh host, calibrate_remotes in JSON, and/or\n"
|
||||||
" FIWI_CALIBRATE_REMOTES in remote_ssh.env (comma-separated) for one-command hybrid.\n"
|
" FIWI_CALIBRATE_REMOTES in remote_ssh.env (comma-separated) for one-command hybrid.\n"
|
||||||
|
" Per port: s/skip/. skip · q/quit/exit stop · Ctrl-C save map & exit.\n"
|
||||||
" merge / N as before; remote steps set \"ssh\" on new fiber_ports entries.\n"
|
" merge / N as before; remote steps set \"ssh\" on new fiber_ports entries.\n"
|
||||||
" Calibrate starts by setting patch_panel.slots (front-panel positions); panel <n> is 1…slots.\n"
|
" Calibrate starts by setting patch_panel.slots (front-panel positions); panel <n> is 1…slots.\n"
|
||||||
" Use power fiber-port for arbitrary fiber ids beyond the panel if needed.\n"
|
" Use power fiber-port for arbitrary fiber ids beyond the panel if needed.\n"
|
||||||
|
|
|
||||||
|
|
@ -937,7 +937,8 @@ class FiWiConcentrator:
|
||||||
have = PatchPanel.from_map_blob(doc.get("patch_panel"))
|
have = PatchPanel.from_map_blob(doc.get("patch_panel"))
|
||||||
print(
|
print(
|
||||||
"\n--- Patch panel (front-panel positions) ---\n"
|
"\n--- Patch panel (front-panel positions) ---\n"
|
||||||
f"Fiber map keys 1…N refer to these panel positions (power/status: panel <N>).\n",
|
f"Fiber map keys 1…N refer to these panel positions (power/status: panel <N>).\n"
|
||||||
|
"Ctrl-C saves fiber_map.json and exits (patch panel may be unchanged).\n",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
if have is not None:
|
if have is not None:
|
||||||
|
|
@ -1018,7 +1019,16 @@ class FiWiConcentrator:
|
||||||
doc = {"fiber_ports": {}}
|
doc = {"fiber_ports": {}}
|
||||||
doc = fm.ensure_fiber_map_document(doc)
|
doc = fm.ensure_fiber_map_document(doc)
|
||||||
|
|
||||||
self._prompt_patch_panel(doc)
|
try:
|
||||||
|
self._prompt_patch_panel(doc)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(
|
||||||
|
"\n*** Calibrate interrupted (Ctrl-C) during patch panel; "
|
||||||
|
"writing fiber_map.json (unchanged or partial). ***\n",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
self._write_fiber_map_document(doc)
|
||||||
|
raise SystemExit(130)
|
||||||
self._write_fiber_map_document(doc)
|
self._write_fiber_map_document(doc)
|
||||||
|
|
||||||
seen_h = set()
|
seen_h = set()
|
||||||
|
|
@ -1208,8 +1218,10 @@ class FiWiConcentrator:
|
||||||
"After ON (~2s): fiwi snapshots wireless interfaces on that host (sysfs + lspci/iw) — "
|
"After ON (~2s): fiwi snapshots wireless interfaces on that host (sysfs + lspci/iw) — "
|
||||||
"local and SSH — for chip/interface in the map (no external fiwi script).\n"
|
"local and SSH — for chip/interface in the map (no external fiwi script).\n"
|
||||||
"Local steps: lsusb OFF→ON may also suggest a USB downstream device; USB lsusb is not used on SSH hosts.\n"
|
"Local steps: lsusb OFF→ON may also suggest a USB downstream device; USB lsusb is not used on SSH hosts.\n"
|
||||||
"Each step: ON → wlan snapshot → fiber id, s=skip, q=quit.\n"
|
"Each step: ON → wlan snapshot → fiber id — "
|
||||||
"Port stays ON through optional PCIe prompts, then powers OFF. Ctrl-C anytime saves fiber_map.json and exits.\n"
|
"s / skip / . = skip this port, q / quit / exit = stop calibrate.\n"
|
||||||
|
"Port stays ON through optional PCIe prompts, then powers OFF. "
|
||||||
|
"Ctrl-C saves fiber_map.json and exits (also during patch panel or PCIe prompts).\n"
|
||||||
"Remote rows: ssh + hub.port + wlan + pcie (usb_id/chip_type from lsusb are cleared; chip_type may come from wlan).\n"
|
"Remote rows: ssh + hub.port + wlan + pcie (usb_id/chip_type from lsusb are cleared; chip_type may come from wlan).\n"
|
||||||
"After each fiber id you can pick PCIe by number: 1–6 = known Adnacom H3 card, then SFP 1–4 "
|
"After each fiber id you can pick PCIe by number: 1–6 = known Adnacom H3 card, then SFP 1–4 "
|
||||||
"(no paste), or m=manual / c=clear / Enter=keep. Edit fiber_map.json anytime (see example).",
|
"(no paste), or m=manual / c=clear / Enter=keep. Edit fiber_map.json anytime (see example).",
|
||||||
|
|
@ -1342,7 +1354,7 @@ class FiWiConcentrator:
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
line = input(
|
line = input(
|
||||||
"Which fiber port id? [s=skip q=quit, Ctrl-C=save map & exit]: "
|
"Which fiber port id? [s/skip/.=skip q/quit/exit=done Ctrl-C=save & exit]: "
|
||||||
).strip()
|
).strip()
|
||||||
except EOFError:
|
except EOFError:
|
||||||
print(
|
print(
|
||||||
|
|
@ -1361,11 +1373,11 @@ class FiWiConcentrator:
|
||||||
raise SystemExit(130)
|
raise SystemExit(130)
|
||||||
|
|
||||||
low = line.lower()
|
low = line.lower()
|
||||||
if low == "q":
|
if low in ("q", "quit", "exit"):
|
||||||
if step_powered:
|
if step_powered:
|
||||||
self._calibrate_step_power_off(ssh_host, hub_1, port_0)
|
self._calibrate_step_power_off(ssh_host, hub_1, port_0)
|
||||||
break
|
break
|
||||||
if low == "s" or not line:
|
if low in ("s", "skip", ".") or not line:
|
||||||
if step_powered:
|
if step_powered:
|
||||||
self._calibrate_step_power_off(ssh_host, hub_1, port_0)
|
self._calibrate_step_power_off(ssh_host, hub_1, port_0)
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -305,10 +305,12 @@ def prompt_pcie_metadata_for_calibrate(existing_pcie):
|
||||||
print_catalog_menu()
|
print_catalog_menu()
|
||||||
try:
|
try:
|
||||||
r = input(
|
r = input(
|
||||||
" PCIe? [Enter=keep, 1–6=card from list, m=manual, c=clear]: "
|
" PCIe? [Enter/s/skip=keep, 1–6=card, m=manual, c=clear, Ctrl-C=save & exit calibrate]: "
|
||||||
).strip().lower()
|
).strip().lower()
|
||||||
except EOFError:
|
except EOFError:
|
||||||
return ("keep", None)
|
return ("keep", None)
|
||||||
|
if r in ("s", "skip"):
|
||||||
|
return ("keep", None)
|
||||||
if r in ("c", "clear"):
|
if r in ("c", "clear"):
|
||||||
return ("clear", None)
|
return ("clear", None)
|
||||||
if r in ("m", "manual"):
|
if r in ("m", "manual"):
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ and ``port-metrics-json``; the remote tree must include that command (same revis
|
||||||
|
|
||||||
python tests/check_concentrator.py
|
python tests/check_concentrator.py
|
||||||
python tests/check_concentrator.py --config uax24
|
python tests/check_concentrator.py --config uax24
|
||||||
|
python tests/check_concentrator.py --inrush 1 0
|
||||||
|
python tests/check_concentrator.py --inrush 1 0 --inrush-host-sample --inrush-json
|
||||||
|
python tests/check_concentrator.py --panel-calibrate
|
||||||
|
python tests/check_concentrator.py --panel-calibrate --calibrate-merge --calibrate-ssh pi@192.168.1.39
|
||||||
|
|
||||||
``--config`` matches ``FIWI_CONFIG`` (profile or absolute ``*.ini``).
|
``--config`` matches ``FIWI_CONFIG`` (profile or absolute ``*.ini``).
|
||||||
|
|
||||||
|
|
@ -25,6 +29,16 @@ and ``port-metrics-json``; the remote tree must include that command (same revis
|
||||||
``FIWI_CALIBRATE_REMOTES`` / merged hub hosts: all ports OFF (verify), then all ON (verify),
|
``FIWI_CALIBRATE_REMOTES`` / merged hub hosts: all ports OFF (verify), then all ON (verify),
|
||||||
then prints the port power table (snapshot after the test).
|
then prints the port power table (snapshot after the test).
|
||||||
|
|
||||||
|
``--inrush HUB PORT`` (after the report, or after ``--powercycle``) runs the same USB inrush probe as
|
||||||
|
``tests/check_inrush.py`` on **local** hubs only (1-based hub index, 0-based port). Use
|
||||||
|
``--inrush-host-sample`` for host-side polling; default is on-hub Reflex scratchpad.
|
||||||
|
|
||||||
|
``--panel-calibrate`` runs the interactive **fiber map** workflow (patch panel size, USB port walk,
|
||||||
|
wlan + lspci snapshot per step → ``chip_type`` / ``wlan`` / ``hub``+``port`` in ``fiber_map.json``).
|
||||||
|
Same as ``python3 fiwi.py panel calibrate``. Use ``--calibrate-merge``, ``--calibrate-limit N``,
|
||||||
|
and repeatable ``--calibrate-ssh user@host``. Needs a TTY; cannot combine with ``--powercycle`` or
|
||||||
|
``--inrush``.
|
||||||
|
|
||||||
With pytest::
|
With pytest::
|
||||||
|
|
||||||
FIWI_CONFIG=uax24 pytest tests/check_concentrator.py
|
FIWI_CONFIG=uax24 pytest tests/check_concentrator.py
|
||||||
|
|
@ -850,8 +864,15 @@ def _print_per_port_power_table(
|
||||||
lines_out.append((st, line))
|
lines_out.append((st, line))
|
||||||
|
|
||||||
if n_ma and n_power:
|
if n_ma and n_power:
|
||||||
|
# Literal "120V/220V=…A/…A" suffix (labels, not f-string slash parsing).
|
||||||
summary = (
|
summary = (
|
||||||
f"Power({n_on}): Total {total_power_w:.3f} W / {total_ma:.2f} mA (per port follows)"
|
"Power({n}): Total {tw:.3f} W / {tm:.2f} mA (120V/220V={a120:.2f}A/{a220:.2f}A)"
|
||||||
|
).format(
|
||||||
|
n=n_on,
|
||||||
|
tw=total_power_w,
|
||||||
|
tm=total_ma,
|
||||||
|
a120=total_power_w / 120.0,
|
||||||
|
a220=total_power_w / 220.0,
|
||||||
)
|
)
|
||||||
elif n_ma:
|
elif n_ma:
|
||||||
summary = (
|
summary = (
|
||||||
|
|
@ -928,6 +949,177 @@ def _print_pcie_catalog_section() -> None:
|
||||||
print(flush=True)
|
print(flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _prepend_tests_dir_to_syspath() -> None:
|
||||||
|
td = os.path.join(_ROOT, "tests")
|
||||||
|
if td not in sys.path:
|
||||||
|
sys.path.insert(0, td)
|
||||||
|
|
||||||
|
|
||||||
|
def _run_inrush_probe(c: FiWiConcentrator, args: argparse.Namespace) -> int:
|
||||||
|
"""Run :mod:`check_inrush` measurement after the hub report; **local** hubs only."""
|
||||||
|
assert args.inrush is not None
|
||||||
|
hub_1, port_0 = int(args.inrush[0]), int(args.inrush[1])
|
||||||
|
if hub_1 < 1:
|
||||||
|
print("check_concentrator --inrush: hub must be >= 1", file=sys.stderr, flush=True)
|
||||||
|
return 2
|
||||||
|
if port_0 < 0:
|
||||||
|
print("check_concentrator --inrush: port must be >= 0", file=sys.stderr, flush=True)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
_prepend_tests_dir_to_syspath()
|
||||||
|
import check_inrush as ci # noqa: E402
|
||||||
|
|
||||||
|
if not c.hubs:
|
||||||
|
if not c.connect():
|
||||||
|
print(
|
||||||
|
"check_concentrator --inrush: no local USB power-control hubs connected.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
hi = hub_1 - 1
|
||||||
|
if hi < 0 or hi >= len(c.hubs):
|
||||||
|
print(
|
||||||
|
f"check_concentrator --inrush: hub {hub_1} invalid (have {len(c.hubs)} hub(s)).",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
stem = c.hubs[hi]
|
||||||
|
n = c._port_count(stem)
|
||||||
|
if port_0 < 0 or port_0 >= n:
|
||||||
|
print(
|
||||||
|
f"check_concentrator --inrush: port {port_0} out of range for hub {hub_1} (0..{n - 1}).",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if not args.inrush_host_sample and port_0 != 0:
|
||||||
|
print(
|
||||||
|
"check_concentrator --inrush: on-hub reflex/inrush.reflex monitors port 0 only; "
|
||||||
|
f"you asked for port {port_0}. Recompile Reflex or use --inrush-host-sample.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
off_s = max(args.inrush_off_ms / 1000.0, 0.0)
|
||||||
|
sample_s = max(args.inrush_sample_ms / 1000.0, 0.01)
|
||||||
|
interval_s = max(args.inrush_interval_ms / 1000.0, 0.0005)
|
||||||
|
power_cycle = not args.inrush_no_power_cycle
|
||||||
|
|
||||||
|
if args.inrush_host_sample:
|
||||||
|
out = ci._measure_inrush_host(
|
||||||
|
c,
|
||||||
|
hi,
|
||||||
|
port_0,
|
||||||
|
off_s=off_s,
|
||||||
|
sample_s=sample_s,
|
||||||
|
interval_s=interval_s,
|
||||||
|
threshold_ma=args.inrush_threshold_ma,
|
||||||
|
power_cycle=power_cycle,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
out = ci._measure_inrush_on_hub(
|
||||||
|
c,
|
||||||
|
hi,
|
||||||
|
port_0,
|
||||||
|
off_s=off_s,
|
||||||
|
sample_s=sample_s,
|
||||||
|
power_cycle=power_cycle,
|
||||||
|
store_index=args.inrush_map_store_index,
|
||||||
|
map_slot=args.inrush_map_slot,
|
||||||
|
pointer_index=args.inrush_pointer_index,
|
||||||
|
rearm_map=not args.inrush_no_rearm_map,
|
||||||
|
)
|
||||||
|
|
||||||
|
if out.get("error") == "rearm_map_failed":
|
||||||
|
print(
|
||||||
|
"check_concentrator --inrush: could not re-arm map (store slotDisable/Enable failed). "
|
||||||
|
"Load reflex/inrush.map into --inrush-map-store-index / --inrush-map-slot, "
|
||||||
|
"or try --inrush-no-rearm-map.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
if out.get("error") == "scratchpad_read_failed":
|
||||||
|
print(
|
||||||
|
"check_concentrator --inrush: scratchpad read failed (pointer offset / hub type). "
|
||||||
|
"Confirm inrush.map is loaded, mapEnable ran, and pointer index matches Reflex.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(flush=True)
|
||||||
|
print("--- USB inrush (--inrush) ---", flush=True)
|
||||||
|
print(flush=True)
|
||||||
|
|
||||||
|
if args.inrush_json:
|
||||||
|
print(json.dumps(out), flush=True)
|
||||||
|
else:
|
||||||
|
ptr_idx = args.inrush_pointer_index
|
||||||
|
if out.get("mode") == "on-hub":
|
||||||
|
print(
|
||||||
|
f"Hub {out['hub']} port {out['port']} serial {out['serial']}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(f" mode: on-hub reflex (scratchpad via pointer {ptr_idx})", flush=True)
|
||||||
|
print(
|
||||||
|
f" peak current: {out['peak_ma']} mA (peak_ua={out['peak_ua']})",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" hub ticks: {out['ticks']} @ {out['sample_period_us']} µs → "
|
||||||
|
f"~{out['observation_us']} µs on timer grid",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" > {ci._REFLEX_THRESHOLD_MA} mA (reflex): ~{out['above_threshold_us']} µs "
|
||||||
|
f"({out['above_threshold_ticks']} ticks)",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" host wait: {out['sample_window_ms']} ms power_cycled: {out['power_cycled']} "
|
||||||
|
f"store[{out['map_store_index']}] slot {out['map_slot']}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
if out["ticks"] == 0 and out.get("sample_period_us", 0) == 0:
|
||||||
|
print(
|
||||||
|
" ! ticks and sample_period_us are 0 — map likely not running or wrong pointer.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
fat = out["first_above_threshold_ms"]
|
||||||
|
fat_s = f"{fat} ms" if fat is not None else "n/a"
|
||||||
|
print(
|
||||||
|
f"Hub {out['hub']} port {out['port']} serial {out['serial']}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(" mode: host-sample (getPortCurrent loop)", flush=True)
|
||||||
|
print(
|
||||||
|
f" peak current: {out['peak_ma']} mA "
|
||||||
|
f"(when max first rose: {out['peak_time_ms']} ms)",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" first > {out['above_threshold_ma']} mA: {fat_s}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" > {out['above_threshold_ma']} mA for ~{out['above_threshold_ms']} ms "
|
||||||
|
f"({out['sample_count']} samples @ {out['interval_ms']} ms; wall {out['elapsed_ms']} ms)",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f" nominal window: {out['sample_window_ms']} ms power_cycled: {out['power_cycled']}",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
print(flush=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def _print_consolidated_report(c: FiWiConcentrator) -> int:
|
def _print_consolidated_report(c: FiWiConcentrator) -> int:
|
||||||
_print_ssh_and_hosts_summary()
|
_print_ssh_and_hosts_summary()
|
||||||
hub_rows, remote_rc = _build_consolidated_hub_rows(c)
|
hub_rows, remote_rc = _build_consolidated_hub_rows(c)
|
||||||
|
|
@ -944,7 +1136,10 @@ def test_concentrator() -> None:
|
||||||
|
|
||||||
def _parse_args() -> argparse.Namespace:
|
def _parse_args() -> argparse.Namespace:
|
||||||
p = argparse.ArgumentParser(
|
p = argparse.ArgumentParser(
|
||||||
description="Check FiWiConcentrator: consolidated local + remote USB hub table.",
|
description=(
|
||||||
|
"FiWiConcentrator check: consolidated USB hub table, optional inrush probe, "
|
||||||
|
"or interactive panel calibrate (fiber_map.json — hub/port, chip_type, patch panel)."
|
||||||
|
),
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog=(
|
epilog=(
|
||||||
"PROFILE selects <repo>/config/<PROFILE>.ini (e.g. uax24, uax4, default).\n"
|
"PROFILE selects <repo>/config/<PROFILE>.ini (e.g. uax24, uax4, default).\n"
|
||||||
|
|
@ -966,9 +1161,184 @@ def _parse_args() -> argparse.Namespace:
|
||||||
"(disrupts USB power)."
|
"(disrupts USB power)."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
p.add_argument(
|
||||||
|
"--inrush",
|
||||||
|
nargs=2,
|
||||||
|
type=int,
|
||||||
|
metavar=("HUB", "PORT"),
|
||||||
|
help=(
|
||||||
|
"After the report (or after --powercycle), run USB inrush on this **local** hub "
|
||||||
|
"(1-based) and downstream port (0-based). Same behavior as tests/check_inrush.py."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ir = p.add_argument_group("inrush options (only with --inrush)")
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-host-sample",
|
||||||
|
action="store_true",
|
||||||
|
help="Poll getPortCurrent on the host instead of on-hub Reflex scratchpad",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-json",
|
||||||
|
action="store_true",
|
||||||
|
help="Print one JSON object for the inrush result",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-off-ms",
|
||||||
|
type=float,
|
||||||
|
default=250.0,
|
||||||
|
help="ms downstream port off before re-enable when power-cycling (default 250)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-sample-ms",
|
||||||
|
type=float,
|
||||||
|
default=500.0,
|
||||||
|
help="Host wait after re-arm / power-on while hub samples (default 500)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-interval-ms",
|
||||||
|
type=float,
|
||||||
|
default=1.0,
|
||||||
|
help="(host-sample) ms between getPortCurrent reads (default 1)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-threshold-ma",
|
||||||
|
type=float,
|
||||||
|
default=50.0,
|
||||||
|
help="(host-sample) threshold mA for above-threshold tally (default 50)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-no-power-cycle",
|
||||||
|
action="store_true",
|
||||||
|
help="Do not disable/enable the downstream port before measure",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-map-store-index",
|
||||||
|
type=int,
|
||||||
|
default=1,
|
||||||
|
help="stem.store index for slotDisable/Enable (default 1)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-map-slot",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help="Store slot where inrush.map is loaded (default 0)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-pointer-index",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help="stem.pointer index for Reflex scratchpad (default 0)",
|
||||||
|
)
|
||||||
|
ir.add_argument(
|
||||||
|
"--inrush-no-rearm-map",
|
||||||
|
action="store_true",
|
||||||
|
help="Do not slotDisable/slotEnable before measure (on-hub mode)",
|
||||||
|
)
|
||||||
|
p.add_argument(
|
||||||
|
"--panel-calibrate",
|
||||||
|
action="store_true",
|
||||||
|
help=(
|
||||||
|
"Interactive fiber_map.json build: patch panel slots, walk each USB downstream port "
|
||||||
|
"(one powered at a time), capture wlan+lspci for chip_type, map hub.port → panel id. "
|
||||||
|
"Same as 'python3 fiwi.py panel calibrate'. Requires a TTY; not with --powercycle/--inrush."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
cal = p.add_argument_group("panel calibrate (only with --panel-calibrate)")
|
||||||
|
cal.add_argument(
|
||||||
|
"--calibrate-merge",
|
||||||
|
action="store_true",
|
||||||
|
help="Merge into existing fiber_map.json instead of clearing fiber_ports",
|
||||||
|
)
|
||||||
|
cal.add_argument(
|
||||||
|
"--calibrate-limit",
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
metavar="N",
|
||||||
|
help="Stop after the first N USB calibrate steps (hub.port walks)",
|
||||||
|
)
|
||||||
|
cal.add_argument(
|
||||||
|
"--calibrate-ssh",
|
||||||
|
action="append",
|
||||||
|
default=None,
|
||||||
|
metavar="USER@HOST",
|
||||||
|
help="Remote hub host for calibrate steps (repeat for several); also uses calibrate_remotes / env",
|
||||||
|
)
|
||||||
return p.parse_args()
|
return p.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def _calibrate_flags_without_panel(args: argparse.Namespace) -> bool:
|
||||||
|
if args.panel_calibrate:
|
||||||
|
return False
|
||||||
|
if args.calibrate_merge:
|
||||||
|
return True
|
||||||
|
if args.calibrate_limit is not None:
|
||||||
|
return True
|
||||||
|
if args.calibrate_ssh:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _main_panel_calibrate(args: argparse.Namespace, label: str) -> int:
|
||||||
|
"""Run :meth:`FiWiConcentrator.panel_calibrate` (writes ``fiber_map.json``)."""
|
||||||
|
if args.powercycle or args.inrush is not None:
|
||||||
|
print(
|
||||||
|
"check_concentrator: --panel-calibrate cannot be combined with --powercycle or --inrush.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 2
|
||||||
|
if not sys.stdin.isatty():
|
||||||
|
print(
|
||||||
|
"check_concentrator: --panel-calibrate needs an interactive TTY (e.g. ssh -t host).",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
_ensure_paths_configured()
|
||||||
|
from fiwi.brainstem_loader import load_brainstem
|
||||||
|
|
||||||
|
print(_rule("="), flush=True)
|
||||||
|
print(f"Fi-Wi panel calibrate → fiber_map.json [config: {label}]", flush=True)
|
||||||
|
print(_rule("="), flush=True)
|
||||||
|
print(flush=True)
|
||||||
|
print(
|
||||||
|
"Maps each USB hub downstream port to a patch-panel fiber id; records wlan snapshot "
|
||||||
|
"(sysfs + lspci/iw) as chip_type / wlan, optional PCIe metadata.\n"
|
||||||
|
"Keys: s / skip / . = skip port · q / quit / exit = stop · Ctrl-C = save fiber_map.json & exit.\n"
|
||||||
|
"Equivalent to: python3 fiwi.py panel calibrate "
|
||||||
|
+ ("merge " if args.calibrate_merge else "")
|
||||||
|
+ (f"{args.calibrate_limit} " if args.calibrate_limit is not None else "")
|
||||||
|
+ " ".join(f"--ssh {h}" for h in (args.calibrate_ssh or []))
|
||||||
|
+ "\n",
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
c = None
|
||||||
|
try:
|
||||||
|
load_brainstem()
|
||||||
|
c = _instantiate_concentrator()
|
||||||
|
c.panel_calibrate(
|
||||||
|
merge=args.calibrate_merge,
|
||||||
|
limit=args.calibrate_limit,
|
||||||
|
calibrate_ssh_hosts=list(args.calibrate_ssh or []),
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"FAIL: {exc}", file=sys.stderr, flush=True)
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
if c is not None:
|
||||||
|
try:
|
||||||
|
c.disconnect()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print(_rule("="), flush=True)
|
||||||
|
print(f"Panel calibrate finished [config: {label}]", flush=True)
|
||||||
|
print(_rule("="), flush=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
try:
|
try:
|
||||||
os.chdir(_ROOT)
|
os.chdir(_ROOT)
|
||||||
|
|
@ -980,6 +1350,19 @@ def main() -> int:
|
||||||
os.environ["FIWI_CONFIG"] = args.config.strip()
|
os.environ["FIWI_CONFIG"] = args.config.strip()
|
||||||
|
|
||||||
label = os.environ.get("FIWI_CONFIG", "default (config/default.ini if present)")
|
label = os.environ.get("FIWI_CONFIG", "default (config/default.ini if present)")
|
||||||
|
|
||||||
|
if _calibrate_flags_without_panel(args):
|
||||||
|
print(
|
||||||
|
"check_concentrator: --calibrate-merge / --calibrate-limit / --calibrate-ssh "
|
||||||
|
"require --panel-calibrate.",
|
||||||
|
file=sys.stderr,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if args.panel_calibrate:
|
||||||
|
return _main_panel_calibrate(args, label)
|
||||||
|
|
||||||
_print_banner(label)
|
_print_banner(label)
|
||||||
|
|
||||||
c = None
|
c = None
|
||||||
|
|
@ -995,6 +1378,8 @@ def main() -> int:
|
||||||
remote_fail = _print_per_port_power_table(c, hub_rows, remote_fail)
|
remote_fail = _print_per_port_power_table(c, hub_rows, remote_fail)
|
||||||
else:
|
else:
|
||||||
remote_fail = _print_consolidated_report(c)
|
remote_fail = _print_consolidated_report(c)
|
||||||
|
if args.inrush is not None:
|
||||||
|
remote_fail = remote_fail or _run_inrush_probe(c, args)
|
||||||
except AssertionError as exc:
|
except AssertionError as exc:
|
||||||
print(f"FAIL: {exc}", file=sys.stderr)
|
print(f"FAIL: {exc}", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue