from qcodes.instrument import Instrument
from typing import Literal, Sequence, Any
from dataclasses import dataclass, field
import networkx as nx
from semi_cr.core.lab.station.graphing.models import Node, Edge
from semi_cr.core.lab.station.graphing.ids import (
instrument_connection_node_id,
instrument_node_id,
instrument_module_node_id,
terminal_node_id,
)
from semi_cr.core.lab.station.graphing.operations import build_nx_graph
from semi_cr.core.lab.station.graphing.runtime_builder import build_connection_graph_for_instrument
[docs]
@dataclass
class PCBLine:
name: str
kind: Literal["dc", "rf", "ac"]
pin_type: str | None = None
pads: list[str] = field(default_factory=list)
[docs]
class PCB(Instrument):
"""Generic passive PCB / daughterboard with named electrical lines."""
def __init__(
self,
name: str,
version: str | None = None,
lines: dict[str, PCBLine] | None = None,
) -> None:
super().__init__(name, metadata={}, label="pcb")
self.version = version
self.lines = lines or {}
[docs]
def get_idn(self) -> dict[str, str | None]:
"""
Override Instrument.get_idn so QCoDeS never calls self.ask('*IDN?').
"""
return {
"vendor": None,
"model": self.name,
"serial": None,
"firmware": None
}
[docs]
class RoutedPCB(PCB):
def __init__(
self,
name: str,
version: str | None = None,
lines: dict[str, PCBLine] | None = None,
connections: Sequence[dict[str, Any]] = (),
) -> None:
super().__init__(name=name, version=version, lines=lines)
self.connections = list(connections)
@property
def graph(self) -> nx.MultiDiGraph:
def build_pcb_graph(
pcb: PCB,
parent_id: str | None = None,
) -> nx.MultiDiGraph:
nodes: list[Node] = []
edges: list[Edge] = []
if parent_id is None:
pcb_id = instrument_node_id(pcb.name)
pcb_kind = "instrument"
else:
pcb_id = instrument_module_node_id(parent_id, pcb.name)
pcb_kind = "instrument_module"
nodes.append(
Node(
id=pcb_id,
kind=pcb_kind,
name=pcb.name,
obj=pcb,
)
)
for line_name, line in pcb.lines.items():
line_id = instrument_connection_node_id(
pcb.name,
line_name,
)
nodes.append(
Node(
id=line_id,
kind="pcb_line",
name=line_name,
obj=line,
)
)
edges.append(
Edge(
source=pcb_id,
target=line_id,
kind="contains",
)
)
for pad in line.pads:
pad_id = terminal_node_id(
pcb.name,
pad,
)
nodes.append(
Node(
id=pad_id,
kind="terminal",
name=pad,
obj=None,
)
)
edges.extend(
[
Edge(
source=line_id,
target=pad_id,
kind="contains",
),
Edge(
source=pad_id,
target=line_id,
kind="dependency",
),
]
)
return build_nx_graph(nodes, edges)
pcb_graph = build_pcb_graph(self)
connection_graph = build_connection_graph_for_instrument(
self,
self.connections,
)
graph = nx.compose(pcb_graph, connection_graph)
return graph