Skip to content

nidaqlib.system

nidaqlib.system

System discovery — :func:find_devices, :func:list_physical_channels.

See :mod:nidaqlib.system.discovery for behaviour and the :class:DiscoveryResult / :class:DeviceInfo shapes.

DeviceInfo dataclass

DeviceInfo(
    *,
    name,
    product_type,
    serial_number,
    ai_physical_channels,
    ao_physical_channels,
    di_lines,
    do_lines,
    ci_physical_channels,
    co_physical_channels,
)

Snapshot of one NI device's identity and physical-channel inventory.

Cached on a :class:~nidaqlib.tasks.session.DaqSession at configure time and surfaced by :meth:DaqSession.snapshot. The :func:~nidaqlib.system.discovery.find_devices enumeration also wraps a populated :class:DeviceInfo inside each successful :class:DiscoveryResult.

DiscoveryResult dataclass

DiscoveryResult(
    *,
    ok,
    port,
    address=None,
    baudrate=None,
    protocol=None,
    device_info=None,
    error=None,
    elapsed_s=0.0,
)

One enumeration row from :func:find_devices.

Shape-shared across sibling libraries (alicatlib, sartoriuslib, watlowlib) so cross-instrument tooling can join on a common discovery record.

Attributes:

Name Type Description
ok bool

True when the row describes a healthy device. False is reserved for enumeration-level driver failures, which surface as a single ok=False row carrying error.

port str

NI device name (Dev1, cDAQ1Mod3). Empty string only when ok=False and the driver call failed before any name could be read.

address str | int | None

Always None for NI (no multi-drop address concept).

baudrate int | None

Always None for NI (no serial line).

protocol ProtocolKind | None

Always None for NI (no wire protocol).

device_info DeviceInfo | None

Populated only when ok=True. None on error rows.

error NIDaqError | None

Populated only when ok=False. None on healthy rows.

elapsed_s float

Wall-clock seconds spent enumerating this entry.

NIDaqDiscoveryResult dataclass

NIDaqDiscoveryResult(
    *,
    ok,
    port,
    address=None,
    baudrate=None,
    protocol=None,
    device_info=None,
    error=None,
    elapsed_s=0.0,
    product_type=None,
    serial_number=None,
    chassis=None,
    physical_module=None,
)

Bases: DiscoveryResult

NI-specific discovery row carrying product / chassis identity.

Subclasses :class:DiscoveryResult without renaming any base fields. The NI extras (product_type, serial_number, chassis, physical_module) sit alongside the shape-shared base.

find_devices

find_devices()

Enumerate NI DAQ devices visible to the driver. Never raises.

Returns:

Name Type Description
One list[DiscoveryResult]

class:NIDaqDiscoveryResult per device. Empty list when no

list[DiscoveryResult]

NI hardware is present. On enumeration-level failure (driver

list[DiscoveryResult]

missing, system call raises), returns a single ok=False row

list[DiscoveryResult]

with error populated.

Source code in src/nidaqlib/system/discovery.py
def find_devices() -> list[DiscoveryResult]:
    """Enumerate NI DAQ devices visible to the driver. Never raises.

    Returns:
        One :class:`NIDaqDiscoveryResult` per device. Empty list when no
        NI hardware is present. On enumeration-level failure (driver
        missing, system call raises), returns a single ``ok=False`` row
        with ``error`` populated.
    """
    try:
        nidaqmx = _import_nidaqmx()
    except NIDaqDependencyError as exc:
        return [
            DiscoveryResult(
                ok=False,
                port="",
                error=exc,
                elapsed_s=0.0,
            )
        ]

    started = time.monotonic()
    try:
        system = nidaqmx.system.System.local()
        devices = list(system.devices)
    except (nidaqmx.errors.DaqNotFoundError, nidaqmx.errors.DaqNotSupportedError) as exc:
        # Driver not installed, or platform unsupported (e.g. darwin). Neither
        # subclasses DaqError, so they need their own clause — surface as a
        # dependency failure rather than a backend error.
        elapsed = time.monotonic() - started
        wrapped: NIDaqError = NIDaqDependencyError(str(exc))
        wrapped.__cause__ = exc
        return [
            DiscoveryResult(
                ok=False,
                port="",
                error=wrapped,
                elapsed_s=elapsed,
            )
        ]
    except nidaqmx.errors.DaqError as exc:  # pragma: no cover — hardware path
        elapsed = time.monotonic() - started
        wrapped = NIDaqBackendError(
            "failed to enumerate NI devices",
            context=ErrorContext(
                command_name="find_devices",
                ni_error_code=getattr(exc, "error_code", None),
            ),
        )
        wrapped.__cause__ = exc
        return [
            DiscoveryResult(
                ok=False,
                port="",
                error=wrapped,
                elapsed_s=elapsed,
            )
        ]

    results: list[DiscoveryResult] = []
    for dev in devices:
        per_started = time.monotonic()
        name = str(_safe_attr(dev, "name", ""))
        product_type = _safe_attr(dev, "product_type")
        serial_num = _safe_attr(dev, "serial_num")
        info = DeviceInfo(
            name=name,
            product_type=str(product_type) if product_type is not None else None,
            serial_number=str(serial_num or "") or None,
            ai_physical_channels=_channel_names(_safe_attr(dev, "ai_physical_chans", ())),
            ao_physical_channels=_channel_names(_safe_attr(dev, "ao_physical_chans", ())),
            di_lines=_channel_names(_safe_attr(dev, "di_lines", ())),
            do_lines=_channel_names(_safe_attr(dev, "do_lines", ())),
            ci_physical_channels=_channel_names(_safe_attr(dev, "ci_physical_chans", ())),
            co_physical_channels=_channel_names(_safe_attr(dev, "co_physical_chans", ())),
        )
        results.append(
            NIDaqDiscoveryResult(
                ok=True,
                port=name,
                device_info=info,
                product_type=info.product_type,
                serial_number=info.serial_number,
                chassis=_chassis_of(dev),
                physical_module=_physical_module_of(dev),
                elapsed_s=time.monotonic() - per_started,
            )
        )
    return results

list_physical_channels

list_physical_channels(device)

Return AI physical channel names for device (e.g. "Dev1").

For AO / DI / DO / counter inventories, use :func:find_devices and inspect the returned :class:DeviceInfo.

Raises:

Type Description
NIDaqBackendError

NI rejected the request (e.g. unknown device).

Source code in src/nidaqlib/system/discovery.py
def list_physical_channels(device: str) -> tuple[str, ...]:
    """Return AI physical channel names for ``device`` (e.g. ``"Dev1"``).

    For AO / DI / DO / counter inventories, use :func:`find_devices` and
    inspect the returned :class:`DeviceInfo`.

    Raises:
        NIDaqBackendError: NI rejected the request (e.g. unknown device).
    """
    nidaqmx = _import_nidaqmx()
    try:
        dev = nidaqmx.system.Device(device)
        return _channel_names(dev.ai_physical_chans)
    except nidaqmx.errors.DaqError as exc:  # pragma: no cover - hardware path
        raise NIDaqBackendError(
            f"failed to list physical channels for {device!r}",
            context=ErrorContext(
                command_name="list_physical_channels",
                ni_error_code=getattr(exc, "error_code", None),
            ),
        ) from exc