Skip to content

AiDevice.t_in(): passing TcType as scale argument accepted silently, returns wrong-unit data #70

Description

@pureagave

Package: uldaq (PyPI, v1.2.3)
Method: AiDevice.t_in(channel, scale, flags)

What happened

I accidentally passed a TcType member as the scale argument instead of a TempScale member:

# Bug: TcType.K passed where TempScale is expected
val = ai_device.t_in(channel, TcType.K, TInFlag.DEFAULT)

# Correct call
val = ai_device.t_in(channel, TempScale.CELSIUS, TInFlag.DEFAULT)

The call succeeded without error. TcType.K has integer value 2; TempScale.FAHRENHEIT also has integer value 2. The C library received a valid integer and interpreted it as a request for Fahrenheit readings. My code then labelled those readings as Celsius.

Observed symptom: a thermocouple at ~22 °C read 71.6 — exactly (22 × 9/5) + 32, i.e. the correct Fahrenheit value. The bug was live in a lab data-acquisition system for several weeks because we had no thermocouples wired to live hardware during that period.

Why this is an easy mistake to make

TcType and TempScale appear together in typical usage: you call set_chan_tc_type(channel, TcType.K) to configure the channel, then t_in(channel, TempScale.CELSIUS, TInFlag.DEFAULT) to read it. The two enums are imported from the same module, used in the same workflow, and share at least one integer value. Passing the wrong one is a natural slip, not an exotic misuse.

Python enums are not strictly typed at runtime. Any IntEnum-compatible value crosses the CFFI boundary unvalidated, so the C library has no way to detect the mismatch.

Suggested fix (Python binding)

A type annotation plus a one-line runtime guard in ai_device.py would convert this silent data error into an immediate, readable exception:

def t_in(self, channel: int, scale: TempScale, flags: TInFlag) -> float:
    if not isinstance(scale, TempScale):
        raise TypeError(
            f"scale must be a TempScale member (CELSIUS, FAHRENHEIT, KELVIN, or VOLTS), "
            f"got {type(scale).__name__!r}"
        )
    # ... existing implementation

The same pattern could be applied to the flags argument and to similar methods (t_in_scan, etc.) for consistency, but scale on t_in is the highest-value fix given the integer collision with TcType.

I'm happy to open a PR with this change if that would be useful — the diff is small.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions