#!/usr/bin/env python3
"""
Analog input quick start for the USB-6009 / 6001 / 6212 / 6229 / 4431 series.

The same script runs against every model on the site: pass a different device
name and channel list and nothing else changes.

    pip install nidaqmx
    python python-quickstart.py --device Dev1 --channels ai0:3

Two acquisition modes are demonstrated:

  1. read_one()        single software-timed point per channel — fine for
                       temperature, strain and other slow signals.
  2. read_buffered()   hardware-timed block using the device clock — this is
                       what you want above a few hundred samples per second.

Run with --mode to pick one; the default runs both.
"""

from __future__ import annotations

import argparse
import sys
import time

try:
    import nidaqmx
    from nidaqmx.constants import AcquisitionType, TerminalConfiguration
except ImportError:  # pragma: no cover
    sys.exit("nidaqmx is not installed. Run: pip install nidaqmx")


def read_one(device: str, channels: str, samples: int = 10, interval: float = 0.2) -> None:
    """Software-timed single-point reads."""
    print(f"\n[1/2] Single-point reads on {device}/{channels}")
    print("-" * 56)

    with nidaqmx.Task() as task:
        task.ai_channels.add_ai_voltage_chan(
            f"{device}/{channels}",
            terminal_config=TerminalConfiguration.RSE,
            min_val=-10.0,
            max_val=10.0,
        )

        for i in range(samples):
            value = task.read()
            print(f"  {i + 1:3d}  {value}")
            time.sleep(interval)


def read_buffered(
    device: str,
    channels: str,
    rate: float = 10_000.0,
    samples_per_channel: int = 1_000,
) -> None:
    """Hardware-timed block acquisition."""
    print(f"\n[2/2] Buffered acquisition on {device}/{channels}")
    print(f"      {rate:,.0f} S/s, {samples_per_channel:,} samples per channel")
    print("-" * 56)

    with nidaqmx.Task() as task:
        task.ai_channels.add_ai_voltage_chan(
            f"{device}/{channels}",
            terminal_config=TerminalConfiguration.RSE,
            min_val=-10.0,
            max_val=10.0,
        )
        task.timing.cfg_samp_clk_timing(
            rate=rate,
            sample_mode=AcquisitionType.FINITE,
            samps_per_chan=samples_per_channel,
        )

        started = time.perf_counter()
        data = task.read(number_of_samples_per_channel=samples_per_channel)
        elapsed = time.perf_counter() - started

    # nidaqmx returns a flat list for multi-channel reads; reshape it.
    if data and isinstance(data[0], list):
        columns = data
    else:
        columns = [data]

    for ch_index, column in enumerate(columns):
        lo = min(column)
        hi = max(column)
        mean = sum(column) / len(column)
        print(
            f"  ch{ch_index}: {len(column):,} samples  "
            f"min {lo:+.4f} V  max {hi:+.4f} V  mean {mean:+.4f} V"
        )

    print(f"  wall clock: {elapsed * 1000:.1f} ms")


def main() -> int:
    parser = argparse.ArgumentParser(description="Analog input quick start")
    parser.add_argument("--device", default="Dev1", help="device name, e.g. Dev1")
    parser.add_argument("--channels", default="ai0:3", help="channel list, e.g. ai0:3")
    parser.add_argument("--rate", type=float, default=10_000.0, help="sample rate in S/s")
    parser.add_argument("--mode", choices=["one", "buffered", "both"], default="both")
    args = parser.parse_args()

    print(f"Device : {args.device}")
    print(f"Channels: {args.channels}")

    try:
        if args.mode in ("one", "both"):
            read_one(args.device, args.channels)
        if args.mode in ("buffered", "both"):
            read_buffered(args.device, args.channels, rate=args.rate)
    except nidaqmx.DaqError as exc:
        print(f"\nDAQ error: {exc}", file=sys.stderr)
        print("Check the device name with the configuration utility.", file=sys.stderr)
        return 1

    print("\nDone.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
