refactor: fronthaul, telemetry, radio packages; system harness dir
- Add fiwicontrol.fronthaul (FrontHaul link) and fiwicontrol.telemetry (FrontHaulTelemetry + retimer/SFP structs; common placeholder). - Move RadioHead to fiwicontrol.radio; lab is discovery + INI only. - Rename FrontHaulSnapshot to FrontHaulTelemetry; FrontHaul.snapshot to telemetry field. - Add scripts/system/ for long-running hardware harnesses (vs pytest). - README and pyproject describe the new layout; Power docstring path fix. Made-with: Cursor
This commit is contained in:
parent
ed8020ecc3
commit
7b57568176
36
README.md
36
README.md
|
|
@ -10,21 +10,24 @@ This repository ships that distribution (**`fiwicontrol`** on PyPI / `pip`) with
|
|||
|
||||
1. **`fiwicontrol.commands`** — run commands on remote rigs via OpenSSH or **`ush`** (`Command`, `CommandManager`, `ssh_node`). Remote Pi bootstrap: **`python3 -m fiwicontrol.commands <ip> --remote-repo …`** (see **`docs/install.md`**). Must not import **`fiwicontrol.power`**.
|
||||
2. **`fiwicontrol.lab`** — USB discovery (Acroname / Monsoon) and **`configs/*.ini`** inventory load + verify. Imports **`fiwicontrol.commands`** for SSH discovery only.
|
||||
3. **`fiwicontrol.power`** — **`Power`** (``.on()`` / ``.off()`` / ``.voltage()`` / …) via **`AcronamePower`** and **`MonsoonPower`**, plus a small CLI (**`--discovery-json`**, **`--list-power-devices`**, **`--verify-inventory`** with **`-c`**). Re-exports **`fiwicontrol.lab`** discovery/inventory for compatibility. May import **`fiwicontrol.commands`** and **`fiwicontrol.lab`**; the reverse is forbidden.
|
||||
4. **`fiwicontrol.flows`** — async **iperf**-driven traffic **flows** over **`ssh`** (remote server/client processes, sampling, histogram / KS tooling). Lives in **`flows/flows.py`**, structured for **Python 3.11** (**`asyncio.timeout`**, **`asyncio.gather`**, **`await iperf_flow.run_traffic`** and related coroutines; sync **`run`** / **`commence`** / … call **`asyncio.run`** only when no loop is already running). Depends on **SciPy**, **NumPy**, **Matplotlib**, and a compatible **iperf** binary on endpoints—use **`pip install -e ".[flows]"`** when you need it; **`import fiwicontrol.flows`** alone stays lightweight (lazy load). Not yet integrated with **`ssh_node`** or **`iperf 2` on Pi 5** roadmap text above—that wiring is follow-on work.
|
||||
5. **`fiwicontrol.spc`** — **SPC** primitives: **`ShewhartControlChart`** (individuals / MR limits) and **`HotellingT2`** (Phase I mean–covariance, *T*² and UCL). Under **`spc/`** (not **`flows/`**). Optional **`pip install -e ".[spc]"`** (**NumPy**, **SciPy**); **`import fiwicontrol.spc`** is lazy until you reference a class.
|
||||
3. **`fiwicontrol.fronthaul`** — concentrator→RRH **fronthaul link** identity (**`FrontHaul`**: medium, PCIe-style ids, link state); used in **lab and production**.
|
||||
4. **`fiwicontrol.telemetry`** — telemetry **schemas split by domain**; today **`telemetry.fronthaul`** exposes **`FrontHaulTelemetry`** (retimer board / PCIe slot / cable / SFP / rails, e.g. **Adnacom Monitor** fields). Future: Wi‑Fi / Fi‑Wi–wide modules alongside **`common`** for shared envelopes when needed.
|
||||
5. **`fiwicontrol.radio`** — logical **RRH / AP** aggregate (**`RadioHead`**: Wi‑Fi, hostapd, clients, **`FrontHaul`**, optional **`Power`**); **lab and production**.
|
||||
6. **`fiwicontrol.power`** — **`Power`** (``.on()`` / ``.off()`` / ``.voltage()`` / …) via **`AcronamePower`** and **`MonsoonPower`**, plus a small CLI (**`--discovery-json`**, **`--list-power-devices`**, **`--verify-inventory`** with **`-c`**). Re-exports **`fiwicontrol.lab`** discovery/inventory for compatibility. May import **`fiwicontrol.commands`** and **`fiwicontrol.lab`**; the reverse is forbidden.
|
||||
7. **`fiwicontrol.flows`** — async **iperf**-driven traffic **flows** over **`ssh`** (remote server/client processes, sampling, histogram / KS tooling). Lives in **`flows/flows.py`**, structured for **Python 3.11** (**`asyncio.timeout`**, **`asyncio.gather`**, **`await iperf_flow.run_traffic`** and related coroutines; sync **`run`** / **`commence`** / … call **`asyncio.run`** only when no loop is already running). Depends on **SciPy**, **NumPy**, **Matplotlib**, and a compatible **iperf** binary on endpoints—use **`pip install -e ".[flows]"`** when you need it; **`import fiwicontrol.flows`** alone stays lightweight (lazy load). Not yet integrated with **`ssh_node`** or **`iperf 2` on Pi 5** roadmap text above—that wiring is follow-on work.
|
||||
8. **`fiwicontrol.spc`** — **SPC** primitives: **`ShewhartControlChart`** (individuals / MR limits) and **`HotellingT2`** (Phase I mean–covariance, *T*² and UCL). Under **`spc/`** (not **`flows/`**). Optional **`pip install -e ".[spc]"`** (**NumPy**, **SciPy**); **`import fiwicontrol.spc`** is lazy until you reference a class.
|
||||
|
||||
## Relationship to the Fi‑Wi architecture spec
|
||||
|
||||
The spec (**`html/Fi-Wi-L4S.html`**) describes the **Fi‑Wi system**: the **Umber concentrator** as the centralized **control, queueing, and time** plane, **RRHs** on the **PCIe / fronthaul** fabric, and the **L4S-oriented** latency model that depends on that split. FiWiControl does not implement the datapath or MAC; it is the **concentrator-resident (and dev-workstation) Python layer** that **manages and validates** the deployed system around that architecture.
|
||||
|
||||
Concretely, it is how we keep **what we think is connected** aligned with **what is actually cabled and powered**: **INI inventory and verification** (**`fiwicontrol.lab`**, **`fiwicontrol.power`**), **scripted remote work** over the same **SSH / `ush`** paths we use in production (**`fiwicontrol.commands`**), **programmable power and USB** so bring-up and PCIe **hot-plug** sequences are repeatable, **async iperf flows** for load and measurement against RRHs or lab stand-ins (**`fiwicontrol.flows`**), and **SPC** when we need statistical discipline across long campaigns (**`fiwicontrol.spc`**). Read the spec for **why** the topology and timing model look the way they do; read this repo and **`docs/`** for **how** we install, verify, and operate it day to day.
|
||||
Concretely, it is how we keep **what we think is connected** aligned with **what is actually cabled and powered**: **INI inventory and verification** (**`fiwicontrol.lab`**, **`fiwicontrol.power`**), **scripted remote work** over the same **SSH / `ush`** paths we use in production (**`fiwicontrol.commands`**), **programmable power and USB** so bring-up and **fronthaul hot-plug** sequences are repeatable (PCIe is the fronthaul medium today), **fronthaul telemetry** (**`fiwicontrol.telemetry`**) for host retimer readouts, **async iperf flows** for load and measurement against RRHs or lab stand-ins (**`fiwicontrol.flows`**), and **SPC** when we need statistical discipline across long campaigns (**`fiwicontrol.spc`**). Read the spec for **why** the topology and timing model look the way they do; read this repo and **`docs/`** for **how** we install, verify, and operate it day to day.
|
||||
|
||||
## Near-term focus: PCIe hot-swap testing
|
||||
## Near-term focus: fronthaul hot-swap testing (PCIe today)
|
||||
|
||||
A **first concrete goal** for this stack is to support **PCIe hot-swap (hot-plug) testing** in the lab: controlled **remove / restore** of the link, predictable **enumeration** and driver behavior, and **repeatable** runs across builds and rigs.
|
||||
A **first concrete goal** for this stack is to support **fronthaul hot-swap (hot-plug) testing** in the lab—**PCIe** for now: controlled **remove / restore** of the link, predictable **enumeration** and driver behavior, and **repeatable** runs across builds and rigs.
|
||||
|
||||
That work fails if the bench is informal—wrong port, wrong power path, or no shared picture of what was connected when. FiWiControl targets that gap by combining **documented inventory** (INI + **`--verify-inventory`**), **remote automation** (**`ssh_node`**, **`python3 -m fiwicontrol.commands`** to bring up rigs), and **programmable power / USB paths** (**`fiwicontrol.power`**) so sequences are **scripted and checkable** before the PCIe harness runs. **`fiwicontrol.spc`** is aimed at **many hot-plug cycles**—spotting drift in failure rates or side metrics—not only general Fi‑Wi bring-up (see **`docs/spc.md`**).
|
||||
That work fails if the bench is informal—wrong port, wrong power path, or no shared picture of what was connected when. FiWiControl targets that gap by combining **documented inventory** (INI + **`--verify-inventory`**), **remote automation** (**`ssh_node`**, **`python3 -m fiwicontrol.commands`** to bring up rigs), and **programmable power / USB paths** (**`fiwicontrol.power`**) so sequences are **scripted and checkable** before the fronthaul harness runs. **`fiwicontrol.spc`** is aimed at **many hot-plug cycles**—spotting drift in failure rates or side metrics—not only general Fi‑Wi bring-up (see **`docs/spc.md`**).
|
||||
|
||||
## Lab fleet: Raspberry Pi 5 and iperf 2 (planned)
|
||||
|
||||
|
|
@ -32,7 +35,7 @@ We expect a **fleet of Raspberry Pi 5** boards to act as **controllers and actua
|
|||
|
||||
## ESP32, IEEE 802.11, and advanced telemetry (planned)
|
||||
|
||||
Separately from the Pi 5 **`iperf`** plane, we plan **ESP32**-based nodes for **advanced telemetry**, including rich use of **IEEE 802.11 (Wi‑Fi)**: airlink statistics, channel / PHY-adjacent metrics, retries, timing, and correlation with harness and inventory state. The goal is lab- and field-grade **802.11 telemetry**, not only GPIO or serial counters. **This stack is not implemented yet** in **`fiwicontrol`**; today’s code is still the commands / lab / power packages above.
|
||||
Separately from the Pi 5 **`iperf`** plane, we plan **ESP32**-based nodes for **advanced telemetry**, including rich use of **IEEE 802.11 (Wi‑Fi)**: airlink statistics, channel / PHY-adjacent metrics, retries, timing, and correlation with harness and inventory state. The goal is lab- and field-grade **802.11 telemetry**, not only GPIO or serial counters. **This stack is not implemented yet** in **`fiwicontrol`**; today’s packages are **commands**, **lab**, **fronthaul**, **telemetry** (fronthaul slice), **radio**, **power**, **flows**, and **spc** above.
|
||||
|
||||
## `ush` expansion (planned)
|
||||
|
||||
|
|
@ -55,6 +58,9 @@ FiWiControl/
|
|||
├── LICENSE
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
├── scripts/
|
||||
│ ├── setup_pi_power.sh
|
||||
│ └── system/
|
||||
├── html/
|
||||
│ └── Fi-Wi-L4S.html
|
||||
├── docs/
|
||||
|
|
@ -70,11 +76,21 @@ FiWiControl/
|
|||
│ │ ├── __main__.py
|
||||
│ │ ├── node_control.py
|
||||
│ │ └── remote_setup.py
|
||||
│ ├── fronthaul/
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── link.py
|
||||
│ ├── lab/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── discovery.py
|
||||
│ │ ├── inventory_config.py
|
||||
│ │ └── inventory_verify.py
|
||||
│ ├── radio/
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── radiohead.py
|
||||
│ ├── telemetry/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── common.py
|
||||
│ │ └── fronthaul.py
|
||||
│ ├── power/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── __main__.py
|
||||
|
|
@ -122,6 +138,10 @@ from fiwicontrol.commands import ssh_node, Command, CommandManager
|
|||
|
||||
## Tests
|
||||
|
||||
**`tests/`** — **`pytest`** package tests (CI-oriented, import smoke, gated remote tests, etc.).
|
||||
|
||||
**`scripts/system/`** — **manual or long-running harness scripts** that exercise real hardware (e.g. fronthaul **PCIe hot-swap** campaigns). Not part of the default **`pytest`** tree; run explicitly when the bench is wired.
|
||||
|
||||
After install, use the ordered **verification checklist** and **INI reference** in **`docs/power-control-and-inventory.md`** (sections **“Verification checklist (after install)”** and **“Lab INI file reference”**).
|
||||
|
||||
Commands, example **`pytest`** output, and **`unittest`** entry points for **`node_control`**: **`docs/node-control-asyncio-design.md`** → section **“Running tests”** (top-level `##` heading near the top of the file).
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||
[project]
|
||||
name = "fiwicontrol"
|
||||
version = "0.1.0"
|
||||
description = "FiWiControl repo: commands (SSH/ush), lab, power, flows (async iperf), spc (Shewhart / Hotelling)."
|
||||
description = "FiWiControl repo: commands (SSH/ush), lab, fronthaul, telemetry, radio, power, flows (async iperf), spc (Shewhart / Hotelling)."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
license = { file = "LICENSE" }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Fronthaul link model (concentrator ↔ RRH). Telemetry lives under :mod:`fiwicontrol.telemetry`."""
|
||||
|
||||
from fiwicontrol.fronthaul.link import FrontHaul
|
||||
|
||||
__all__ = ["FrontHaul"]
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Logical concentrator→RRH fronthaul link identity (medium + PCIe ids today)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fiwicontrol.telemetry.fronthaul import FrontHaulTelemetry
|
||||
|
||||
|
||||
@dataclass
|
||||
class FrontHaul:
|
||||
"""Physical / logical link from concentrator host toward the radio head.
|
||||
|
||||
Fi‑Wi uses a **fronthaul** between concentrator and RRH. Today that is **PCIe**;
|
||||
other media can reuse this type with a different ``medium`` and id fields as
|
||||
documented for that transport.
|
||||
|
||||
Attach :class:`~fiwicontrol.telemetry.fronthaul.FrontHaulTelemetry` for host
|
||||
retimer / cable / SFP readouts (production or lab).
|
||||
"""
|
||||
|
||||
#: Transport name, e.g. ``"pcie"`` (default); future values are host-defined.
|
||||
medium: str = "pcie"
|
||||
#: 16-bit vendor id when ``medium == "pcie"`` (configuration space).
|
||||
vendor_id: int | None = None
|
||||
#: 16-bit device id when ``medium == "pcie"``.
|
||||
device_id: int | None = None
|
||||
#: Subsystem vendor id when ``medium == "pcie"``.
|
||||
subsystem_vendor_id: int | None = None
|
||||
#: Subsystem device id when ``medium == "pcie"``.
|
||||
subsystem_device_id: int | None = None
|
||||
#: Link / power-management state strings (e.g. ``("L0", "D0")``); host-defined.
|
||||
link_states: tuple[str, ...] = ()
|
||||
#: Latest fronthaul telemetry sample from the host (optional).
|
||||
telemetry: FrontHaulTelemetry | None = None
|
||||
|
|
@ -2,7 +2,11 @@
|
|||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Lab USB topology: BrainStem / Monsoon discovery and INI inventory."""
|
||||
"""Lab-focused USB topology and INI inventory (discovery, load, verify).
|
||||
|
||||
Radio models live in :mod:`fiwicontrol.radio`. Fronthaul link and telemetry live in
|
||||
:mod:`fiwicontrol.fronthaul` and :mod:`fiwicontrol.telemetry`.
|
||||
"""
|
||||
|
||||
from fiwicontrol.lab.discovery import (
|
||||
MONSOON_USB_PIDS,
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class Power:
|
|||
"""
|
||||
Bench power facet: switch Acroname hub ports and read Monsoon electricals.
|
||||
|
||||
Intended for use as ``radiohead[id].power`` with methods ``.on()``, ``.off()``,
|
||||
``.voltage()``, ``.current()``, ``.watts()``.
|
||||
Attach to :class:`fiwicontrol.radio.radiohead.RadioHead` as ``head.power``; methods
|
||||
``.on()``, ``.off()``, ``.voltage()``, ``.current()``, ``.watts()``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Logical radio / RRH models (lab and production concentrator)."""
|
||||
|
||||
from fiwicontrol.radio.radiohead import RadioHead
|
||||
|
||||
__all__ = ["RadioHead"]
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Logical radio head: Wi-Fi, host integration, fronthaul, and optional bench ``Power``."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from fiwicontrol.fronthaul import FrontHaul
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fiwicontrol.power.control import Power
|
||||
|
||||
|
||||
@dataclass
|
||||
class RadioHead:
|
||||
"""Aggregate state for one logical Fi‑Wi radio / remote radio head (RRH-oriented).
|
||||
|
||||
Most fields are optional so probes and inventory can populate them incrementally.
|
||||
Use :attr:`fronthaul` for the concentrator→RRH link (PCIe today). Use :attr:`power`
|
||||
to attach a :class:`fiwicontrol.power.Power` facade when this head maps to Acroname
|
||||
/ Monsoon paths from the inventory INI.
|
||||
"""
|
||||
|
||||
#: Stable logical id (e.g. inventory ``[node.*]`` key or RRH label).
|
||||
radio_id: str
|
||||
#: Patch panel port number for the copper/fiber hop to this head (plant layer).
|
||||
patch_panel_port: int | None = None
|
||||
#: Channel description from the driver or tooling (e.g. ``"6g80/160"``), if known.
|
||||
chanspec: str | None = None
|
||||
#: Center or channel carrier frequency in MHz, if known separately from ``chanspec``.
|
||||
carrier_frequency_mhz: float | None = None
|
||||
#: High-level power state (e.g. ``"on"``, ``"off"``, ``"psm"``); semantics are host-defined.
|
||||
power_state: str | None = None
|
||||
#: BSSID / AP MAC (often derived from the underlying 802.3-style station MAC).
|
||||
bssid: str | None = None
|
||||
#: Hostapd instance or control surface identifier (path, dbus name, etc.).
|
||||
hostapd: str | None = None
|
||||
#: Service set identifier broadcast for this BSS.
|
||||
ssid: str | None = None
|
||||
#: Associated client MAC addresses (or stable client ids) as reported by the AP stack.
|
||||
associated_clients: list[str] = field(default_factory=list)
|
||||
#: Vendor / chip name string from ``ethtool``, sysfs, or nl80211.
|
||||
wifi_chip: str | None = None
|
||||
#: WLAN firmware version string.
|
||||
wifi_firmware: str | None = None
|
||||
#: In-tree or staged driver name / version string.
|
||||
wifi_driver: str | None = None
|
||||
#: Concentrator→RRH fronthaul (PCIe link identity and state today).
|
||||
fronthaul: FrontHaul | None = None
|
||||
#: Optional bench power for this head (Acroname / Monsoon via :class:`~fiwicontrol.power.Power`).
|
||||
power: Power | None = None
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Telemetry schemas split by domain (fronthaul today; Wi‑Fi / Fi‑Wi later).
|
||||
|
||||
Import submodules explicitly (e.g. :mod:`fiwicontrol.telemetry.fronthaul`) or use
|
||||
the re-exports below for the fronthaul bundle.
|
||||
"""
|
||||
|
||||
from fiwicontrol.telemetry.fronthaul import (
|
||||
CablePortStats,
|
||||
FrontHaulTelemetry,
|
||||
FronthaulBoardInfo,
|
||||
PcieSlotTelemetry,
|
||||
SfpModuleInventory,
|
||||
SfpModuleTelemetry,
|
||||
SystemPowerRails,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CablePortStats",
|
||||
"FrontHaulTelemetry",
|
||||
"FronthaulBoardInfo",
|
||||
"PcieSlotTelemetry",
|
||||
"SfpModuleInventory",
|
||||
"SfpModuleTelemetry",
|
||||
"SystemPowerRails",
|
||||
]
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Cross-domain telemetry helpers (timestamps, sources, envelopes) when shared code is needed."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# Copyright (c) 2026 Umber
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0; see LICENSE.
|
||||
|
||||
"""Fronthaul-side telemetry: retimer host readouts (e.g. Adnacom-style GUIs)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class FronthaulBoardInfo:
|
||||
"""Device / retimer board row (e.g. ``1:0.0 H3`` header in Adnacom Monitor)."""
|
||||
|
||||
#: Linux PCI BDF of the bridge or retimer (e.g. ``"0000:01:00.0"``).
|
||||
device_bdf: str | None = None
|
||||
#: PCI class line as reported by tools (e.g. ``"PCI bridge: PLX Tech"``).
|
||||
pci_class_text: str | None = None
|
||||
board_name: str | None = None
|
||||
#: e.g. ``"Host"`` vs ``"Device"`` mode string from firmware.
|
||||
mode: str | None = None
|
||||
hw_rev: str | None = None
|
||||
fw_rev: str | None = None
|
||||
board_serial: str | None = None
|
||||
#: Switch ASIC string (e.g. ``"PEX8718"``).
|
||||
pcie_switch: str | None = None
|
||||
#: Optional 8-bit DIP pattern (LSB = switch 1); ``None`` if unknown.
|
||||
dip_switch_mask: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class PcieSlotTelemetry:
|
||||
"""Upstream PCIe slot / edge status."""
|
||||
|
||||
port: int | None = None
|
||||
capability: str | None = None
|
||||
link_state: str | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class CablePortStats:
|
||||
"""One row of the **Cable** table (per fronthaul lane / retimer port)."""
|
||||
|
||||
port: int = 0
|
||||
capability: str | None = None
|
||||
status: str | None = None
|
||||
training_ms: float | None = None
|
||||
remote_up_ms: float | None = None
|
||||
recovery: int | None = None
|
||||
rx_errors: int | None = None
|
||||
bad_tlp: int | None = None
|
||||
bad_dllp: int | None = None
|
||||
framing_error: int | None = None
|
||||
retrain: int | None = None
|
||||
link_down: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class SfpModuleInventory:
|
||||
"""Static / slow fields from the **SFP** inventory table."""
|
||||
|
||||
port: int = 0
|
||||
present: bool | None = None
|
||||
power_supply_v: float | None = None
|
||||
power_supply_a: float | None = None
|
||||
power_supply_w: float | None = None
|
||||
state: str | None = None
|
||||
rx_los: int | None = None
|
||||
startup_ms: float | None = None
|
||||
vendor: str | None = None
|
||||
part_number: str | None = None
|
||||
serial_number: str | None = None
|
||||
technology: str | None = None
|
||||
squelch_tx_implemented: bool | None = None
|
||||
squelch_rx_implemented: bool | None = None
|
||||
i2c_errors: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class SfpModuleTelemetry:
|
||||
"""Live optics fields (e.g. per-port gauges in Adnacom Monitor)."""
|
||||
|
||||
port: int = 0
|
||||
temp_c: float | None = None
|
||||
vcc_v: float | None = None
|
||||
rx_power_uw: float | None = None
|
||||
rx_power_dbm: float | None = None
|
||||
tx_bias_ma: float | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class SystemPowerRails:
|
||||
"""Bottom **System** voltage / temperature row on the retimer card."""
|
||||
|
||||
tpex_c: float | None = None
|
||||
topu_c: float | None = None
|
||||
slot_3v3_v: float | None = None
|
||||
slot_12v_v: float | None = None
|
||||
slot_12v_a: float | None = None
|
||||
slot_12v_w: float | None = None
|
||||
slot_3v3aux_v: float | None = None
|
||||
slot_3v3aux_a: float | None = None
|
||||
slot_3v3aux_w: float | None = None
|
||||
rail_3v3_v: float | None = None
|
||||
rail_1v8sb_v: float | None = None
|
||||
rail_1v8_v: float | None = None
|
||||
rail_0v9_v: float | None = None
|
||||
rail_0v9a_v: float | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class FrontHaulTelemetry:
|
||||
"""Host-side fronthaul telemetry (retimer GUI or JSON/API export).
|
||||
|
||||
Board / PCIe slot / cable / SFP / rail fields for one capture. Containers may
|
||||
be empty when unknown. Intended for **lab and production** concentrator hosts.
|
||||
"""
|
||||
|
||||
#: Provenance string (e.g. ``"Adnacom Monitor v1.0.0"``).
|
||||
monitor_source: str | None = None
|
||||
board: FronthaulBoardInfo | None = None
|
||||
pcie_slot: PcieSlotTelemetry | None = None
|
||||
cable_ports: tuple[CablePortStats, ...] = field(default_factory=tuple)
|
||||
sfp_inventory: tuple[SfpModuleInventory, ...] = field(default_factory=tuple)
|
||||
sfp_telemetry: tuple[SfpModuleTelemetry, ...] = field(default_factory=tuple)
|
||||
system_power: SystemPowerRails | None = None
|
||||
|
|
@ -2,10 +2,75 @@ def test_import_subpackages() -> None:
|
|||
import fiwicontrol
|
||||
import fiwicontrol.commands
|
||||
import fiwicontrol.flows
|
||||
import fiwicontrol.fronthaul
|
||||
import fiwicontrol.lab
|
||||
import fiwicontrol.power
|
||||
import fiwicontrol.radio
|
||||
import fiwicontrol.spc
|
||||
import fiwicontrol.telemetry
|
||||
from fiwicontrol.fronthaul import FrontHaul
|
||||
from fiwicontrol.radio import RadioHead
|
||||
from fiwicontrol.telemetry import (
|
||||
CablePortStats,
|
||||
FrontHaulTelemetry,
|
||||
FronthaulBoardInfo,
|
||||
PcieSlotTelemetry,
|
||||
SfpModuleInventory,
|
||||
SfpModuleTelemetry,
|
||||
SystemPowerRails,
|
||||
)
|
||||
|
||||
assert fiwicontrol.__version__
|
||||
assert "iperf" in (fiwicontrol.flows.__doc__ or "").lower()
|
||||
assert "Shewhart" in (fiwicontrol.spc.__doc__ or "")
|
||||
tel = FrontHaulTelemetry(
|
||||
monitor_source="Adnacom Monitor v1.0.0",
|
||||
board=FronthaulBoardInfo(
|
||||
device_bdf="0000:01:00.0",
|
||||
board_name="H3",
|
||||
mode="Host",
|
||||
hw_rev="1.0.1",
|
||||
fw_rev="1.2.0",
|
||||
board_serial="4C-0A-3D-62-13-13",
|
||||
pcie_switch="PEX8718",
|
||||
),
|
||||
pcie_slot=PcieSlotTelemetry(port=0, capability="Gen3 x4", link_state="Gen3 x4"),
|
||||
cable_ports=(
|
||||
CablePortStats(port=1, capability="Gen2 x1", training_ms=17.0, remote_up_ms=3.0, recovery=3),
|
||||
),
|
||||
sfp_inventory=(
|
||||
SfpModuleInventory(
|
||||
port=1,
|
||||
present=True,
|
||||
power_supply_v=3.33,
|
||||
state="On",
|
||||
vendor="6COM",
|
||||
part_number="6C-SFP+-LR, Rev.B",
|
||||
serial_number="6C82510221225",
|
||||
technology="1310 nm VCSEL",
|
||||
),
|
||||
),
|
||||
sfp_telemetry=(SfpModuleTelemetry(port=1, temp_c=33.3, vcc_v=3.31, rx_power_uw=501.0, rx_power_dbm=-3.0, tx_bias_ma=28.9),),
|
||||
system_power=SystemPowerRails(tpex_c=37.0, topu_c=31.0, slot_3v3_v=3.33, rail_3v3_v=3.33),
|
||||
)
|
||||
fh = FrontHaul(
|
||||
medium="pcie",
|
||||
vendor_id=0x1234,
|
||||
device_id=0x5678,
|
||||
link_states=("L0",),
|
||||
telemetry=tel,
|
||||
)
|
||||
h = RadioHead(
|
||||
radio_id="rrh-01",
|
||||
patch_panel_port=17,
|
||||
ssid="lab-bss",
|
||||
bssid="02:00:00:00:01:00",
|
||||
fronthaul=fh,
|
||||
)
|
||||
assert h.radio_id == "rrh-01"
|
||||
assert h.patch_panel_port == 17
|
||||
assert h.fronthaul is not None and h.fronthaul.medium == "pcie"
|
||||
assert h.fronthaul.telemetry is not None
|
||||
assert h.fronthaul.telemetry.monitor_source == "Adnacom Monitor v1.0.0"
|
||||
assert h.fronthaul.telemetry.board is not None and h.fronthaul.telemetry.board.pcie_switch == "PEX8718"
|
||||
assert h.associated_clients == []
|
||||
|
|
|
|||
Loading…
Reference in New Issue