From 67fc7a6af16c6143823db2557f7fad15fcad8640 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Tue, 30 Sep 2025 14:26:29 -0500 Subject: [PATCH 1/7] use waveforms in an example --- .../nidaqmx_continuous_analog_input.py | 28 ++++++----- .../nidaqmx_continuous_analog_input_panel.py | 48 ++++++++++++++----- poetry.lock | 20 ++++---- pyproject.toml | 2 +- 4 files changed, 61 insertions(+), 37 deletions(-) diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py index 697d6cd..245406f 100644 --- a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py @@ -1,10 +1,13 @@ """Data acquisition script that continuously acquires analog input data.""" +import os import time from pathlib import Path +from typing import cast -import nidaqmx -from nidaqmx.constants import ( +os.environ["NIDAQMX_ENABLE_WAVEFORM_SUPPORT"] = "1" +import nidaqmx # noqa: E402 # Must import after setting os environment variable +from nidaqmx.constants import ( # noqa: E402 AcquisitionType, CJCSource, LoggingMode, @@ -13,22 +16,23 @@ TerminalConfiguration, ThermocoupleType, ) -from nidaqmx.errors import DaqError +from nidaqmx.errors import DaqError # noqa: E402 +from nitypes.waveform import AnalogWaveform # noqa: E402 -import nipanel +import nipanel # noqa: E402 panel_script_path = Path(__file__).with_name("nidaqmx_continuous_analog_input_panel.py") panel = nipanel.create_streamlit_panel(panel_script_path) try: panel.set_value("daq_error", "") + panel.set_value("is_running", False) print(f"Panel URL: {panel.panel_url}") print(f"Waiting for the 'Run' button to be pressed...") print(f"(Press Ctrl + C to quit)") while True: - panel.set_value("run_button", False) - while not panel.get_value("run_button", False): + while not panel.get_value("is_running", False): time.sleep(0.1) # How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/ @@ -66,15 +70,13 @@ try: print(f"Starting data acquisition...") task.start() - panel.set_value("is_running", True) - panel.set_value("stop_button", False) - while not panel.get_value("stop_button", False): - data = task.read( - number_of_samples_per_channel=1000 # pyright: ignore[reportArgumentType] + while panel.get_value("is_running", False): + waveforms = cast( + list[AnalogWaveform], task.read_waveform(number_of_samples_per_channel=1000) ) - panel.set_value("voltage_data", data[0]) - panel.set_value("thermocouple_data", data[1]) + panel.set_value("voltage_waveform", waveforms[0]) + panel.set_value("thermocouple_waveform", waveforms[1]) except KeyboardInterrupt: raise finally: diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py index 942fe3d..642747e 100644 --- a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py @@ -1,5 +1,8 @@ """Streamlit visualization script to display data acquired by nidaqmx_continuous_analog_input.py.""" +from typing import cast + +import hightime as ht import streamlit as st from nidaqmx.constants import ( TerminalConfiguration, @@ -8,6 +11,7 @@ ThermocoupleType, LoggingMode, ) +from nitypes.waveform import AnalogWaveform from streamlit_echarts import st_echarts import nipanel @@ -34,20 +38,27 @@ unsafe_allow_html=True, ) + +def click_start(): + panel.set_value("is_running", True) + + +def click_stop(): + panel.set_value("is_running", False) + + panel = nipanel.get_streamlit_panel_accessor() is_running = panel.get_value("is_running", False) if is_running: - st.button(r"⏹️ Stop", key="stop_button") + st.button(r"⏹️ Stop", key="stop_button", on_click=click_stop) elif not is_running and panel.get_value("daq_error", "") == "": - st.button(r"▶️ Run", key="run_button") + st.button(r"▶️ Run", key="run_button", on_click=click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_continuous_analog_input.py \n\n {panel.get_value('daq_error', '')}" ) -thermocouple_data = panel.get_value("thermocouple_data", [0.0]) -voltage_data = panel.get_value("voltage_data", [0.0]) sample_rate = panel.get_value("sample_rate", 0.0) # Create two-column layout for the entire interface @@ -176,15 +187,28 @@ with st.container(border=True): # Graph section st.header("Voltage & Thermocouple") + + thermocouple_waveform = panel.get_value("thermocouple_waveform", AnalogWaveform()) + voltage_waveform = panel.get_value("voltage_waveform", AnalogWaveform()) + if voltage_waveform.sample_count == 0: + time_labels = ["00:00:00.000"] + else: + timestamps = cast( + list[ht.datetime], + list(voltage_waveform.timing.get_timestamps(0, voltage_waveform.sample_count)), + ) + time_labels = [ + f"{ts.hour:02d}:{ts.minute:02d}:{ts.second:02d}.{ts.microsecond//1000:03d}" + for ts in timestamps + ] + voltage_therm_graph = { "animation": False, "tooltip": {"trigger": "axis"}, - "legend": {"data": ["Voltage (V)", "Temperature (C)"]}, + "legend": {"data": [voltage_waveform.units, thermocouple_waveform.units]}, "xAxis": { "type": "category", - "data": [ - x / sample_rate if sample_rate > 0.001 else x for x in range(len(voltage_data)) - ], + "data": time_labels, "name": "Time", "nameLocation": "center", "nameGap": 40, @@ -198,17 +222,17 @@ }, "series": [ { - "name": "voltage_amplitude", + "name": voltage_waveform.units, "type": "line", - "data": voltage_data, + "data": list(voltage_waveform.scaled_data), "emphasis": {"focus": "series"}, "smooth": True, "seriesLayoutBy": "row", }, { - "name": "thermocouple_amplitude", + "name": thermocouple_waveform.units, "type": "line", - "data": thermocouple_data, + "data": list(thermocouple_waveform.scaled_data), "color": "red", "emphasis": {"focus": "series"}, "smooth": True, diff --git a/poetry.lock b/poetry.lock index b89397c..7c04fe8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1278,31 +1278,29 @@ toml = ">=0.10.1" [[package]] name = "nidaqmx" -version = "1.2.0" +version = "1.3.0.dev0" description = "NI-DAQmx Python API" optional = false python-versions = "<4.0,>=3.9" groups = ["examples"] files = [ - {file = "nidaqmx-1.2.0-py3-none-any.whl", hash = "sha256:c2b8d6d39556a7920d966bf4aff6f5229e8df3bdf3b49c24dc68a2d42dfd5d30"}, - {file = "nidaqmx-1.2.0.tar.gz", hash = "sha256:faeea9889a2dae03b7e23f525a44889264a5cfbf5590558e3792c28871620c87"}, + {file = "nidaqmx-1.3.0.dev0-py3-none-any.whl", hash = "sha256:0402adef089a58e2eb8e2191210201f32f8ec49aec02f9dcaa0a3a751e5253e3"}, + {file = "nidaqmx-1.3.0.dev0.tar.gz", hash = "sha256:672fdbcff7aec9fa1476cd96d000dc68112baef0d713946f368796e049aec5c7"}, ] [package.dependencies] -click = [ - {version = ">=8.0.0", markers = "python_version >= \"3.10\" and python_version < \"4.0\""}, - {version = ">=8.0.0,<8.2.0", markers = "python_version == \"3.9\""}, -] +click = ">=8.0.0" deprecation = ">=2.1" distro = {version = ">=1.9.0", markers = "sys_platform == \"linux\""} hightime = ">=0.2.2" +nitypes = ">=0.1.0dev10" numpy = [ - {version = ">=1.26", markers = "python_version == \"3.12\""}, + {version = ">=1.22", markers = "python_version >= \"3.9\" and python_version < \"3.13\""}, {version = ">=2.1", markers = "python_version >= \"3.13\" and python_version < \"4.0\""}, - {version = ">=1.22", markers = "python_version >= \"3.9\" and python_version < \"3.12\""}, ] python-decouple = ">=3.8" requests = ">=2.25.0" +typing_extensions = ">=4.0.0" tzlocal = ">=5.0,<6.0" [package.extras] @@ -1348,7 +1346,7 @@ version = "0.1.0.dev10" description = "Data types for NI Python APIs" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] +groups = ["main", "examples"] files = [ {file = "nitypes-0.1.0.dev10-py3-none-any.whl", hash = "sha256:c4f097ffdeaf05697a7752a1e2b712760b28e84a4f6f446b4b7b76b349927490"}, {file = "nitypes-0.1.0.dev10.tar.gz", hash = "sha256:e319ebbdefca63db8e879b9f119cc4f76a086c550931dea2be0e33e9c10e9d9f"}, @@ -3365,4 +3363,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0,!=3.9.7" -content-hash = "2919e6e9f65f22ab74586d2aba7b46dbc58b8afbb316f7f80b8483030ce41f8d" +content-hash = "c7263b58c3c1e89dd34425fc2764c6bee106f5e7636356d4f93bb8a3d1e74126" diff --git a/pyproject.toml b/pyproject.toml index 76b4fa7..eccdaad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,7 +93,7 @@ optional = true [tool.poetry.group.examples.dependencies] streamlit-echarts = ">=0.4.0" extra-streamlit-components = "^0.1.80" -nidaqmx = { version = ">=0.8.0", allow-prereleases = true } +nidaqmx = { version = ">=1.3.0-dev0", allow-prereleases = true } niscope = "^1.4.9" [build-system] From 20df7cd25e7200fa3505c21c17e4c4f5e50a3243 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Tue, 30 Sep 2025 15:21:14 -0500 Subject: [PATCH 2/7] update filtering example --- .../nidaqmx_analog_input_filtering.py | 26 +++++------ .../nidaqmx_analog_input_filtering_panel.py | 43 +++++++++++++++---- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py index 62b6511..00e2307 100644 --- a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py +++ b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py @@ -1,11 +1,13 @@ """Data acquisition script that continuously acquires analog input data.""" +import os import time from pathlib import Path -import nidaqmx -import nidaqmx.system -from nidaqmx.constants import ( +os.environ["NIDAQMX_ENABLE_WAVEFORM_SUPPORT"] = "1" +import nidaqmx # noqa: E402 # Must import after setting os environment variable +import nidaqmx.system # noqa: E402 +from nidaqmx.constants import ( # noqa: E402 AcquisitionType, CurrentShuntResistorLocation, CurrentUnits, @@ -16,9 +18,9 @@ StrainGageBridgeType, TerminalConfiguration, ) -from nidaqmx.errors import DaqError +from nidaqmx.errors import DaqError # noqa: E402 -import nipanel +import nipanel # noqa: E402 panel_script_path = Path(__file__).with_name("nidaqmx_analog_input_filtering_panel.py") panel = nipanel.create_streamlit_panel(panel_script_path) @@ -44,8 +46,7 @@ print(f"Waiting for the 'Run' button to be pressed...") print(f"(Press Ctrl + C to quit)") while True: - panel.set_value("run_button", False) - while not panel.get_value("run_button", False): + while not panel.get_value("is_running", False): time.sleep(0.1) # How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/ with nidaqmx.Task() as task: @@ -131,14 +132,9 @@ try: task.start() - panel.set_value("is_running", True) - - panel.set_value("stop_button", False) - while not panel.get_value("stop_button", False): - data = task.read( - number_of_samples_per_channel=100 # pyright: ignore[reportArgumentType] - ) - panel.set_value("acquired_data", data) + while panel.get_value("is_running", False): + waveform = task.read_waveform(number_of_samples_per_channel=100) + panel.set_value("waveform", waveform) except KeyboardInterrupt: pass finally: diff --git a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py index 0828b02..6db3a03 100644 --- a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py +++ b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py @@ -1,5 +1,8 @@ """Streamlit visualization script to display data acquired by nidaqmx_analog_input_filtering.py.""" +from typing import cast + +import hightime as ht import extra_streamlit_components as stx # type: ignore[import-untyped] import streamlit as st from nidaqmx.constants import ( @@ -11,11 +14,21 @@ StrainGageBridgeType, TerminalConfiguration, ) +from nitypes.waveform import AnalogWaveform from streamlit_echarts import st_echarts import nipanel from nipanel.controls import enum_selectbox + +def click_start(): + panel.set_value("is_running", True) + + +def click_stop(): + panel.set_value("is_running", False) + + st.set_page_config(page_title="Analog Input Filtering", page_icon="📈", layout="wide") st.title("Analog Input - Filtering") panel = nipanel.get_streamlit_panel_accessor() @@ -49,9 +62,9 @@ with st.container(border=True): with st.container(border=True): if panel.get_value("is_running", True): - st.button("Stop", key="stop_button") + st.button("Stop", key="stop_button", on_click=click_stop) elif not panel.get_value("is_running", True) and panel.get_value("daq_error", "") == "": - run_button = st.button("Run", key="run_button") + run_button = st.button("Run", key="run_button", on_click=click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_analog_input_filtering.py \n\n {panel.get_value('daq_error', '')}" @@ -148,31 +161,43 @@ with st.container(border=True): st.title("Acquired Data") - acquired_data = panel.get_value("acquired_data", [0.0]) - sample_rate = panel.get_value("sample_rate", 100.0) + + waveform = panel.get_value("waveform", AnalogWaveform()) + if waveform.sample_count == 0: + time_labels = ["00:00:00.000"] + else: + timestamps = cast( + list[ht.datetime], + list(waveform.timing.get_timestamps(0, waveform.sample_count)), + ) + time_labels = [ + f"{ts.hour:02d}:{ts.minute:02d}:{ts.second:02d}.{ts.microsecond//1000:03d}" + for ts in timestamps + ] + acquired_data_graph = { "animation": False, "tooltip": {"trigger": "axis"}, - "legend": {"data": ["Voltage (V)"]}, + "legend": {"data": [waveform.units]}, "xAxis": { "type": "category", - "data": [x / sample_rate for x in range(len(acquired_data))], + "data": time_labels, "name": "Time", "nameLocation": "center", "nameGap": 40, }, "yAxis": { "type": "value", - "name": "Volts", + "name": waveform.units, "nameRotate": 90, "nameLocation": "center", "nameGap": 40, }, "series": [ { - "name": "voltage_amplitude", + "name": waveform.units, "type": "line", - "data": acquired_data, + "data": list(waveform.scaled_data), "emphasis": {"focus": "series"}, "smooth": True, "seriesLayoutBy": "row", From 9882abdeb3b55f1d6f0c4ae189681047c1c88c8e Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 1 Oct 2025 09:41:56 -0500 Subject: [PATCH 3/7] update analog output example --- .../nidaqmx_analog_input_filtering_panel.py | 4 +- .../nidaqmx_analog_output_voltage.py | 49 ++++++++++++------- .../nidaqmx_analog_output_voltage_panel.py | 46 ++++++++++++----- 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py index 6db3a03..fc9fc48 100644 --- a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py +++ b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py @@ -62,9 +62,9 @@ def click_stop(): with st.container(border=True): with st.container(border=True): if panel.get_value("is_running", True): - st.button("Stop", key="stop_button", on_click=click_stop) + st.button("⏹️ Stop", key="stop_button", on_click=click_stop) elif not panel.get_value("is_running", True) and panel.get_value("daq_error", "") == "": - run_button = st.button("Run", key="run_button", on_click=click_start) + run_button = st.button("▶️ Run", key="run_button", on_click=click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_analog_input_filtering.py \n\n {panel.get_value('daq_error', '')}" diff --git a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py index e46dfb1..6d444c8 100644 --- a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py +++ b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py @@ -1,16 +1,21 @@ """Data acquisition script that continuously generates analog output data.""" +import os import time from pathlib import Path -import nidaqmx -import nidaqmx.stream_writers -import nidaqmx.system -import numpy as np -from nidaqmx.constants import AcquisitionType, Edge -from nidaqmx.errors import DaqError +os.environ["NIDAQMX_ENABLE_WAVEFORM_SUPPORT"] = "1" +import nidaqmx # noqa: E402 # Must import after setting os environment variable +import nidaqmx.stream_writers # noqa: E402 +import nidaqmx.system # noqa: E402 +import hightime as ht # noqa: E402 +import datetime as dt # noqa: E402 +import numpy as np # noqa: E402 +from nidaqmx.constants import AcquisitionType, Edge # noqa: E402 +from nidaqmx.errors import DaqError # noqa: E402 +from nitypes.waveform import AnalogWaveform, SampleIntervalMode, Timing # noqa: E402 -import nipanel +import nipanel # noqa: E402 panel_script_path = Path(__file__).with_name("nidaqmx_analog_output_voltage_panel.py") panel = nipanel.create_streamlit_panel(panel_script_path) @@ -29,15 +34,14 @@ for term in dev.terminals: available_trigger_sources.append(term) panel.set_value("available_trigger_sources", available_trigger_sources) -panel.set_value("run_button", False) +panel.set_value("is_running", False) try: panel.set_value("daq_error", "") print(f"Panel URL: {panel.panel_url}") print(f"Waiting for the 'Run' button to be pressed...") print(f"(Press Ctrl + C to quit)") while True: - panel.set_value("is_running", False) - while not panel.get_value("run_button", False): + while not panel.get_value("is_running", False): time.sleep(0.1) # How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/ with nidaqmx.Task() as task: @@ -74,22 +78,31 @@ wave_type = panel.get_value("wave_type", "Sine Wave") if wave_type == "Sine Wave": - waveform = amplitude * np.sin(2 * np.pi * frequency * t) + waveform = AnalogWaveform.from_array_1d( + amplitude * np.sin(2 * np.pi * frequency * t) + ) elif wave_type == "Square Wave": - waveform = amplitude * np.sign(np.sin(2 * np.pi * frequency * t)) + waveform = AnalogWaveform.from_array_1d( + amplitude * np.sign(np.sin(2 * np.pi * frequency * t)) + ) else: - waveform = amplitude * (2 * np.abs(2 * (t * frequency % 1) - 1) - 1) + waveform = AnalogWaveform.from_array_1d( + amplitude * (2 * np.abs(2 * (t * frequency % 1) - 1) - 1) + ) + waveform.timing = Timing( + sample_interval_mode=SampleIntervalMode.REGULAR, + sample_interval=ht.timedelta(seconds=1.0 / sample_rate), + ) + waveform.units = chan.ao_voltage_units.name writer = nidaqmx.stream_writers.AnalogSingleChannelWriter( task.out_stream, auto_start=False # pyright: ignore[reportArgumentType] ) - writer.write_many_sample(waveform) - panel.set_value("data", waveform.tolist()) + writer.write_waveform(waveform) + panel.set_value("waveform", waveform) try: task.start() - panel.set_value("is_running", True) - panel.set_value("stop_button", False) - while not panel.get_value("stop_button", False): + while panel.get_value("is_running", False): time.sleep(0.1) except KeyboardInterrupt: diff --git a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py index cee0292..389f129 100644 --- a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py +++ b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py @@ -1,13 +1,26 @@ """Streamlit visualization script to display data acquired by nidaqmx_analog_output_voltage.py.""" +from typing import cast + +import hightime as ht import extra_streamlit_components as stx # type: ignore[import-untyped] import streamlit as st from nidaqmx.constants import Edge +from nitypes.waveform import AnalogWaveform from streamlit_echarts import st_echarts import nipanel from nipanel.controls import enum_selectbox + +def click_start(): + panel.set_value("is_running", True) + + +def click_stop(): + panel.set_value("is_running", False) + + st.set_page_config(page_title="Voltage - Continuous Output", page_icon="📈", layout="wide") st.title("Voltage - Continuous Output") panel = nipanel.get_streamlit_panel_accessor() @@ -40,9 +53,9 @@ with st.container(border=True): is_running = panel.get_value("is_running", False) if is_running: - st.button("Stop", key="stop_button") + st.button("⏹️ Stop", key="stop_button", on_click=click_stop) elif not is_running and panel.get_value("daq_error", "") == "": - run_button = st.button("Run", key="run_button") + run_button = st.button("▶️ Run", key="run_button", on_click=click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_analog_output_voltage.py \n\n {panel.get_value('daq_error', '')}" @@ -130,38 +143,47 @@ with right_col: with st.container(border=True): st.title("Output") - acquired_data = panel.get_value("data", [0.0]) - sample_rate = panel.get_value("sample_rate", 1000.0) - acquired_data_graph = { + + waveform = panel.get_value("waveform", AnalogWaveform()) + if waveform.sample_count == 0: + time_labels = ["0.000"] + else: + timestamps = cast( + list[ht.datetime], + list(waveform.timing.get_timestamps(0, waveform.sample_count)), + ) + time_labels = [f"{ts.second}.{ts.microsecond//1000:03d}" for ts in timestamps] + + graph = { "animation": False, "tooltip": {"trigger": "axis"}, - "legend": {"data": ["Voltage (V)"]}, + "legend": {"data": [waveform.units]}, "xAxis": { "type": "category", - "data": [x / sample_rate for x in range(len(acquired_data))], - "name": "Time", + "data": time_labels, + "name": "Time (s)", "nameLocation": "center", "nameGap": 40, }, "yAxis": { "type": "value", - "name": "Amplitude", + "name": waveform.units, "nameRotate": 90, "nameLocation": "center", "nameGap": 40, }, "series": [ { - "name": "voltage_amplitude", + "name": waveform.units, "type": "line", - "data": acquired_data, + "data": list(waveform.scaled_data), "emphasis": {"focus": "series"}, "smooth": True, "seriesLayoutBy": "row", }, ], } - st_echarts(options=acquired_data_graph, height="400px", key="graph", width="100%") + st_echarts(options=graph, height="400px", key="graph", width="100%") with st.container(border=True): st.title("Trigger Settings") From 6a28bf82058db482b7020ef8fc4859057814dea9 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 1 Oct 2025 09:51:10 -0500 Subject: [PATCH 4/7] lint and mypy --- .../nidaqmx_analog_input_filtering_panel.py | 10 +++++----- .../nidaqmx_analog_output_voltage.py | 3 +-- .../nidaqmx_analog_output_voltage_panel.py | 10 +++++----- .../nidaqmx_continuous_analog_input.py | 4 +++- .../nidaqmx_continuous_analog_input_panel.py | 8 ++++---- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py index fc9fc48..4c94bb3 100644 --- a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py +++ b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py @@ -2,8 +2,8 @@ from typing import cast -import hightime as ht import extra_streamlit_components as stx # type: ignore[import-untyped] +import hightime as ht import streamlit as st from nidaqmx.constants import ( CurrentShuntResistorLocation, @@ -21,11 +21,11 @@ from nipanel.controls import enum_selectbox -def click_start(): +def _click_start() -> None: panel.set_value("is_running", True) -def click_stop(): +def _click_stop() -> None: panel.set_value("is_running", False) @@ -62,9 +62,9 @@ def click_stop(): with st.container(border=True): with st.container(border=True): if panel.get_value("is_running", True): - st.button("⏹️ Stop", key="stop_button", on_click=click_stop) + st.button("⏹️ Stop", key="stop_button", on_click=_click_stop) elif not panel.get_value("is_running", True) and panel.get_value("daq_error", "") == "": - run_button = st.button("▶️ Run", key="run_button", on_click=click_start) + run_button = st.button("▶️ Run", key="run_button", on_click=_click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_analog_input_filtering.py \n\n {panel.get_value('daq_error', '')}" diff --git a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py index 6d444c8..1a4dc83 100644 --- a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py +++ b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage.py @@ -5,11 +5,10 @@ from pathlib import Path os.environ["NIDAQMX_ENABLE_WAVEFORM_SUPPORT"] = "1" +import hightime as ht # noqa: E402 import nidaqmx # noqa: E402 # Must import after setting os environment variable import nidaqmx.stream_writers # noqa: E402 import nidaqmx.system # noqa: E402 -import hightime as ht # noqa: E402 -import datetime as dt # noqa: E402 import numpy as np # noqa: E402 from nidaqmx.constants import AcquisitionType, Edge # noqa: E402 from nidaqmx.errors import DaqError # noqa: E402 diff --git a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py index 389f129..6ad37ab 100644 --- a/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py +++ b/examples/nidaqmx/nidaqmx_analog_output_voltage/nidaqmx_analog_output_voltage_panel.py @@ -2,8 +2,8 @@ from typing import cast -import hightime as ht import extra_streamlit_components as stx # type: ignore[import-untyped] +import hightime as ht import streamlit as st from nidaqmx.constants import Edge from nitypes.waveform import AnalogWaveform @@ -13,11 +13,11 @@ from nipanel.controls import enum_selectbox -def click_start(): +def _click_start() -> None: panel.set_value("is_running", True) -def click_stop(): +def _click_stop() -> None: panel.set_value("is_running", False) @@ -53,9 +53,9 @@ def click_stop(): with st.container(border=True): is_running = panel.get_value("is_running", False) if is_running: - st.button("⏹️ Stop", key="stop_button", on_click=click_stop) + st.button("⏹️ Stop", key="stop_button", on_click=_click_stop) elif not is_running and panel.get_value("daq_error", "") == "": - run_button = st.button("▶️ Run", key="run_button", on_click=click_start) + run_button = st.button("▶️ Run", key="run_button", on_click=_click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_analog_output_voltage.py \n\n {panel.get_value('daq_error', '')}" diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py index 245406f..9eb5414 100644 --- a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py @@ -7,6 +7,7 @@ os.environ["NIDAQMX_ENABLE_WAVEFORM_SUPPORT"] = "1" import nidaqmx # noqa: E402 # Must import after setting os environment variable +import numpy as np # noqa: E402 from nidaqmx.constants import ( # noqa: E402 AcquisitionType, CJCSource, @@ -73,7 +74,8 @@ while panel.get_value("is_running", False): waveforms = cast( - list[AnalogWaveform], task.read_waveform(number_of_samples_per_channel=1000) + list[AnalogWaveform[np.float64]], + task.read_waveform(number_of_samples_per_channel=1000), ) panel.set_value("voltage_waveform", waveforms[0]) panel.set_value("thermocouple_waveform", waveforms[1]) diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py index 642747e..a8abd05 100644 --- a/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py @@ -39,11 +39,11 @@ ) -def click_start(): +def _click_start() -> None: panel.set_value("is_running", True) -def click_stop(): +def _click_stop() -> None: panel.set_value("is_running", False) @@ -51,9 +51,9 @@ def click_stop(): is_running = panel.get_value("is_running", False) if is_running: - st.button(r"⏹️ Stop", key="stop_button", on_click=click_stop) + st.button(r"⏹️ Stop", key="stop_button", on_click=_click_stop) elif not is_running and panel.get_value("daq_error", "") == "": - st.button(r"▶️ Run", key="run_button", on_click=click_start) + st.button(r"▶️ Run", key="run_button", on_click=_click_start) else: st.error( f"There was an error running the script. Fix the issue and re-run nidaqmx_continuous_analog_input.py \n\n {panel.get_value('daq_error', '')}" From 6877ff9d5c67cc4391da7ee9b7b1357556f27204 Mon Sep 17 00:00:00 2001 From: Mike Prosser Date: Wed, 1 Oct 2025 13:48:06 -0500 Subject: [PATCH 5/7] cleanup and improvements --- .../nidaqmx_analog_input_filtering.py | 202 ++++++++++-------- .../nidaqmx_analog_input_filtering_panel.py | 108 +++++----- .../nidaqmx_analog_output_voltage.py | 147 +++++++------ .../nidaqmx_analog_output_voltage_panel.py | 105 ++++----- .../nidaqmx_continuous_analog_input.py | 136 +++++++----- .../nidaqmx_continuous_analog_input_panel.py | 133 ++++++------ 6 files changed, 443 insertions(+), 388 deletions(-) diff --git a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py index 00e2307..ad3ef7a 100644 --- a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py +++ b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering.py @@ -17,6 +17,7 @@ Slope, StrainGageBridgeType, TerminalConfiguration, + UsageTypeAI, ) from nidaqmx.errors import DaqError # noqa: E402 @@ -31,7 +32,8 @@ available_channel_names = [] for dev in system.devices: for chan in dev.ai_physical_chans: - available_channel_names.append(chan.name) + if UsageTypeAI.VOLTAGE in chan.ai_meas_types: + available_channel_names.append(chan.name) panel.set_value("available_channel_names", available_channel_names) available_trigger_sources = [""] @@ -40,111 +42,121 @@ for term in dev.terminals: available_trigger_sources.append(term) panel.set_value("available_trigger_sources", available_trigger_sources) + try: - panel.set_value("daq_error", "") print(f"Panel URL: {panel.panel_url}") print(f"Waiting for the 'Run' button to be pressed...") print(f"(Press Ctrl + C to quit)") while True: while not panel.get_value("is_running", False): time.sleep(0.1) - # How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/ - with nidaqmx.Task() as task: - - chan_type = panel.get_value("chan_type", "1") - - if chan_type == "2": - chan = task.ai_channels.add_ai_current_chan( - panel.get_value("physical_channel", ""), - max_val=panel.get_value("max_value_current", 0.01), - min_val=panel.get_value("min_value_current", -0.01), - ext_shunt_resistor_val=panel.get_value("shunt_resistor_value", 249.0), - shunt_resistor_loc=panel.get_value( - "shunt_location", CurrentShuntResistorLocation.EXTERNAL - ), - units=panel.get_value("units", CurrentUnits.AMPS), - ) - elif chan_type == "3": - chan = task.ai_channels.add_ai_strain_gage_chan( - panel.get_value("physical_channel", ""), - nominal_gage_resistance=panel.get_value("gage_resistance", 350.0), - voltage_excit_source=ExcitationSource.EXTERNAL, # Only mode that works - max_val=panel.get_value("max_value_strain", 0.001), - min_val=panel.get_value("min_value_strain", -0.001), - poisson_ratio=panel.get_value("poisson_ratio", 0.3), - lead_wire_resistance=panel.get_value("wire_resistance", 0.0), - initial_bridge_voltage=panel.get_value("initial_voltage", 0.0), - gage_factor=panel.get_value("gage_factor", 2.0), - voltage_excit_val=panel.get_value("voltage_excitation_value", 0.0), - strain_config=panel.get_value( - "strain_configuration", StrainGageBridgeType.FULL_BRIDGE_I - ), - ) - else: - chan = task.ai_channels.add_ai_voltage_chan( - panel.get_value("physical_channel", ""), - terminal_config=panel.get_value( - "terminal_configuration", TerminalConfiguration.DEFAULT - ), - max_val=panel.get_value("max_value_voltage", 5.0), - min_val=panel.get_value("min_value_voltage", -5.0), - ) - task.timing.cfg_samp_clk_timing( - source=panel.get_value("source", ""), # "" - means Onboard Clock (default value) - rate=panel.get_value("rate", 1000.0), - sample_mode=AcquisitionType.CONTINUOUS, - samps_per_chan=panel.get_value("total_samples", 100), - ) - panel.set_value("sample_rate", task.timing.samp_clk_rate) - # Not all hardware supports all filter types. - # Refer to your device documentation for more information. - if panel.get_value("filter", "Filter") == "Filter": - chan.ai_filter_enable = True - chan.ai_filter_freq = panel.get_value("filter_freq", 0.0) - chan.ai_filter_response = panel.get_value("filter_response", FilterResponse.COMB) - chan.ai_filter_order = panel.get_value("filter_order", 1) - panel.set_value("actual_filter_freq", chan.ai_filter_freq) - panel.set_value("actual_filter_response", chan.ai_filter_response) - panel.set_value("actual_filter_order", chan.ai_filter_order) - else: - panel.set_value("actual_filter_freq", 0.0) - panel.set_value("actual_filter_response", FilterResponse.COMB) - panel.set_value("actual_filter_order", 0) - # Not all hardware supports all filter types. - # Refer to your device documentation for more information. - trigger_type = panel.get_value("trigger_type") - if trigger_type == "5": - task.triggers.start_trigger.cfg_anlg_edge_start_trig( - trigger_source=panel.get_value("analog_source", ""), - trigger_slope=panel.get_value("slope", Slope.FALLING), - trigger_level=panel.get_value("level", 0.0), - ) + print(f"Running...") + try: + # How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/ + panel.set_value("daq_error", "") + with nidaqmx.Task() as task: - if trigger_type == "2": - task.triggers.start_trigger.cfg_dig_edge_start_trig( - trigger_source=panel.get_value("digital_source", ""), - trigger_edge=panel.get_value("edge", Edge.FALLING), - ) - task.triggers.start_trigger.anlg_edge_hyst = hysteresis = panel.get_value( - "hysteresis", 0.0 + chan_type = panel.get_value("chan_type", "1") + + if chan_type == "2": + chan = task.ai_channels.add_ai_current_chan( + panel.get_value("physical_channel", ""), + max_val=panel.get_value("max_value_current", 0.01), + min_val=panel.get_value("min_value_current", -0.01), + ext_shunt_resistor_val=panel.get_value("shunt_resistor_value", 249.0), + shunt_resistor_loc=panel.get_value( + "shunt_location", CurrentShuntResistorLocation.EXTERNAL + ), + units=panel.get_value("units", CurrentUnits.AMPS), + ) + + elif chan_type == "3": + chan = task.ai_channels.add_ai_strain_gage_chan( + panel.get_value("physical_channel", ""), + nominal_gage_resistance=panel.get_value("gage_resistance", 350.0), + voltage_excit_source=ExcitationSource.EXTERNAL, # Only mode that works + max_val=panel.get_value("max_value_strain", 0.001), + min_val=panel.get_value("min_value_strain", -0.001), + poisson_ratio=panel.get_value("poisson_ratio", 0.3), + lead_wire_resistance=panel.get_value("wire_resistance", 0.0), + initial_bridge_voltage=panel.get_value("initial_voltage", 0.0), + gage_factor=panel.get_value("gage_factor", 2.0), + voltage_excit_val=panel.get_value("voltage_excitation_value", 0.0), + strain_config=panel.get_value( + "strain_configuration", StrainGageBridgeType.FULL_BRIDGE_I + ), + ) + else: + chan = task.ai_channels.add_ai_voltage_chan( + panel.get_value("physical_channel", ""), + terminal_config=panel.get_value( + "terminal_configuration", TerminalConfiguration.DEFAULT + ), + max_val=panel.get_value("max_value_voltage", 5.0), + min_val=panel.get_value("min_value_voltage", -5.0), + ) + task.timing.cfg_samp_clk_timing( + source=panel.get_value( + "source", "" + ), # "" - means Onboard Clock (default value) + rate=panel.get_value("rate", 1000.0), + sample_mode=AcquisitionType.CONTINUOUS, + samps_per_chan=panel.get_value("total_samples", 100), ) + panel.set_value("sample_rate", task.timing.samp_clk_rate) + # Not all hardware supports all filter types. + # Refer to your device documentation for more information. + if panel.get_value("filter", "Filter") == "Filter": + chan.ai_filter_enable = True + chan.ai_filter_freq = panel.get_value("filter_freq", 0.0) + chan.ai_filter_response = panel.get_value( + "filter_response", FilterResponse.COMB + ) + chan.ai_filter_order = panel.get_value("filter_order", 1) + panel.set_value("actual_filter_freq", chan.ai_filter_freq) + panel.set_value("actual_filter_response", chan.ai_filter_response) + panel.set_value("actual_filter_order", chan.ai_filter_order) + else: + panel.set_value("actual_filter_freq", 0.0) + panel.set_value("actual_filter_response", FilterResponse.COMB) + panel.set_value("actual_filter_order", 0) + # Not all hardware supports all filter types. + # Refer to your device documentation for more information. + trigger_type = panel.get_value("trigger_type") + if trigger_type == "5": + task.triggers.start_trigger.cfg_anlg_edge_start_trig( + trigger_source=panel.get_value("analog_source", ""), + trigger_slope=panel.get_value("slope", Slope.FALLING), + trigger_level=panel.get_value("level", 0.0), + ) + + if trigger_type == "2": + task.triggers.start_trigger.cfg_dig_edge_start_trig( + trigger_source=panel.get_value("digital_source", ""), + trigger_edge=panel.get_value("edge", Edge.FALLING), + ) + task.triggers.start_trigger.anlg_edge_hyst = hysteresis = panel.get_value( + "hysteresis", 0.0 + ) + + try: + task.start() + while panel.get_value("is_running", False): + waveform = task.read_waveform(number_of_samples_per_channel=100) + panel.set_value("waveform", waveform) + except KeyboardInterrupt: + pass + finally: + task.stop() + panel.set_value("is_running", False) + print(f"Stopped") - try: - task.start() - while panel.get_value("is_running", False): - waveform = task.read_waveform(number_of_samples_per_channel=100) - panel.set_value("waveform", waveform) - except KeyboardInterrupt: - pass - finally: - task.stop() - panel.set_value("is_running", False) - -except DaqError as e: - daq_error = str(e) - print(daq_error) - panel.set_value("daq_error", daq_error) + except DaqError as e: + daq_error = str(e) + panel.set_value("daq_error", daq_error) + panel.set_value("is_running", False) + print(f"ERRORED") except KeyboardInterrupt: pass diff --git a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py index 4c94bb3..b464cd3 100644 --- a/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py +++ b/examples/nidaqmx/nidaqmx_analog_input_filtering/nidaqmx_analog_input_filtering_panel.py @@ -40,14 +40,14 @@ def _click_stop() -> None: """