Skip to content

Discovery & capabilities

Enumerate boards and subsystems and introspect what each one can do before you configure a task. See the Discovery & capabilities guide.

dtollib.system

System-level discovery + capability query.

Public surface:

  • :func:find_devices — enumerate every installed DT-Open Layers board.
  • :func:find_subsystems — enumerate subsystems on a given board.
  • :class:CapabilitySet — typed view over an HDASS's reported caps.
  • :class:BoardInfo, :class:SubsystemInfo, :class:DeviceInfo — immutable result dataclasses.

Discovery / lifecycle / capability surface; :class:CapabilitySet extends as new capability flags become relevant (e.g. OLSSC_SUP_PAUSE for continuous streaming).

Design reference: docs/design.md §20.

BoardInfo dataclass

BoardInfo(*, name, model, driver_name, instance)

One DT-Open Layers board as reported by olDaEnumBoardsEx.

Attributes:

Name Type Description
name str

Board name passed to :func:olDaInitialize (e.g. "DT9805(00)").

model str

Model identifier from olDaGetBoardInfo (e.g. "DT9805"). Falls back to name if the SDK does not populate a distinct model string.

driver_name str

Kernel-mode driver name from the registry (e.g. "OLDT9805").

instance int

Driver instance number; non-zero when multiple boards of the same model are installed.

CapabilitySet dataclass

CapabilitySet(
    *,
    supports_singlevalue,
    supports_continuous,
    supports_simultaneous_sh,
    supports_simultaneous_da,
    supports_multisensor,
    supports_singleended,
    supports_dma,
    supports_autocal,
    supports_singlevalue_autorange,
    supports_inprocess_flush,
    supports_interleaved_cjc_in_stream,
    returns_floats,
    supports_thermocouples=False,
    supports_ctmode_measure=False,
    supports_quadrature_decoder=False,
    supports_mute=False,
    supports_wrp_waveform=False,
    supports_put_single_values=False,
    supports_synchronous_digitalio=False,
    current_outputs=False,
    max_digitaliolist_value=0,
    resolution=0,
    num_channels,
    cgl_depth,
    max_throughput_hz,
    ranges=tuple(),
    gains=tuple(),
)

Consolidated capability view for one HDASS.

All fields are populated at construction time from the SDK; the typed view is then used in lieu of raw flag queries throughout the codebase.

Attributes:

Name Type Description
supports_singlevalue bool

OLSSC_SUP_SINGLEVALUE.

supports_continuous bool

OLSSC_SUP_CONTINUOUS.

supports_simultaneous_sh bool

OLSSC_SUP_SIMULTANEOUS_SH.

supports_simultaneous_da bool

OLSSC_SUP_SIMULTANEOUS_DA.

supports_multisensor bool

OLSSC_SUP_MULTISENSOR.

supports_singleended bool

OLSSC_SUP_SINGLEENDED.

supports_dma bool

OLSSC_SUP_DMA.

supports_autocal bool

OLSSC_SUP_AUTOCAL.

supports_singlevalue_autorange bool

OLSSC_SUP_SINGLEVALUE_AUTORANGE.

supports_inprocess_flush bool

OLSSC_SUP_INPROCESSFLUSH.

supports_interleaved_cjc_in_stream bool

OLSSC_SUP_INTERLEAVED_CJC_IN_STREAM.

returns_floats bool

OLSSC_RETURNS_FLOATS — true only on subsystems that linearise in firmware and return engineering units. The DT9805/DT9806 A/D reports false: it returns raw codes and the wrapper applies NIST ITS-90 itself (see :attr:supports_thermocouples).

supports_thermocouples bool

OLSSC_SUP_THERMOCOUPLES — the subsystem has a thermocouple front-end + CJC channel. On the DT9805/DT9806 this is true while returns_floats is false, which selects the application-side linearisation read path (differential emf + CJC channel + ITS-90 polynomials).

supports_ctmode_measure bool

OLSSC_SUP_CTMODE_MEASURE — the C/T subsystem supports edge-to-edge / frequency MEASURE mode. The DT9805/DT9806 C/T reports false; the builder gates CounterMode.MEASURE / .TACHOMETER off with a :class:~dtollib.errors.DtolCapabilityError when this is false.

supports_quadrature_decoder bool

OLSSC_SUP_QUADRATURE_DECODER — the C/T subsystem supports quadrature decoding. The DT9805/DT9806 report false; the builder gates CounterMode.QUADRATURE off when this is false.

supports_mute bool

OLSSC_SUP_MUTE — the (D/A) subsystem can mute / unmute its output. play() mutes before stop to avoid a DAC transient; when false the bridge skips the mute step. Position header-verified, bench read-back pending (WS-B).

supports_wrp_waveform bool

OLSSC_SUP_WRPWAVEFORM — the subsystem supports continuous (refilled) waveform output. Gates the WrapMode.MULTIPLE AO path. Bench read-back pending (WS-B).

supports_put_single_values bool

OLSSC_SUP_PUT_SINGLE_VALUES — the subsystem supports the simultaneous multi-channel single-value write (olDaPutSingleValues). Bench read-back pending (WS-B).

supports_synchronous_digitalio bool

OLSSC_SUP_SYNCHRONOUS_DIGITALIO — the subsystem supports synchronous digital output interleaved with the analog stream. Bench read-back pending (WS-B).

current_outputs bool

OLSSC_CURRENT_OUTPUTS — the D/A drives current (not voltage) outputs. Bench read-back pending (WS-B).

max_digitaliolist_value int

OLSSC_MAX_DIGITALIOLIST_VALUE — max value for a synchronous-DIO list entry. Bench read-back pending (WS-B).

resolution int

olDaGetResolution — bits per sample. For an A/D this is the ADC resolution; for a digital subsystem it is the port width (number of lines per port; 8 on the DT9805/06 DIN/DOUT). 0 if the subsystem does not report it.

num_channels int

OLSSC_NUMCHANNELS — for digital subsystems this is the number of ports, not lines (1 on the DT9805/06).

cgl_depth int

OLSSC_CGLDEPTH — maximum channel-list size.

max_throughput_hz float | None

OLSSCE_MAX_THROUGHPUT — float Hz. None if not reported by the subsystem.

ranges tuple[tuple[float, float], ...]

Supported input ranges as (max_volts, min_volts) pairs from olDaEnumSSCaps(OL_ENUM_RANGES). Empty if the subsystem does not enumerate ranges.

gains tuple[float, ...]

Supported programmable-gain values from olDaEnumSSCaps(OL_ENUM_GAINS) (e.g. (1.0, 10.0, 100.0, 500.0) on the DT9805/06 A/D). Empty if not enumerated.

supports_simultaneous

supports_simultaneous()

True if either AI or AO simultaneous-sample-hold is supported.

Source code in src/dtollib/system/capabilities.py
def supports_simultaneous(self) -> bool:
    """True if either AI or AO simultaneous-sample-hold is supported."""
    return self.supports_simultaneous_sh or self.supports_simultaneous_da

DeviceInfo dataclass

DeviceInfo(*, name, board, subsystem_type, element)

A single device endpoint = board + one subsystem.

Mirrors the nidaqlib :class:DeviceInfo shape so cross-instrument experiment scripts that loop over find_devices() see the same field names. Discovery populates board + subsystem_type + element; single-value sessions construct a :class:DeviceInfo per open subsystem.

Attributes:

Name Type Description
name str

Human-readable device identifier; typically f"{board.name}/{subsystem_type.value}{element}".

board BoardInfo

Owning :class:BoardInfo.

subsystem_type SubsystemType

Typed subsystem kind.

element int

Element index of the subsystem on the board.

SubsystemInfo dataclass

SubsystemInfo(
    *,
    type,
    element,
    num_channels,
    supports_singlevalue,
    supports_continuous,
    supports_simultaneous_sh,
    supports_multisensor,
    supports_dma,
    returns_floats,
    max_throughput_hz,
    cgl_depth,
)

One subsystem on a board, as reported by capability queries.

The capability-query surface populates the boolean-cap fields and the two most common integer fields (num_channels, cgl_depth). Sensor-list capabilities, range lists, etc. are not yet populated.

Attributes:

Name Type Description
type SubsystemType

Typed subsystem kind.

element int

Element index — 0 for the first AI subsystem, the second AI subsystem on the same board has element=1, and so on. Used as the third argument to :func:olDaGetDASS.

num_channels int

Number of physical channels on the subsystem (OLSSC_NUMCHANNELS).

supports_singlevalue bool

OLSSC_SUP_SINGLEVALUE.

supports_continuous bool

OLSSC_SUP_CONTINUOUS.

supports_simultaneous_sh bool

OLSSC_SUP_SIMULTANEOUS_SH — simultaneous-sample-and-hold (single-call read of every channel at one timestamp).

supports_multisensor bool

OLSSC_SUP_MULTISENSOR — channels can be re-typed at configure time (DT9805/DT9806).

supports_dma bool

OLSSC_SUP_DMA.

returns_floats bool

OLSSC_RETURNS_FLOATS — subsystem returns engineering units, skip code-to-volts conversion.

max_throughput_hz float | None

OLSSCE_MAX_THROUGHPUT (float Hz). None if the SDK does not report it for this subsystem type.

cgl_depth int

OLSSC_CGLDEPTH — maximum channel-list size.

find_devices async

find_devices(*, backend=None)

Enumerate every installed DT-Open Layers board.

Parameters:

Name Type Description Default
backend DtolBackend | None

Backend to query. Defaults to a freshly-constructed :class:~dtollib.backend.dataacq.DataAcqBackend — the test suite injects a :class:~dtollib.backend.fake.FakeDtolBackend here.

None

Returns:

Type Description
list[BoardInfo]

List of :class:BoardInfo. Empty list if no boards are

list[BoardInfo]

installed or the SDK is unavailable.

Source code in src/dtollib/system/discovery.py
async def find_devices(*, backend: DtolBackend | None = None) -> list[BoardInfo]:
    """Enumerate every installed DT-Open Layers board.

    Args:
        backend: Backend to query.  Defaults to a freshly-constructed
            :class:`~dtollib.backend.dataacq.DataAcqBackend` — the
            test suite injects a
            :class:`~dtollib.backend.fake.FakeDtolBackend` here.

    Returns:
        List of :class:`BoardInfo`.  Empty list if no boards are
        installed or the SDK is unavailable.
    """
    resolved = backend if backend is not None else await _default_backend()
    try:
        return await anyio.to_thread.run_sync(resolved.enum_boards)
    except Exception:
        _logger.exception("find_devices failed")
        return []

find_subsystems async

find_subsystems(board, *, backend=None)

Enumerate subsystems on a board.

Parameters:

Name Type Description Default
board BoardInfo | str

A :class:BoardInfo from :func:find_devices, or a raw board name string.

required
backend DtolBackend | None

Backend to query. Defaults to a freshly-constructed :class:~dtollib.backend.dataacq.DataAcqBackend.

None

Returns:

Type Description
list[SubsystemInfo]

List of :class:SubsystemInfo. Empty list on any failure;

list[SubsystemInfo]

the failure is logged.

Source code in src/dtollib/system/discovery.py
async def find_subsystems(
    board: BoardInfo | str,
    *,
    backend: DtolBackend | None = None,
) -> list[SubsystemInfo]:
    """Enumerate subsystems on a board.

    Args:
        board: A :class:`BoardInfo` from :func:`find_devices`, or a
            raw board name string.
        backend: Backend to query.  Defaults to a freshly-constructed
            :class:`~dtollib.backend.dataacq.DataAcqBackend`.

    Returns:
        List of :class:`SubsystemInfo`.  Empty list on any failure;
        the failure is logged.
    """
    resolved = backend if backend is not None else await _default_backend()
    name = board if isinstance(board, str) else board.name
    try:
        return await anyio.to_thread.run_sync(resolved.enum_subsystems, name)
    except Exception:
        _logger.exception("find_subsystems(%s) failed", name)
        return []

query_capabilities

query_capabilities(api, hdass)

Build a :class:CapabilitySet for hdass from SDK queries.

Composition order matters per the SDK manual: olDaGetSSCaps is queried first to establish which downstream queries are valid. The boolean / integer / float caps below are each queried individually; subsystems that don't support a particular cap return an error from olDaGetSSCaps which we map to the "feature absent" default.

Parameters:

Name Type Description Default
api OpenLayersApi

Bound :class:OpenLayersApi.

required
hdass int

Subsystem handle.

required

Returns:

Name Type Description
Immutable CapabilitySet

class:CapabilitySet populated from live SDK

CapabilitySet

queries.

Source code in src/dtollib/system/capabilities.py
def query_capabilities(api: OpenLayersApi, hdass: int) -> CapabilitySet:
    """Build a :class:`CapabilitySet` for ``hdass`` from SDK queries.

    Composition order matters per the SDK manual: ``olDaGetSSCaps``
    is queried first to establish which downstream queries are valid.
    The boolean / integer / float caps below are each queried
    individually; subsystems that don't support a particular cap
    return an error from ``olDaGetSSCaps`` which we map to the
    "feature absent" default.

    Args:
        api: Bound :class:`OpenLayersApi`.
        hdass: Subsystem handle.

    Returns:
        Immutable :class:`CapabilitySet` populated from live SDK
        queries.
    """
    return CapabilitySet(
        supports_singlevalue=_bool_cap(api, hdass, OLSSC_SUP_SINGLEVALUE),
        supports_continuous=_bool_cap(api, hdass, OLSSC_SUP_CONTINUOUS),
        supports_simultaneous_sh=_bool_cap(api, hdass, OLSSC_SUP_SIMULTANEOUS_SH),
        supports_simultaneous_da=_bool_cap(api, hdass, OLSSC_SUP_SIMULTANEOUS_DA),
        supports_multisensor=_bool_cap(api, hdass, OLSSC_SUP_MULTISENSOR),
        supports_singleended=_bool_cap(api, hdass, OLSSC_SUP_SINGLEENDED),
        supports_dma=_bool_cap(api, hdass, OLSSC_SUP_DMA),
        supports_autocal=_bool_cap(api, hdass, OLSSC_SUP_AUTOCAL),
        supports_singlevalue_autorange=_bool_cap(api, hdass, OLSSC_SUP_SINGLEVALUE_AUTORANGE),
        supports_inprocess_flush=_bool_cap(api, hdass, OLSSC_SUP_INPROCESSFLUSH),
        supports_interleaved_cjc_in_stream=_bool_cap(
            api, hdass, OLSSC_SUP_INTERLEAVED_CJC_IN_STREAM
        ),
        returns_floats=_bool_cap(api, hdass, OLSSC_RETURNS_FLOATS),
        supports_thermocouples=_bool_cap(api, hdass, OLSSC_SUP_THERMOCOUPLES),
        supports_ctmode_measure=_bool_cap(api, hdass, OLSSC_SUP_CTMODE_MEASURE),
        supports_quadrature_decoder=_bool_cap(api, hdass, OLSSC_SUP_QUADRATURE_DECODER),
        supports_mute=_bool_cap(api, hdass, OLSSC_SUP_MUTE),
        supports_wrp_waveform=_bool_cap(api, hdass, OLSSC_SUP_WRPWAVEFORM),
        supports_put_single_values=_bool_cap(api, hdass, OLSSC_SUP_PUT_SINGLE_VALUES),
        supports_synchronous_digitalio=_bool_cap(api, hdass, OLSSC_SUP_SYNCHRONOUS_DIGITALIO),
        current_outputs=_bool_cap(api, hdass, OLSSC_CURRENT_OUTPUTS),
        max_digitaliolist_value=_int_cap(api, hdass, OLSSC_MAX_DIGITALIOLIST_VALUE),
        resolution=_resolution(api, hdass),
        num_channels=_int_cap(api, hdass, OLSSC_NUMCHANNELS),
        cgl_depth=_int_cap(api, hdass, OLSSC_CGLDEPTH),
        max_throughput_hz=_float_cap(api, hdass, OLSSCE_MAX_THROUGHPUT),
        ranges=tuple(_enum_cap(api, hdass, OL_ENUM_RANGES)),
        gains=tuple(g[0] for g in _enum_cap(api, hdass, OL_ENUM_GAINS)),
    )