herosdevices.hardware.menlo.ofc =============================== .. py:module:: herosdevices.hardware.menlo.ofc .. autoapi-nested-parse:: HERO driver for Menlo Systems frequency combs exposed via QWebChannel. Module Contents --------------- .. py:data:: WebChannelObject .. py:function:: poll_observables(observables: dict[str, dict[str, str]], ensure_connected: collections.abc.Callable[[], None], get_value: collections.abc.Callable[[str], Any], context: str) -> dict[str, tuple[Any, str]] Read a dict of `{name: {"path", "unit"}}` observables, tolerating an unreachable device. Shared by :py:class:`OFC` and :py:class:`~herosdevices.hardware.menlo.functional_layer.FunctionalLayerModule`, whose `_observable_data` implementations both poll the device on demand rather than react to pushed updates: the device is contacted once per poll (not once per observable), and a single unreachable device or a single failing path does not stop the rest of the observables from being read. :param observables: Maps an observable's name to a dict with keys "path" (passed to `get_value`) and "unit". :param ensure_connected: Called once before reading any observable; may raise if the device is unreachable. :param get_value: Reads the current value for one observable's "path". :param context: Identifies the device/module in the log message if it is unreachable. :returns: A dict mapping each observable's name to its `(value, unit)` pair. Empty if `ensure_connected` raised. .. py:class:: OFC(host: str, port: int = QWEBCHANNEL_DEFAULT_PORT, user: str = 'guest', password: str = '', timeout: float = 5.0, reconnect_cooldown: float = 30.0, observables: dict[str, dict[str, str]] | None = None) Driver for a Menlo Systems optical frequency comb (OFC) exposed via the QWebChannel websocket interface. The OFC exposes its full control/status tree (functional layer, modules, settings, ...) via Qt's WebChannel protocol. Because that tree is deep and firmware-dependent, individual nodes are addressed by dotted path strings (see :py:meth:`get_node`) instead of being declared as fixed class attributes. Use :py:meth:`explore` interactively to discover which paths are available on a given device, then list the ones you want polled in `observables`. Values are pushed by the device and kept in a local cache as soon as the connection is established, so :py:meth:`get_node` and :py:meth:`_observable_data` never trigger network traffic themselves. :param host: Hostname or IP address of the OFC's QWebChannel websocket endpoint. :param port: Port of the websocket endpoint. :param user: Username used for authentication. :param password: Password used for authentication. :param timeout: Seconds to wait for a single connection attempt, see :py:class:`QWebChannelConnection`. :param reconnect_cooldown: Minimum seconds between two connection attempts, see :py:class:`QWebChannelConnection`. Keeps a prolonged outage (e.g. the OFC being powered off for half an hour) from causing a reconnect attempt on every single poll tick; the OFC is picked back up automatically the next time it is read after coming back online, no restart needed. :param observables: Additional observables to poll, merged on top of :py:attr:`DEFAULT_OBSERVABLES` (an entry here with the same name overrides the default) rather than replacing it. Each entry maps the name under which a value is emitted with the `observable_data` event to a dict with keys "path" (dotted node path, see :py:meth:`get_node`) and "unit". The repetition-rate, CEO, and oscillator modules have their own default observables instead, see :py:class:`~herosdevices.hardware.menlo.RepetitionRate`, :py:class:`~herosdevices.hardware.menlo.CEO`, and :py:class:`~herosdevices.hardware.menlo.Oscillator`. .. py:attribute:: DEFAULT_OBSERVABLES :type: dict[str, dict[str, str]] .. py:attribute:: host .. py:attribute:: observables .. py:attribute:: connection .. py:method:: get_node(path: str) -> Any Read the current (cached) value of a node in the OFC's control tree. :param path: Dotted path to the node, e.g. `functionalLayer.rrSettings.repetitionRate.rrCounterRepRate` or, for dict-valued nodes such as `modules`, `modules["SYNCRO3U"].functionalLayer...`. :returns: The current value of the node. .. py:method:: set_node(path: str, value: Any) -> None Set the value of a node in the OFC's control tree. :param path: Dotted path to the node, see :py:meth:`get_node`. :param value: Value to set. .. py:method:: call_method(path: str, *args: Any, timeout: float | None = None) -> Any Call a method on a node in the OFC's control tree and wait for its result. QWebChannel method calls are inherently asynchronous (the remote object's generated method wrapper never returns a value, it only accepts a callback for the result). This wraps that callback in a blocking wait so `call_method` behaves like a normal synchronous function call. :param path: Dotted path to the method, see :py:meth:`get_node`. E.g. `functionalLayer.rrSettings.mainControls.unlockHere`. :param \*args: Positional arguments to pass to the method. :param timeout: Seconds to wait for the method's response. Defaults to the connection's own `timeout`. :returns: The method's return value. :raises TimeoutError: No response was received within `timeout` seconds. .. py:method:: explore(path: str = '', depth: int = 1) -> Any Explore the OFC's control/status node tree starting at `path`. Use this interactively to find the dotted paths to put into `observables`, e.g. `ofc.explore("functionalLayer.rrSettings", depth=2)`. :param path: Dotted path to start exploring from (same syntax as :py:meth:`get_node`). Defaults to the root object. :param depth: How many levels of children to expand. Children beyond this depth are listed by name only, without expanding further. :returns: A nested dictionary mapping child names to either their value (leaf), a list of child names (unexpanded branch), or another such dictionary (expanded branch). .. py:method:: format_tree(path: str = '', depth: int = 2) -> str Render the OFC's control/status node tree starting at `path` as an indented ASCII tree. A more readable alternative to :py:meth:`explore` for interactive use, e.g. in a REPL or script: `print(ofc.format_tree("functionalLayer.rrSettings", depth=2))`. :param path: Dotted path to start exploring from, see :py:meth:`explore`. :param depth: How many levels of children to expand, see :py:meth:`explore`. :returns: The rendered tree as a multi-line string, rooted at `path` (or "root" if not given).