95 lines
3.3 KiB
Python
95 lines
3.3 KiB
Python
"""
|
||
Fiber + radio port: central domain object for a row in fiber_map.json.
|
||
|
||
Power (Acroname hub downstream), SSH routing, PCIe / wlan / USB metadata all hang off this
|
||
aggregate. ``FiWiHarness`` supplies BrainStem power; not the conceptual center.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
||
|
||
from fiwi import fiber_map_io as fm
|
||
from fiwi.ssh_node import SshNode
|
||
|
||
|
||
@dataclass
|
||
class FiberRadioPort:
|
||
"""
|
||
One logical fiber/radio attachment: ``fiber_ports[<map_key>]`` in the map document.
|
||
|
||
``entry`` is the live dict from the document (mutations persist when the doc is saved).
|
||
"""
|
||
|
||
map_key: str
|
||
entry: Optional[Dict[str, Any]]
|
||
|
||
@property
|
||
def port_id(self) -> Optional[int]:
|
||
"""Integer fiber id when ``map_key`` is decimal; else None."""
|
||
if self.map_key.isdigit():
|
||
return int(self.map_key)
|
||
return None
|
||
|
||
def hub_port(self) -> Optional[Tuple[int, int]]:
|
||
"""(hub_1based, port_0based) or None if unmapped / invalid."""
|
||
return fm.fiber_entry_hub_port(self.entry)
|
||
|
||
def ssh_target(self) -> Optional[str]:
|
||
"""user@host when this port’s hubs are reached via SSH; else None."""
|
||
return fm.fiber_ssh_target(self.entry) if isinstance(self.entry, dict) else None
|
||
|
||
def ssh_node(self) -> Optional[SshNode]:
|
||
"""Remote Fi-Wi host (``SshNode``) for this port, or None when mapped locally."""
|
||
t = self.ssh_target()
|
||
if not t:
|
||
return None
|
||
try:
|
||
return SshNode.parse(t)
|
||
except ValueError:
|
||
return None
|
||
|
||
def is_mapped(self) -> bool:
|
||
"""True when hub.port is valid in the entry."""
|
||
return self.hub_port() is not None
|
||
|
||
def chip_preview(self, width: int = 26) -> str:
|
||
return fm.stored_chip_preview(self.entry) if isinstance(self.entry, dict) else ""
|
||
|
||
def pcie_preview(self, width: int = 22) -> str:
|
||
return fm.stored_pcie_preview(self.entry) if isinstance(self.entry, dict) else ""
|
||
|
||
@classmethod
|
||
def from_map_key(cls, doc: Dict[str, Any], map_key: str) -> FiberRadioPort:
|
||
ports = doc.get("fiber_ports") if isinstance(doc, dict) else None
|
||
if not isinstance(ports, dict):
|
||
return cls(str(map_key), None)
|
||
ent = ports.get(str(map_key))
|
||
return cls(
|
||
str(map_key),
|
||
ent if isinstance(ent, dict) else None,
|
||
)
|
||
|
||
@classmethod
|
||
def from_port_id(cls, doc: Dict[str, Any], port_id: int) -> FiberRadioPort:
|
||
return cls.from_map_key(doc, str(int(port_id)))
|
||
|
||
@staticmethod
|
||
def each_from_document(doc: Dict[str, Any]) -> Iterator[FiberRadioPort]:
|
||
"""All ``fiber_ports`` rows (sorted), including unmapped / empty values."""
|
||
ports = doc.get("fiber_ports") if isinstance(doc, dict) else None
|
||
if not isinstance(ports, dict):
|
||
return
|
||
for key in sorted(ports.keys(), key=fm.fiber_sort_key):
|
||
ent = ports[key]
|
||
yield FiberRadioPort(
|
||
str(key),
|
||
ent if isinstance(ent, dict) else None,
|
||
)
|
||
|
||
|
||
def load_fiber_radio_ports(doc: Dict[str, Any]) -> List[FiberRadioPort]:
|
||
"""Registry of all fiber map rows (sorted keys)."""
|
||
return list(FiberRadioPort.each_from_document(doc))
|