From b7abc39cf611a83a9a24e62ce33d434313889559 Mon Sep 17 00:00:00 2001 From: Dilmi Wickramanayake Date: Thu, 19 Jun 2025 06:53:29 -0500 Subject: [PATCH 1/3] Analog Input - Voltage and Thermocouple Single Task --- .../nidaqmx_continuous_analog_input.py | 38 ++++ .../nidaqmx_continuous_analog_input_panel.py | 168 ++++++++++++++++ examples/sample/sample.py | 2 +- poetry.lock | 187 +++++++++++++++++- pyproject.toml | 1 + 5 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py create mode 100644 examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py diff --git a/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py b/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py new file mode 100644 index 0000000..246e053 --- /dev/null +++ b/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py @@ -0,0 +1,38 @@ +"""This example demonstrates how to open/update a Streamlit application using nipanel package.""" + +import nipanel +import nidaqmx +import pathlib +import time + +script_path = pathlib.Path(__file__) +panel_script_path = str(script_path.with_name("nidaqmx_continuous_analog_input_panel.py")) + +panel = nipanel.StreamlitPanel( + panel_id="nidaqmx_continuous_analog_input_panel", + streamlit_script_path=panel_script_path, +) + +data_arr = [] +with nidaqmx.Task() as task: + task.ai_channels.add_ai_voltage_chan("Mod1/ai2") + task.ai_channels.add_ai_thrmcpl_chan("Mod1/ai3") + try: + total_read = 0 + while True: + data = task.read(number_of_samples_per_channel=3) + read = len(data) + total_read += read + + data_arr.append(data) + time.sleep(1) + panel.set_value("amplitude",data_arr[-1][-1]) + panel.set_value("Volts",data_arr[-1][0]) + except KeyboardInterrupt: + pass + finally: + task.stop() + print(f"\nAcquired {total_read} total samples.") + + + diff --git a/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py b/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py new file mode 100644 index 0000000..b83f09b --- /dev/null +++ b/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py @@ -0,0 +1,168 @@ +"""Streamlit application script for displaying values using nipanel package.""" +import nipanel +import streamlit as st +import streamlit.components.v1 as components +from streamlit_echarts import st_echarts + +panel = nipanel.StreamlitPanelValueAccessor(panel_id="nidaqmx_continuous_analog_input_panel") + +add_refresh_component = components.declare_component( + "panelRefreshComponent", + url=f"http://localhost:42001/panels/refresh/{panel.panel_id}",) +add_refresh_component() + + +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 +) + + +list_of_therm_amp = panel.get_value("amplitude") +list_of_voltage_amp = panel.get_value("Volts") + +if "therm_history" not in st.session_state: + st.session_state.therm_history = [] +if "volts_history" not in st.session_state: + st.session_state.volts_history = [] + +for therm_amp in list_of_therm_amp: + st.session_state.therm_history.append(therm_amp) +for voltage_amp in list_of_voltage_amp: + st.session_state.volts_history.append(voltage_amp) + +therm_amp_graph = { + "tooltip": {"trigger": "axis"}, + "legend": {"data": ["thermocouple_amplitude"]}, + "xAxis": { + "type": "category", + "data":list(range(len(st.session_state.therm_history))), + "name": "Time" + }, + "yAxis": { + "type": "value", + "name": "Thermocouple Amplitude" + }, + "series": [ + { + "name": "thermocouple_amplitude", + "type": "line", + "data": st.session_state.therm_history, + "color": "red" + }, + + + ], +} +st_echarts(options=therm_amp_graph, height="400px") + +voltage_amp_graph = { + "tooltip": {"trigger": "axis"}, + "legend": {"data": ["voltage_amplitude"]}, + "xAxis": { + "type": "category", + "data": list(range(len(st.session_state.volts_history))), + "name": "Time" + }, + "yAxis": { + "type": "value", + "name": "Voltage Amplitude" + }, + "series": [ + { + "name": "voltage_amplitude", + "type": "line", + "data": st.session_state.volts_history, + }, + + ], +} +st_echarts(options=voltage_amp_graph, height="400px") + +st.header("Voltage & Thermocouple") +voltage_therm_graph = { + "tooltip": {"trigger": "axis"}, + "legend": {"data": ["voltage_amplitude", "thermocouple_amplitude"]}, + "xAxis": { + "type": "category", + "data": list(range(len(st.session_state.volts_history))), + "name": "Time" + }, + "yAxis": { + "type": "value", + "name": "Voltage and Thermocouple Amplitude" + }, + "series": [ + { + "name": "voltage_amplitude", + "type": "line", + "data": st.session_state.volts_history, + "emphasis": {"focus":"series"}, + "smooth": True, + "seriesLayoutBy": "row", + }, + { + "name": "thermocouple_amplitude", + "type": "line", + "data": st.session_state.therm_history, + "color": "red", + "emphasis": {"focus":"series"}, + "smooth": True, + "seriesLayoutBy": "row", + }, + ], +} +st_echarts(options=voltage_therm_graph, height="400px") + +with voltage_tab: + left_volt_tab, center_volt_tab, right_volt_tab = st.columns(3) + with left_volt_tab: + st.selectbox(options=["Mod1/ai2"], 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=["Mod1/ai3"], 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/sample/sample.py b/examples/sample/sample.py index 09977e4..188c1f2 100644 --- a/examples/sample/sample.py +++ b/examples/sample/sample.py @@ -12,7 +12,7 @@ streamlit_script_path=panel_script_path, ) panel.set_value("sample_string", "Hello, World!") -panel.set_value("sample_int", 42) +panel.set_value("sample_int", 6) panel.set_value("sample_float", 3.14) panel.set_value("sample_bool", True) panel.set_value("float_values", [1.1, 2.2, 3.3]) diff --git a/poetry.lock b/poetry.lock index 2adc8d0..deb5730 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1700,6 +1700,23 @@ files = [ dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] +[[package]] +name = "prettytable" +version = "3.16.0" +description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" +optional = false +python-versions = ">=3.9" +files = [ + {file = "prettytable-3.16.0-py3-none-any.whl", hash = "sha256:b5eccfabb82222f5aa46b798ff02a8452cf530a352c31bddfa29be41242863aa"}, + {file = "prettytable-3.16.0.tar.gz", hash = "sha256:3c64b31719d961bf69c9a7e03d0c1e477320906a98da63952bc6698d6164ff57"}, +] + +[package.dependencies] +wcwidth = "*" + +[package.extras] +tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] + [[package]] name = "protobuf" version = "4.25.8" @@ -1865,6 +1882,28 @@ snowballstemmer = ">=2.2.0" [package.extras] toml = ["tomli (>=1.2.3)"] +[[package]] +name = "pyecharts" +version = "2.0.8" +description = "Python options, make charting easier" +optional = false +python-versions = "*" +files = [ + {file = "pyecharts-2.0.8-py3-none-any.whl", hash = "sha256:8b711ba139f39f89bc1b2a869d7adda89dc74c910d158a1f9063109fe66bc985"}, + {file = "pyecharts-2.0.8.tar.gz", hash = "sha256:908dbd939862dd3c76bb53697bdb41d3cdd0b5ba48ca69a76a6085d0aa27dbdf"}, +] + +[package.dependencies] +jinja2 = "*" +prettytable = "*" +simplejson = "*" + +[package.extras] +images = ["PIL"] +phantomjs = ["snapshot-phantomjs"] +pyppeteer = ["snapshot-pyppeteer"] +selenium = ["snapshot-selenium"] + [[package]] name = "pyflakes" version = "2.5.0" @@ -2299,6 +2338,125 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +[[package]] +name = "simplejson" +version = "3.20.1" +description = "Simple, fast, extensible JSON encoder/decoder for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.5" +files = [ + {file = "simplejson-3.20.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f5272b5866b259fe6c33c4a8c5073bf8b359c3c97b70c298a2f09a69b52c7c41"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5c0de368f3052a59a1acf21f8b2dd28686a9e4eba2da7efae7ed9554cb31e7bc"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0821871404a537fd0e22eba240c74c0467c28af6cc435903eca394cfc74a0497"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:c939a1e576bded47d7d03aa2afc2ae90b928b2cf1d9dc2070ceec51fd463f430"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:3c4f0a61cdc05550782ca4a2cdb311ea196c2e6be6b24a09bf71360ca8c3ca9b"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6c21f5c026ca633cfffcb6bc1fac2e99f65cb2b24657d3bef21aed9916cc3bbf"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:8d23b7f8d6b72319d6d55a0261089ff621ce87e54731c2d3de6a9bf7be5c028c"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:cda5c32a98f392909088111ecec23f2b0d39346ceae1a0fea23ab2d1f84ec21d"}, + {file = "simplejson-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e580aa65d5f6c3bf41b9b4afe74be5d5ddba9576701c107c772d936ea2b5043a"}, + {file = "simplejson-3.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a586ce4f78cec11f22fe55c5bee0f067e803aab9bad3441afe2181693b5ebb5"}, + {file = "simplejson-3.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74a1608f9e6e8c27a4008d70a54270868306d80ed48c9df7872f9f4b8ac87808"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03db8cb64154189a92a7786209f24e391644f3a3fa335658be2df2af1960b8d8"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eea7e2b7d858f6fdfbf0fe3cb846d6bd8a45446865bc09960e51f3d473c2271b"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e66712b17d8425bb7ff8968d4c7c7fd5a2dd7bd63728b28356223c000dd2f91f"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2cc4f6486f9f515b62f5831ff1888886619b84fc837de68f26d919ba7bbdcbc"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a3c2df555ee4016148fa192e2b9cd9e60bc1d40769366134882685e90aee2a1e"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78520f04b7548a5e476b5396c0847e066f1e0a4c0c5e920da1ad65e95f410b11"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f4bd49ecde87b0fe9f55cc971449a32832bca9910821f7072bbfae1155eaa007"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7eaae2b88eb5da53caaffdfa50e2e12022553949b88c0df4f9a9663609373f72"}, + {file = "simplejson-3.20.1-cp310-cp310-win32.whl", hash = "sha256:e836fb88902799eac8debc2b642300748f4860a197fa3d9ea502112b6bb8e142"}, + {file = "simplejson-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a19b552b212fc3b5b96fc5ce92333d4a9ac0a800803e1f17ebb16dac4be5"}, + {file = "simplejson-3.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:325b8c107253d3217e89d7b50c71015b5b31e2433e6c5bf38967b2f80630a8ca"}, + {file = "simplejson-3.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88a7baa8211089b9e58d78fbc1b0b322103f3f3d459ff16f03a36cece0d0fcf0"}, + {file = "simplejson-3.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:299b1007b8101d50d95bc0db1bf5c38dc372e85b504cf77f596462083ee77e3f"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ec618ed65caab48e81e3ed29586236a8e57daef792f1f3bb59504a7e98cd10"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2cdead1d3197f0ff43373cf4730213420523ba48697743e135e26f3d179f38"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3466d2839fdc83e1af42e07b90bc8ff361c4e8796cd66722a40ba14e458faddd"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d492ed8e92f3a9f9be829205f44b1d0a89af6582f0cf43e0d129fa477b93fe0c"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f924b485537b640dc69434565463fd6fc0c68c65a8c6e01a823dd26c9983cf79"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e8eacf6a3491bf76ea91a8d46726368a6be0eb94993f60b8583550baae9439e"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d04bf90b4cea7c22d8b19091633908f14a096caa301b24c2f3d85b5068fb8"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:69dd28d4ce38390ea4aaf212902712c0fd1093dc4c1ff67e09687c3c3e15a749"}, + {file = "simplejson-3.20.1-cp311-cp311-win32.whl", hash = "sha256:dfe7a9da5fd2a3499436cd350f31539e0a6ded5da6b5b3d422df016444d65e43"}, + {file = "simplejson-3.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:896a6c04d7861d507d800da7642479c3547060bf97419d9ef73d98ced8258766"}, + {file = "simplejson-3.20.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f31c4a3a7ab18467ee73a27f3e59158255d1520f3aad74315edde7a940f1be23"}, + {file = "simplejson-3.20.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884e6183d16b725e113b83a6fc0230152ab6627d4d36cb05c89c2c5bccfa7bc6"}, + {file = "simplejson-3.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03d7a426e416fe0d3337115f04164cd9427eb4256e843a6b8751cacf70abc832"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:000602141d0bddfcff60ea6a6e97d5e10c9db6b17fd2d6c66199fa481b6214bb"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af8377a8af78226e82e3a4349efdde59ffa421ae88be67e18cef915e4023a595"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c7de4c88ab2fbcb8781a3b982ef883696736134e20b1210bca43fb42ff1acf"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:455a882ff3f97d810709f7b620007d4e0aca8da71d06fc5c18ba11daf1c4df49"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fc0f523ce923e7f38eb67804bc80e0a028c76d7868500aa3f59225574b5d0453"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76461ec929282dde4a08061071a47281ad939d0202dc4e63cdd135844e162fbc"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19c2da8c043607bde4d4ef3a6b633e668a7d2e3d56f40a476a74c5ea71949f"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2578bedaedf6294415197b267d4ef678fea336dd78ee2a6d2f4b028e9d07be3"}, + {file = "simplejson-3.20.1-cp312-cp312-win32.whl", hash = "sha256:339f407373325a36b7fd744b688ba5bae0666b5d340ec6d98aebc3014bf3d8ea"}, + {file = "simplejson-3.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:627d4486a1ea7edf1f66bb044ace1ce6b4c1698acd1b05353c97ba4864ea2e17"}, + {file = "simplejson-3.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:71e849e7ceb2178344998cbe5ade101f1b329460243c79c27fbfc51c0447a7c3"}, + {file = "simplejson-3.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b63fdbab29dc3868d6f009a59797cefaba315fd43cd32ddd998ee1da28e50e29"}, + {file = "simplejson-3.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1190f9a3ce644fd50ec277ac4a98c0517f532cfebdcc4bd975c0979a9f05e1fb"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1336ba7bcb722ad487cd265701ff0583c0bb6de638364ca947bb84ecc0015d1"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e975aac6a5acd8b510eba58d5591e10a03e3d16c1cf8a8624ca177491f7230f0"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a6dd11ee282937ad749da6f3b8d87952ad585b26e5edfa10da3ae2536c73078"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab980fcc446ab87ea0879edad41a5c28f2d86020014eb035cf5161e8de4474c6"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f5aee2a4cb6b146bd17333ac623610f069f34e8f31d2f4f0c1a2186e50c594f0"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:652d8eecbb9a3b6461b21ec7cf11fd0acbab144e45e600c817ecf18e4580b99e"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c09948f1a486a89251ee3a67c9f8c969b379f6ffff1a6064b41fea3bce0a112"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cbbd7b215ad4fc6f058b5dd4c26ee5c59f72e031dfda3ac183d7968a99e4ca3a"}, + {file = "simplejson-3.20.1-cp313-cp313-win32.whl", hash = "sha256:ae81e482476eaa088ef9d0120ae5345de924f23962c0c1e20abbdff597631f87"}, + {file = "simplejson-3.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:1b9fd15853b90aec3b1739f4471efbf1ac05066a2c7041bf8db821bb73cd2ddc"}, + {file = "simplejson-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c7edf279c1376f28bf41e916c015a2a08896597869d57d621f55b6a30c7e1e6d"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9202b9de38f12e99a40addd1a8d508a13c77f46d87ab1f9095f154667f4fe81"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:391345b4157cc4e120027e013bd35c45e2c191e2bf48b8913af488cdc3b9243c"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6fdcc9debb711ddd2ad6d69f9386a3d9e8e253234bbb30513e0a7caa9510c51"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9daf8cdc7ee8a9e9f7a3b313ba0a003391857e90d0e82fbcd4d614aa05cb7c3b"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:c02f4868a3a46ffe284a51a88d134dc96feff6079a7115164885331a1ba8ed9f"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:3d7310172d5340febd258cb147f46aae30ad57c445f4d7e1ae8461c10aaf43b0"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:4762e05577955312a4c6802f58dd02e040cc79ae59cda510aa1564d84449c102"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:8bb98fdf318c05aefd08a92583bd6ee148e93c6756fb1befb7b2d5f27824be78"}, + {file = "simplejson-3.20.1-cp36-cp36m-win32.whl", hash = "sha256:9a74e70818818981294b8e6956ce3496c5e1bd4726ac864fae473197671f7b85"}, + {file = "simplejson-3.20.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e041add470e8f8535cc05509485eb7205729a84441f03b25cde80ad48823792e"}, + {file = "simplejson-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7e9d73f46119240e4f4f07868241749d67d09873f40cb968d639aa9ccc488b86"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae6e637dc24f8fee332ed23dd070e81394138e42cd4fd9d0923e5045ba122e27"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efd3bc6c6b17e3d4620eb6be5196f0d1c08b6ce7c3101fa8e292b79e0908944b"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87fc623d457173a0213bc9ca4e346b83c9d443f63ed5cca847fb0cacea3cfc95"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec6a1e0a7aff76f0e008bebfa950188b9c50b58c1885d898145f48fc8e189a56"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:9c079606f461a6e950099167e21e13985147c8a24be8eea66c9ad68f73fad744"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:9faceb68fba27ef17eda306e4cd97a7b4b14fdadca5fbb15790ba8b26ebeec0c"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:7ceed598e4bacbf5133fe7a418f7991bb2df0683f3ac11fbf9e36a2bc7aa4b85"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ede69c765e9901861ad7c6139023b7b7d5807c48a2539d817b4ab40018002d5f"}, + {file = "simplejson-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:d8853c269a4c5146ddca4aa7c70e631795e9d11239d5fedb1c6bbc91ffdebcac"}, + {file = "simplejson-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ed6a17fd397f0e2b3ad668fc9e19253ed2e3875ad9086bd7f795c29a3223f4a1"}, + {file = "simplejson-3.20.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7551682b60bba3a9e2780742e101cf0a64250e76de7d09b1c4b0c8a7c7cc6834"}, + {file = "simplejson-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd9577ec1c8c3a43040e3787711e4c257c70035b7551a21854b5dec88dad09e1"}, + {file = "simplejson-3.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8e197e4cf6d42c2c57e7c52cd7c1e7b3e37c5911df1314fb393320131e2101"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bd09c8c75666e7f62a33d2f1fb57f81da1fcbb19a9fe7d7910b5756e1dd6048"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bd6bfe5678d73fbd5328eea6a35216503796428fc47f1237432522febaf3a0c"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b75d448fd0ceb2e7c90e72bb82c41f8462550d48529980bc0bab1d2495bfbb"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7e15b716d09f318c8cda3e20f82fae81684ce3d3acd1d7770fa3007df1769de"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3e7963197d958fcf9e98b212b80977d56c022384621ff463d98afc3b6b1ce7e8"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:2e671dd62051129185d3a9a92c60101f56cbc174854a1a3dfb69114ebd9e1699"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e25b2a0c396f3b84fb89573d07b0e1846ed563eb364f2ea8230ca92b8a8cb786"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:489c3a43116082bad56795215786313832ba3991cca1f55838e52a553f451ab6"}, + {file = "simplejson-3.20.1-cp38-cp38-win32.whl", hash = "sha256:4a92e948bad8df7fa900ba2ba0667a98303f3db206cbaac574935c332838208e"}, + {file = "simplejson-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:49d059b8363327eee3c94799dd96782314b2dbd7bcc293b4ad48db69d6f4d362"}, + {file = "simplejson-3.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a8011f1dd1d676befcd4d675ebdbfdbbefd3bf350052b956ba8c699fca7d8cef"}, + {file = "simplejson-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e91703a4c5fec53e36875ae426ad785f4120bd1d93b65bed4752eeccd1789e0c"}, + {file = "simplejson-3.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e39eaa57c7757daa25bcd21f976c46be443b73dd6c3da47fe5ce7b7048ccefe2"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceab2ce2acdc7fbaa433a93006758db6ba9a659e80c4faa13b80b9d2318e9b17"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d4f320c33277a5b715db5bf5b10dae10c19076bd6d66c2843e04bd12d1f1ea5"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6436c48e64378fa844d8c9e58a5ed0352bbcfd4028369a9b46679b7ab79d2d"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e18345c8dda5d699be8166b61f9d80aaee4545b709f1363f60813dc032dac53"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:90b573693d1526bed576f6817e2a492eaaef68f088b57d7a9e83d122bbb49e51"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:272cc767826e924a6bd369ea3dbf18e166ded29059c7a4d64d21a9a22424b5b5"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:51b41f284d603c4380732d7d619f8b34bd04bc4aa0ed0ed5f4ffd0539b14da44"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6e6697a3067d281f01de0fe96fc7cba4ea870d96d7deb7bfcf85186d74456503"}, + {file = "simplejson-3.20.1-cp39-cp39-win32.whl", hash = "sha256:6dd3a1d5aca87bf947f3339b0f8e8e329f1badf548bdbff37fac63c17936da8e"}, + {file = "simplejson-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:463f1fca8fbf23d088e5850fdd0dd4d5faea8900a9f9680270bd98fd649814ca"}, + {file = "simplejson-3.20.1-py3-none-any.whl", hash = "sha256:8a6c1bbac39fa4a79f83cbf1df6ccd8ff7069582a9fd8db1e52cea073bc2c697"}, + {file = "simplejson-3.20.1.tar.gz", hash = "sha256:e64139b4ec4f1f24c142ff7dcafe55a22b811a74d86d66560c8815687143037d"}, +] + [[package]] name = "six" version = "1.17.0" @@ -2581,6 +2739,22 @@ watchdog = {version = ">=2.1.5,<7", markers = "platform_system != \"Darwin\""} [package.extras] snowflake = ["snowflake-connector-python (>=3.3.0)", "snowflake-snowpark-python[modin] (>=1.17.0)"] +[[package]] +name = "streamlit-echarts" +version = "0.4.0" +description = "Echarts custom component for Streamlit" +optional = false +python-versions = ">=3.6" +files = [ + {file = "streamlit-echarts-0.4.0.tar.gz", hash = "sha256:33cc5329b99ddce8b64ce6c4607733e02db575c379af6394a8c78ae5df14934d"}, + {file = "streamlit_echarts-0.4.0-py3-none-any.whl", hash = "sha256:aa86679da0e7680ee43b7a6def31439273a686a8d71de522c55655047e80ec9b"}, +] + +[package.dependencies] +pyecharts = ">=1.9" +simplejson = ">=3.0" +streamlit = ">=0.63" + [[package]] name = "tenacity" version = "9.1.2" @@ -2783,7 +2957,18 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0,!=3.9.7" -content-hash = "2405bfc23e77ee98c4fe58845fa73632b884c4bf6acded29ece6c9380a168675" +content-hash = "7239a2757fe3a06c6ddafa7f51763d7ee77976a7d5f96b05ea1f9fbfa631c801" diff --git a/pyproject.toml b/pyproject.toml index 11ecccc..0b8cfc7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ ni-measurement-plugin-sdk = {version=">=2.3"} typing-extensions = ">=4.13.2" streamlit = ">=1.24" nitypes = {version=">=0.1.0dev1", allow-prereleases=true} +streamlit-echarts = "^0.4.0" [tool.poetry.group.dev.dependencies] types-grpcio = ">=1.0" From 871f6e192778ddeb9bd8ee9d35b70b766a2ab609 Mon Sep 17 00:00:00 2001 From: Dilmi Wickramanayake Date: Tue, 24 Jun 2025 14:51:29 -0500 Subject: [PATCH 2/3] add niscope Signed-off-by: Dilmi Wickramanayake --- examples/nidaqmx/niscope_example.py | 194 ++++++++++++++++++++++++++++ examples/nidaqmx/niscope_panel.py | 39 ++++++ pyproject.toml | 7 + 3 files changed, 240 insertions(+) create mode 100644 examples/nidaqmx/niscope_example.py create mode 100644 examples/nidaqmx/niscope_panel.py diff --git a/examples/nidaqmx/niscope_example.py b/examples/nidaqmx/niscope_example.py new file mode 100644 index 0000000..2f88b80 --- /dev/null +++ b/examples/nidaqmx/niscope_example.py @@ -0,0 +1,194 @@ +import argparse +import niscope +import numpy +import pprint +import sys +import nipanel +from pathlib import Path +import time +pp = pprint.PrettyPrinter(indent=4, width=80) +panel_script_path = Path(__file__).with_name("niscope_panel.py") +panel = nipanel.create_panel(panel_script_path) + +# def example( +# resource_name, +# options, +# +# ): +# + +def example(resource_name, channels, options, length, total_acquisition_time_in_seconds, voltage, sample_rate_in_hz, samples_per_fetch,): + # fetch_into() allows you to preallocate and reuse the destination of the fetched waveforms, which can result in better performance at the expense of the usability of fetch(). + channels = [ch.strip() for ch in channels.split(",")] + num_channels = len(channels) + num_records = 1000 + total_num_wfms = num_channels * num_records + # preallocate a single array for all samples in all waveforms + # Supported array types are: numpy.float64, numpy.int8, numpy.int16, numpy.int32 + # int8, int16, int32 are for fetching unscaled data, which is the fastest way to fetch. + # Gain and Offset are stored in the returned WaveformInfo objects and can be applied to the data by the user later. + + wfm = numpy.ndarray(length * total_num_wfms, dtype=numpy.float64) + with niscope.Session(resource_name=resource_name, options=options) as session: + session.configure_vertical(range=2, coupling=niscope.VerticalCoupling.AC) + session.configure_horizontal_timing(min_sample_rate=100000000, min_num_pts=length, ref_position=50.0, num_records=num_records, enforce_realtime=True) + total_samples = int(total_acquisition_time_in_seconds * sample_rate_in_hz) + with session.initiate(): + waveforms = session.channels[channels].fetch_into(waveform=wfm, num_records=num_records) + for i in range(len(waveforms)): + time.sleep(0.2) + panel.set_value("samples", total_samples) + panel.set_value("Waveform", waveforms[i].samples.tolist()) + + +def _main(argsv): + parser = argparse.ArgumentParser(description='Fetches data directly into a preallocated numpy array.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-n', '--resource-name', default='Dev2', help='Resource name of an NI digitizer.') + parser.add_argument('-c', '--channels', default='0', help='Channel(s) to use') + parser.add_argument('-l', '--length', default=100, type=int, help='Measure record length') + parser.add_argument('-v', '--voltage', default=1.0, type=float, help='Voltage range (V)') + parser.add_argument('-op', '--option-string', default='', type=str, help='Option string') + parser.add_argument("-t", "--time", default=10, type=int, help="Time to sample (s)") + parser.add_argument("-r", "--sample-rate", default=1000.0, type=float, help="Sample Rate (Hz)") + parser.add_argument( + "-s", "--samples-per-fetch", default=100, type=int, help="Samples per fetch" + ) + args = parser.parse_args(argsv) + example(args.resource_name, args.channels, args.option_string, args.length, args.voltage, args.time, + args.sample_rate, + args.samples_per_fetch) + + + +def main(): + _main(sys.argv[1:]) + + +def test_example(): + options = {'simulate': True, 'driver_setup': {'Model': '5124', 'BoardType': 'PXI', }, } + example('Dev2', '0, 1', options, 100, 1.0) + + +def test_main(): + cmd_line = ['--option-string', 'Simulate=1, DriverSetup=Model:5164; BoardType:PXIe', ] + _main(cmd_line) + + +if __name__ == '__main__': + main() + +# # We use fetch_into which allows us to allocate a single buffer per channel and "fetch into" it a section at a time without having to +# # reconstruct the waveform once we are done +# def example( +# resource_name, +# options, +# total_acquisition_time_in_seconds, +# voltage, +# sample_rate_in_hz, +# samples_per_fetch, +# ): +# total_samples = int(total_acquisition_time_in_seconds * sample_rate_in_hz) +# # 1. Opening session +# with niscope.Session(resource_name=resource_name, options=options) as session: +# # We will acquire on all channels of the device +# channel_list = [ +# c for c in range(session.channel_count) +# ] # Need an actual list and not a range + +# # 2. Creating numpy arrays +# waveforms = [np.ndarray(total_samples, dtype=np.float64) for c in channel_list] +# offset = session._fetch_offset + +# # 3. Configuring +# session.configure_horizontal_timing( +# min_sample_rate=sample_rate_in_hz, +# min_num_pts=1, +# ref_position=0.0, +# num_records=1, +# enforce_realtime=True, +# ) +# session.channels[channel_list].configure_vertical( +# voltage, coupling=niscope.VerticalCoupling.DC, enabled=True +# ) +# # Configure software trigger, but never send the trigger. +# # This starts an infinite acquisition, until you call session.abort() or session.close() +# session.configure_trigger_software() +# current_pos = 0 + +# # 4. initiating +# with session.initiate(): +# while current_pos < total_samples: +# # We fetch each channel at a time so we don't have to de-interleave afterwards +# # We do not keep the wfm_info returned from fetch_into +# for channel, waveform in zip(channel_list, waveforms): +# data = waveform.tolist() +# panel.set_value("total", current_pos) +# time.sleep(0.2) +# panel.set_value("Waveform", data) +# panel.set_value("samples", total_samples) +# # session.channels[channel].fetch(num_samples=1000) +# # 5. fetching - we return the slice of the waveform array that we want to "fetch into" +# session.channels[channel].fetch_into( +# waveform[current_pos : current_pos + samples_per_fetch], +# relative_to=niscope.FetchRelativeTo.READ_POINTER, +# offset=0, +# record_number=0, +# num_records=1, +# timeout=hightime.timedelta(seconds=5.0), +# ) +# current_pos += samples_per_fetch + + +# def _main(argsv): +# parser = argparse.ArgumentParser( +# description="Fetch more samples than will fit in memory.", +# formatter_class=argparse.ArgumentDefaultsHelpFormatter, +# ) +# parser.add_argument( +# "-n", "--resource-name", default="Dev2", help="Resource name of an NI digitizer." +# ) +# parser.add_argument("-t", "--time", default=10, type=int, help="Time to sample (s)") +# parser.add_argument("-v", "--voltage", default=1.0, type=float, help="Voltage range (V)") +# parser.add_argument("-op", "--option-string", default="", type=str, help="Option string") +# parser.add_argument("-r", "--sample-rate", default=1000.0, type=float, help="Sample Rate (Hz)") +# parser.add_argument( +# "-s", "--samples-per-fetch", default=100, type=int, help="Samples per fetch" +# ) +# args = parser.parse_args(argsv) +# example( +# args.resource_name, +# args.option_string, +# args.time, +# args.voltage, +# args.sample_rate, +# args.samples_per_fetch, +# ) + + +# def main(): +# _main(sys.argv[1:]) + + +# def test_example(): +# options = { +# "simulate": True, +# "driver_setup": { +# "Model": "5124", +# "BoardType": "PXI", +# }, +# } +# example("Dev2", options, 10, 1.0, 1000.0, 100) + + +# def test_main(): +# cmd_line = [ +# "--option-string", +# "Simulate=1, DriverSetup=Model:5124; BoardType:PXI", +# ] +# _main(cmd_line) + + +# if __name__ == "__main__": +# main() + + diff --git a/examples/nidaqmx/niscope_panel.py b/examples/nidaqmx/niscope_panel.py new file mode 100644 index 0000000..7fae255 --- /dev/null +++ b/examples/nidaqmx/niscope_panel.py @@ -0,0 +1,39 @@ +import streamlit as st +from streamlit_echarts import st_echarts + +import nipanel + +panel = nipanel.get_panel_accessor() + +waveform = panel.get_value("Waveform") +samples = panel.get_value("samples") +graph = { + "tooltip": {"trigger": "axis"}, + "legend": {"data": ["Amplitude (V)"]}, + "xAxis": { + "type": "category", + "data": samples, + "name": "Samples", + "nameLocation": "center", + "nameGap": 40, + }, + "yAxis": { + "type": "value", + "name": "Amplitude(V)", + "nameRotate": 90, + "nameLocation": "center", + "nameGap": 40, + }, + "series": [ + { + "name": "niscope data", + "type": "line", + "data": waveform, + "emphasis": {"focus": "series"}, + "smooth": True, + "seriesLayoutBy": "row", + }, + + ], +} +st_echarts(options=graph, height="400px") \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 0b8cfc7..15808cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,13 @@ sphinx-autoapi = ">=1.8.4" m2r2 = ">=0.3.2" toml = ">=0.10.2" +[tool.poetry.group.examples] +optional = true + +[tool.poetry.group.examples.dependencies] +streamlit-echarts = ">=0.4.0" +nidaqmx = ">=0.8.0" + [build-system] requires = ["poetry-core>=1.8.0"] build-backend = "poetry.core.masonry.api" From 336d7b5af4575a41bd92958151e04214b8f6a43a Mon Sep 17 00:00:00 2001 From: Dilmi Wickramanayake Date: Wed, 25 Jun 2025 14:22:35 -0500 Subject: [PATCH 3/3] Rename Signed-off-by: Dilmi Wickramanayake --- examples/nidaqmx/niscope_example.py | 194 ------------------ .../nidaqmx_continuous_analog_input.py | 38 ---- .../nidaqmx_continuous_analog_input_panel.py | 168 --------------- examples/niscope/niscope_example.py | 95 +++++++++ .../{nidaqmx => niscope}/niscope_panel.py | 0 5 files changed, 95 insertions(+), 400 deletions(-) delete mode 100644 examples/nidaqmx/niscope_example.py delete mode 100644 examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py delete mode 100644 examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py create mode 100644 examples/niscope/niscope_example.py rename examples/{nidaqmx => niscope}/niscope_panel.py (100%) diff --git a/examples/nidaqmx/niscope_example.py b/examples/nidaqmx/niscope_example.py deleted file mode 100644 index 2f88b80..0000000 --- a/examples/nidaqmx/niscope_example.py +++ /dev/null @@ -1,194 +0,0 @@ -import argparse -import niscope -import numpy -import pprint -import sys -import nipanel -from pathlib import Path -import time -pp = pprint.PrettyPrinter(indent=4, width=80) -panel_script_path = Path(__file__).with_name("niscope_panel.py") -panel = nipanel.create_panel(panel_script_path) - -# def example( -# resource_name, -# options, -# -# ): -# - -def example(resource_name, channels, options, length, total_acquisition_time_in_seconds, voltage, sample_rate_in_hz, samples_per_fetch,): - # fetch_into() allows you to preallocate and reuse the destination of the fetched waveforms, which can result in better performance at the expense of the usability of fetch(). - channels = [ch.strip() for ch in channels.split(",")] - num_channels = len(channels) - num_records = 1000 - total_num_wfms = num_channels * num_records - # preallocate a single array for all samples in all waveforms - # Supported array types are: numpy.float64, numpy.int8, numpy.int16, numpy.int32 - # int8, int16, int32 are for fetching unscaled data, which is the fastest way to fetch. - # Gain and Offset are stored in the returned WaveformInfo objects and can be applied to the data by the user later. - - wfm = numpy.ndarray(length * total_num_wfms, dtype=numpy.float64) - with niscope.Session(resource_name=resource_name, options=options) as session: - session.configure_vertical(range=2, coupling=niscope.VerticalCoupling.AC) - session.configure_horizontal_timing(min_sample_rate=100000000, min_num_pts=length, ref_position=50.0, num_records=num_records, enforce_realtime=True) - total_samples = int(total_acquisition_time_in_seconds * sample_rate_in_hz) - with session.initiate(): - waveforms = session.channels[channels].fetch_into(waveform=wfm, num_records=num_records) - for i in range(len(waveforms)): - time.sleep(0.2) - panel.set_value("samples", total_samples) - panel.set_value("Waveform", waveforms[i].samples.tolist()) - - -def _main(argsv): - parser = argparse.ArgumentParser(description='Fetches data directly into a preallocated numpy array.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-n', '--resource-name', default='Dev2', help='Resource name of an NI digitizer.') - parser.add_argument('-c', '--channels', default='0', help='Channel(s) to use') - parser.add_argument('-l', '--length', default=100, type=int, help='Measure record length') - parser.add_argument('-v', '--voltage', default=1.0, type=float, help='Voltage range (V)') - parser.add_argument('-op', '--option-string', default='', type=str, help='Option string') - parser.add_argument("-t", "--time", default=10, type=int, help="Time to sample (s)") - parser.add_argument("-r", "--sample-rate", default=1000.0, type=float, help="Sample Rate (Hz)") - parser.add_argument( - "-s", "--samples-per-fetch", default=100, type=int, help="Samples per fetch" - ) - args = parser.parse_args(argsv) - example(args.resource_name, args.channels, args.option_string, args.length, args.voltage, args.time, - args.sample_rate, - args.samples_per_fetch) - - - -def main(): - _main(sys.argv[1:]) - - -def test_example(): - options = {'simulate': True, 'driver_setup': {'Model': '5124', 'BoardType': 'PXI', }, } - example('Dev2', '0, 1', options, 100, 1.0) - - -def test_main(): - cmd_line = ['--option-string', 'Simulate=1, DriverSetup=Model:5164; BoardType:PXIe', ] - _main(cmd_line) - - -if __name__ == '__main__': - main() - -# # We use fetch_into which allows us to allocate a single buffer per channel and "fetch into" it a section at a time without having to -# # reconstruct the waveform once we are done -# def example( -# resource_name, -# options, -# total_acquisition_time_in_seconds, -# voltage, -# sample_rate_in_hz, -# samples_per_fetch, -# ): -# total_samples = int(total_acquisition_time_in_seconds * sample_rate_in_hz) -# # 1. Opening session -# with niscope.Session(resource_name=resource_name, options=options) as session: -# # We will acquire on all channels of the device -# channel_list = [ -# c for c in range(session.channel_count) -# ] # Need an actual list and not a range - -# # 2. Creating numpy arrays -# waveforms = [np.ndarray(total_samples, dtype=np.float64) for c in channel_list] -# offset = session._fetch_offset - -# # 3. Configuring -# session.configure_horizontal_timing( -# min_sample_rate=sample_rate_in_hz, -# min_num_pts=1, -# ref_position=0.0, -# num_records=1, -# enforce_realtime=True, -# ) -# session.channels[channel_list].configure_vertical( -# voltage, coupling=niscope.VerticalCoupling.DC, enabled=True -# ) -# # Configure software trigger, but never send the trigger. -# # This starts an infinite acquisition, until you call session.abort() or session.close() -# session.configure_trigger_software() -# current_pos = 0 - -# # 4. initiating -# with session.initiate(): -# while current_pos < total_samples: -# # We fetch each channel at a time so we don't have to de-interleave afterwards -# # We do not keep the wfm_info returned from fetch_into -# for channel, waveform in zip(channel_list, waveforms): -# data = waveform.tolist() -# panel.set_value("total", current_pos) -# time.sleep(0.2) -# panel.set_value("Waveform", data) -# panel.set_value("samples", total_samples) -# # session.channels[channel].fetch(num_samples=1000) -# # 5. fetching - we return the slice of the waveform array that we want to "fetch into" -# session.channels[channel].fetch_into( -# waveform[current_pos : current_pos + samples_per_fetch], -# relative_to=niscope.FetchRelativeTo.READ_POINTER, -# offset=0, -# record_number=0, -# num_records=1, -# timeout=hightime.timedelta(seconds=5.0), -# ) -# current_pos += samples_per_fetch - - -# def _main(argsv): -# parser = argparse.ArgumentParser( -# description="Fetch more samples than will fit in memory.", -# formatter_class=argparse.ArgumentDefaultsHelpFormatter, -# ) -# parser.add_argument( -# "-n", "--resource-name", default="Dev2", help="Resource name of an NI digitizer." -# ) -# parser.add_argument("-t", "--time", default=10, type=int, help="Time to sample (s)") -# parser.add_argument("-v", "--voltage", default=1.0, type=float, help="Voltage range (V)") -# parser.add_argument("-op", "--option-string", default="", type=str, help="Option string") -# parser.add_argument("-r", "--sample-rate", default=1000.0, type=float, help="Sample Rate (Hz)") -# parser.add_argument( -# "-s", "--samples-per-fetch", default=100, type=int, help="Samples per fetch" -# ) -# args = parser.parse_args(argsv) -# example( -# args.resource_name, -# args.option_string, -# args.time, -# args.voltage, -# args.sample_rate, -# args.samples_per_fetch, -# ) - - -# def main(): -# _main(sys.argv[1:]) - - -# def test_example(): -# options = { -# "simulate": True, -# "driver_setup": { -# "Model": "5124", -# "BoardType": "PXI", -# }, -# } -# example("Dev2", options, 10, 1.0, 1000.0, 100) - - -# def test_main(): -# cmd_line = [ -# "--option-string", -# "Simulate=1, DriverSetup=Model:5124; BoardType:PXI", -# ] -# _main(cmd_line) - - -# if __name__ == "__main__": -# main() - - diff --git a/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py b/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py deleted file mode 100644 index 246e053..0000000 --- a/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input.py +++ /dev/null @@ -1,38 +0,0 @@ -"""This example demonstrates how to open/update a Streamlit application using nipanel package.""" - -import nipanel -import nidaqmx -import pathlib -import time - -script_path = pathlib.Path(__file__) -panel_script_path = str(script_path.with_name("nidaqmx_continuous_analog_input_panel.py")) - -panel = nipanel.StreamlitPanel( - panel_id="nidaqmx_continuous_analog_input_panel", - streamlit_script_path=panel_script_path, -) - -data_arr = [] -with nidaqmx.Task() as task: - task.ai_channels.add_ai_voltage_chan("Mod1/ai2") - task.ai_channels.add_ai_thrmcpl_chan("Mod1/ai3") - try: - total_read = 0 - while True: - data = task.read(number_of_samples_per_channel=3) - read = len(data) - total_read += read - - data_arr.append(data) - time.sleep(1) - panel.set_value("amplitude",data_arr[-1][-1]) - panel.set_value("Volts",data_arr[-1][0]) - except KeyboardInterrupt: - pass - finally: - task.stop() - print(f"\nAcquired {total_read} total samples.") - - - diff --git a/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py b/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py deleted file mode 100644 index b83f09b..0000000 --- a/examples/nidaqmx_continuous_analog_input/nidaqmx_continuous_analog_input_panel.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Streamlit application script for displaying values using nipanel package.""" -import nipanel -import streamlit as st -import streamlit.components.v1 as components -from streamlit_echarts import st_echarts - -panel = nipanel.StreamlitPanelValueAccessor(panel_id="nidaqmx_continuous_analog_input_panel") - -add_refresh_component = components.declare_component( - "panelRefreshComponent", - url=f"http://localhost:42001/panels/refresh/{panel.panel_id}",) -add_refresh_component() - - -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 -) - - -list_of_therm_amp = panel.get_value("amplitude") -list_of_voltage_amp = panel.get_value("Volts") - -if "therm_history" not in st.session_state: - st.session_state.therm_history = [] -if "volts_history" not in st.session_state: - st.session_state.volts_history = [] - -for therm_amp in list_of_therm_amp: - st.session_state.therm_history.append(therm_amp) -for voltage_amp in list_of_voltage_amp: - st.session_state.volts_history.append(voltage_amp) - -therm_amp_graph = { - "tooltip": {"trigger": "axis"}, - "legend": {"data": ["thermocouple_amplitude"]}, - "xAxis": { - "type": "category", - "data":list(range(len(st.session_state.therm_history))), - "name": "Time" - }, - "yAxis": { - "type": "value", - "name": "Thermocouple Amplitude" - }, - "series": [ - { - "name": "thermocouple_amplitude", - "type": "line", - "data": st.session_state.therm_history, - "color": "red" - }, - - - ], -} -st_echarts(options=therm_amp_graph, height="400px") - -voltage_amp_graph = { - "tooltip": {"trigger": "axis"}, - "legend": {"data": ["voltage_amplitude"]}, - "xAxis": { - "type": "category", - "data": list(range(len(st.session_state.volts_history))), - "name": "Time" - }, - "yAxis": { - "type": "value", - "name": "Voltage Amplitude" - }, - "series": [ - { - "name": "voltage_amplitude", - "type": "line", - "data": st.session_state.volts_history, - }, - - ], -} -st_echarts(options=voltage_amp_graph, height="400px") - -st.header("Voltage & Thermocouple") -voltage_therm_graph = { - "tooltip": {"trigger": "axis"}, - "legend": {"data": ["voltage_amplitude", "thermocouple_amplitude"]}, - "xAxis": { - "type": "category", - "data": list(range(len(st.session_state.volts_history))), - "name": "Time" - }, - "yAxis": { - "type": "value", - "name": "Voltage and Thermocouple Amplitude" - }, - "series": [ - { - "name": "voltage_amplitude", - "type": "line", - "data": st.session_state.volts_history, - "emphasis": {"focus":"series"}, - "smooth": True, - "seriesLayoutBy": "row", - }, - { - "name": "thermocouple_amplitude", - "type": "line", - "data": st.session_state.therm_history, - "color": "red", - "emphasis": {"focus":"series"}, - "smooth": True, - "seriesLayoutBy": "row", - }, - ], -} -st_echarts(options=voltage_therm_graph, height="400px") - -with voltage_tab: - left_volt_tab, center_volt_tab, right_volt_tab = st.columns(3) - with left_volt_tab: - st.selectbox(options=["Mod1/ai2"], 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=["Mod1/ai3"], 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/niscope/niscope_example.py b/examples/niscope/niscope_example.py new file mode 100644 index 0000000..3429bd3 --- /dev/null +++ b/examples/niscope/niscope_example.py @@ -0,0 +1,95 @@ +import argparse +import niscope +import numpy as np +import pprint +import sys +import nipanel +from pathlib import Path +import time +import hightime +pp = pprint.PrettyPrinter(indent=4, width=80) +panel_script_path = Path(__file__).with_name("niscope_panel.py") +panel = nipanel.create_panel(panel_script_path) + + + +def example(resource_name, channels, options, length, total_acquisition_time_in_seconds, voltage, sample_rate_in_hz, samples_per_fetch,): + channels = [ch.strip() for ch in channels.split(",")] + num_channels = len(channels) + + num_records = 1000 + total_num_wfms = num_channels * num_records + current_pos = 0 + # preallocate a single array for all samples in all waveforms + # Supported array types are: numpy.float64, numpy.int8, numpy.int16, numpy.int32 + # int8, int16, int32 are for fetching unscaled data, which is the fastest way to fetch. + # Gain and Offset are stored in the returned WaveformInfo objects and can be applied to the data by the user later. + total_samples = int(total_acquisition_time_in_seconds * sample_rate_in_hz) + wfm = np.ndarray(length * total_num_wfms, dtype=np.int8) + with niscope.Session(resource_name=resource_name, options=options) as session: + + session.configure_vertical(range=10, coupling=niscope.VerticalCoupling.DC, enabled=True) + session.configure_horizontal_timing(min_sample_rate=100000000, min_num_pts=length, ref_position=50.0, num_records=num_records, enforce_realtime=True) + max_points_per_fetch = 1000 + session._fetch_meas_num_samples = max_points_per_fetch + session.configure_trigger_software() + + with session.initiate(): + waveforms = session.channels[channels].fetch_into( relative_to=niscope.FetchRelativeTo.READ_POINTER, + offset=0, record_number=0, timeout=hightime.timedelta(seconds=5.0), waveform=wfm, num_records=num_records) + offset = session._fetch_offset + gain = session.meas_array_gain + for i in range(len(waveforms)): + time.sleep(1) + amplitude_list = [] + current_pos += samples_per_fetch + panel.set_value("samples", total_samples) + + total_data = waveforms[i].samples.tolist() + print(waveforms[i]) + for amplitude in total_data: + amplitude = (amplitude * 10) * gain + offset + amplitude_list.append(amplitude) + panel.set_value("Waveform", amplitude_list) + +def _main(argsv): + parser = argparse.ArgumentParser(description='Fetches data directly into a preallocated numpy array.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-n', '--resource-name', default='Dev2', help='Resource name of an NI digitizer.') + parser.add_argument('-c', '--channels', default='0', help='Channel(s) to use') + parser.add_argument('-l', '--length', default=100, type=int, help='Measure record length') + parser.add_argument('-v', '--voltage', default=1.0, type=float, help='Voltage range (V)') + parser.add_argument('-op', '--option-string', default='', type=str, help='Option string') + parser.add_argument("-t", "--time", default=1, type=int, help="Time to sample (s)") + parser.add_argument("-r", "--sample-rate", default=1000.0, type=float, help="Sample Rate (Hz)") + parser.add_argument( + "-s", "--samples-per-fetch", default=1000, type=int, help="Samples per fetch" + ) + args = parser.parse_args(argsv) + example(args.resource_name, + args.channels, + args.option_string, + args.length, + args.voltage, + args.time, + args.sample_rate, + args.samples_per_fetch) + + + +def main(): + _main(sys.argv[1:]) + + +def test_example(): + options = {'simulate': True, 'driver_setup': {'Model': '5124', 'BoardType': 'PXI', }, } + example("Dev2", options, 1, 1.0, 1000.0, 1000) + + +def test_main(): + cmd_line = ['--option-string', 'Simulate=1, RangeCheck=1, DriverSetup=Model:5124; BoardType:PXI', ] + _main(cmd_line) + + +if __name__ == '__main__': + main() + diff --git a/examples/nidaqmx/niscope_panel.py b/examples/niscope/niscope_panel.py similarity index 100% rename from examples/nidaqmx/niscope_panel.py rename to examples/niscope/niscope_panel.py