61 lines
1.8 KiB
Python
61 lines
1.8 KiB
Python
"""
|
|
Physical patch panel: front-panel position count for the rack (field workflow).
|
|
|
|
Stored in fiber_map.json as ``patch_panel``: ``{ "slots": N, "label": "…" }``.
|
|
USB hub calibrate still walks hub ports; map keys 1…N align with panel positions.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any, Dict, Optional
|
|
|
|
from fiwi.constants import PANEL_SLOTS
|
|
|
|
_MAX_SLOTS = 256
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PatchPanel:
|
|
"""Instantiated panel: ``slots`` front-panel positions (numbered 1…slots)."""
|
|
|
|
slots: int
|
|
label: str = ""
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.slots < 1:
|
|
raise ValueError("patch panel slots must be >= 1")
|
|
if self.slots > _MAX_SLOTS:
|
|
raise ValueError(f"patch panel slots must be <= {_MAX_SLOTS}")
|
|
|
|
def to_map_blob(self) -> Dict[str, Any]:
|
|
d: Dict[str, Any] = {"slots": self.slots}
|
|
if self.label.strip():
|
|
d["label"] = self.label.strip()
|
|
return d
|
|
|
|
@classmethod
|
|
def from_map_blob(cls, blob: Any) -> Optional[PatchPanel]:
|
|
if not isinstance(blob, dict):
|
|
return None
|
|
s = blob.get("slots")
|
|
if isinstance(s, str) and s.strip().isdigit():
|
|
s = int(s.strip())
|
|
if not isinstance(s, int) or s < 1:
|
|
return None
|
|
if s > _MAX_SLOTS:
|
|
return None
|
|
lab = blob.get("label")
|
|
label = lab.strip() if isinstance(lab, str) else ""
|
|
return cls(slots=s, label=label)
|
|
|
|
|
|
def effective_panel_slots(doc: Optional[Dict[str, Any]]) -> int:
|
|
"""Panel position count from ``doc['patch_panel']``, else ``PANEL_SLOTS``."""
|
|
if not isinstance(doc, dict):
|
|
return PANEL_SLOTS
|
|
pp = PatchPanel.from_map_blob(doc.get("patch_panel"))
|
|
if pp is not None:
|
|
return pp.slots
|
|
return PANEL_SLOTS
|