diff --git a/.github/workflows/check_nipanel.yml b/.github/workflows/check_nipanel.yml index 4cf7d36..4be028f 100644 --- a/.github/workflows/check_nipanel.yml +++ b/.github/workflows/check_nipanel.yml @@ -24,7 +24,7 @@ jobs: path: .venv key: nipanel-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock') }} - name: Install nipanel - run: poetry install -v + run: poetry install -v --with examples,docs - name: Lint run: poetry run ni-python-styleguide lint - name: Mypy static analysis (Linux) diff --git a/examples/nidaqmx/README.md b/examples/nidaqmx/README.md new file mode 100644 index 0000000..0969d0d --- /dev/null +++ b/examples/nidaqmx/README.md @@ -0,0 +1,20 @@ +Prerequisites +=============== +Requires a Physical or Simulated Device : https://github.com/ni/nidaqmx-python/blob/master/README.rst (Getting Started Section) + +## Sample + +This is a nipanel example that displays an interactive Streamlit app and updates continuous analog input examples. + +### Feature + +- Supports various data types + +### Required Software + +- Python 3.9 or later + +### Usage + +Run `poetry run examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py` + diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input.py b/examples/nidaqmx/nidaqmx_continuous_analog_input.py new file mode 100644 index 0000000..f3ab592 --- /dev/null +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input.py @@ -0,0 +1,30 @@ +"""Data acquisition script that continuously acquires analog input data.""" + +from pathlib import Path + +import nidaqmx +from nidaqmx.constants import AcquisitionType + +import nipanel + +panel_script_path = Path(__file__).with_name("nidaqmx_continuous_analog_input_panel.py") +panel = nipanel.create_panel(panel_script_path) + +with nidaqmx.Task() as task: + task.ai_channels.add_ai_voltage_chan("Dev1/ai0") + task.ai_channels.add_ai_thrmcpl_chan("Dev1/ai1") + task.timing.cfg_samp_clk_timing( + rate=1000.0, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=3000 + ) + panel.set_value("sample_rate", task._timing.samp_clk_rate) + task.start() + try: + print(f"\nPress Ctrl + C to stop") + while True: + data = task.read(number_of_samples_per_channel=1000) + panel.set_value("voltage_data", data[0]) + panel.set_value("thermocouple_data", data[1]) + except KeyboardInterrupt: + pass + finally: + task.stop() diff --git a/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py b/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py new file mode 100644 index 0000000..eb2c015 --- /dev/null +++ b/examples/nidaqmx/nidaqmx_continuous_analog_input_panel.py @@ -0,0 +1,101 @@ +"""Streamlit visualization script to display data acquired by nidaqmx_continuous_analog_input.py.""" + +import streamlit as st +from streamlit_echarts import st_echarts + +import nipanel + +panel = nipanel.get_panel_accessor() + +st.title("Analog Input - Voltage and Thermocouple in a Single Task") +voltage_tab, thermocouple_tab = st.tabs(["Voltage", "Thermocouple"]) + +st.markdown( + """ + + """, + unsafe_allow_html=True, +) + +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) + +st.header("Voltage & Thermocouple") +voltage_therm_graph = { + "tooltip": {"trigger": "axis"}, + "legend": {"data": ["Voltage (V)", "Temperature (C)"]}, + "xAxis": { + "type": "category", + "data": [x / sample_rate for x in range(len(voltage_data))], + "name": "Time", + "nameLocation": "center", + "nameGap": 40, + }, + "yAxis": { + "type": "value", + "name": "Measurement", + "nameRotate": 90, + "nameLocation": "center", + "nameGap": 40, + }, + "series": [ + { + "name": "voltage_amplitude", + "type": "line", + "data": voltage_data, + "emphasis": {"focus": "series"}, + "smooth": True, + "seriesLayoutBy": "row", + }, + { + "name": "thermocouple_amplitude", + "type": "line", + "data": thermocouple_data, + "color": "red", + "emphasis": {"focus": "series"}, + "smooth": True, + "seriesLayoutBy": "row", + }, + ], +} +st_echarts(options=voltage_therm_graph, height="400px") + +voltage_tab.header("Voltage") +with voltage_tab: + left_volt_tab, center_volt_tab, right_volt_tab = st.columns(3) + with left_volt_tab: + st.selectbox(options=["Dev1/ai0"], label="Physical Channels", disabled=True) + st.selectbox(options=["Off"], label="Logging Modes", disabled=False) + with center_volt_tab: + st.selectbox(options=["-5"], label="Min Value") + st.selectbox(options=["5"], label="Max Value") + st.selectbox(options=["1000"], label="Samples per Loops", disabled=False) + with right_volt_tab: + st.selectbox(options=["default"], label="Terminal Configurations") + st.selectbox(options=["OnboardClock"], label="Sample Clock Sources", disabled=False) + + +thermocouple_tab.header("Thermocouple") +with thermocouple_tab: + left, middle, right = st.columns(3) + with left: + st.selectbox(options=["Dev1/ai1"], label="Physical Channel", disabled=True) + st.selectbox(options=["0"], label="Min", disabled=False) + st.selectbox(options=["100"], label="Max", disabled=False) + st.selectbox(options=["Off"], label="Logging Mode", disabled=False) + + with middle: + st.selectbox(options=["Deg C"], label="Units", disabled=False) + st.selectbox(options=["J"], label="Thermocouple Type", disabled=False) + st.selectbox(options=["Constant Value"], label="CJC Source", disabled=False) + st.selectbox(options=["1000"], label="Samples per Loop", disabled=False) + with right: + st.selectbox(options=["25"], label="CJC Value", disabled=False) + st.selectbox(options=["OnboardClock"], label="Sample Clock Source", disabled=False) + st.selectbox(options=[" "], label="Actual Sample Rate", disabled=True) diff --git a/examples/simple_graph/simple_graph_panel.py b/examples/simple_graph/simple_graph_panel.py index f7c8a1a..6fde157 100644 --- a/examples/simple_graph/simple_graph_panel.py +++ b/examples/simple_graph/simple_graph_panel.py @@ -1,11 +1,10 @@ """A Streamlit visualization panel for the simple_graph.py example script.""" import streamlit as st -from streamlit_echarts import st_echarts # type: ignore +from streamlit_echarts import st_echarts import nipanel - panel = nipanel.get_panel_accessor() st.set_page_config(page_title="Simple Graph Example", page_icon="📈", layout="wide") diff --git a/poetry.lock b/poetry.lock index 12133c0..1d9f5bc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1340,6 +1340,25 @@ pycodestyle = [ ] toml = ">=0.10.1" +[[package]] +name = "nidaqmx" +version = "0.8.0" +description = "NI-DAQmx Python API" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "nidaqmx-0.8.0-py3-none-any.whl", hash = "sha256:7a5d5b3f78c125d31a8d00349f216f63bb8f6b887d0eca882001b729904d68bc"}, + {file = "nidaqmx-0.8.0.tar.gz", hash = "sha256:6c72ca4ec5da95e8603c1e6d5085d59bb3c1c2ea1f45bdfaa4acac40fe82ae0c"}, +] + +[package.dependencies] +deprecation = ">=2.1" +numpy = {version = ">=1.22", markers = "python_version >= \"3.8\" and python_version < \"4.0\""} + +[package.extras] +docs = ["Sphinx (>=4.3,<5.0)", "sphinx_rtd_theme (>=1.0,<2.0)"] +grpc = ["grpcio (>=1.49.0,<1.53)", "grpcio (>=1.49.0,<2.0)", "protobuf (>=4.21,<5.0)"] + [[package]] name = "nitypes" version = "0.1.0.dev2" @@ -3062,4 +3081,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0,!=3.9.7" -content-hash = "296f5d5ca9c4ab5d392a67d43884fe8a9f63ea0e9437bce8a572793d62827603" +content-hash = "7572c2b90e907a470ea74af8392c186b0a8c7c336f8b43022918a0d7b69b3207" diff --git a/pyproject.toml b/pyproject.toml index ae2db61..b5e828b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ protobuf = {version=">=4.21"} ni-measurement-plugin-sdk = {version=">=2.3"} typing-extensions = ">=4.13.2" streamlit = ">=1.24" +streamlit-echarts = ">=0.4.0" nitypes = {version=">=0.1.0dev2", allow-prereleases=true} debugpy = ">=1.8.1" @@ -57,6 +58,7 @@ optional = true [tool.poetry.group.examples.dependencies] streamlit-echarts = ">=0.4.0" +nidaqmx = ">=0.8.0" [build-system] requires = ["poetry-core>=1.8.0"] @@ -76,9 +78,11 @@ strict = true [[tool.mypy.overrides]] module = [ - # https://github.com/ni/hightime/issues/4 - Add type annotations "hightime.*", "grpc.framework.foundation.*", + "streamlit_echarts.*", + # https://github.com/ni/nidaqmx-python/issues/209 - Support type annotations + "nidaqmx.*", ] ignore_missing_imports = true @@ -93,4 +97,4 @@ testpaths = ["src/nipanel", "tests"] [tool.pyright] include = ["examples/", "src/", "tests/"] -exclude = ["src/ni/protobuf/types/", "src/ni/pythonpanel/v1/"] \ No newline at end of file +exclude = ["src/ni/protobuf/types/", "src/ni/pythonpanel/v1/","examples/nidaqmx/nidaqmx_continuous_analog_input.py"] \ No newline at end of file