Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8be90f7
add numeric and enum inputs to the simple graph example
Jun 30, 2025
585948c
get_value returns the enum type when a default is provided
Jun 30, 2025
163735b
_sync_session_state()
Jul 1, 2025
acc1e9b
GetValueResponse.found
Jul 1, 2025
5c1e492
nipanel.enum_selectbox
Jul 1, 2025
5c64b0f
fix mypy
Jul 1, 2025
3c84a99
start improving daqmx example
Jul 2, 2025
0538676
Enhance enum support in Streamlit panel and tests
Jul 2, 2025
1fc1ce2
Refactor NI-DAQmx example to enhance channel settings and timing conf…
Jul 2, 2025
bd84426
finish updating daqmx example, so all the controls (except I/O) are f…
Jul 2, 2025
d4dd4c9
clean up lint and mypy
Jul 2, 2025
1b93e88
Merge remote-tracking branch 'origin/main' into users/mprosser/input-…
Jul 7, 2025
0cbbee4
update comment in proto file
Jul 7, 2025
2a0c4e4
revert simple_graph changes
Jul 7, 2025
285401f
put enum_selectbox in a controls folder
Jul 7, 2025
2731464
cosmetic improvements for the nidaqmx example panel
Jul 7, 2025
80f7d60
flag checkboxes control
Jul 7, 2025
3de247e
refactor: reorganize controls imports and update usage in examples
Jul 7, 2025
e77345e
cleanup
Jul 7, 2025
6d7df1e
feat: implement set_value_if_changed method to optimize value updates…
Jul 7, 2025
cb4a743
cleanup
Jul 7, 2025
0395bba
cleanup
Jul 7, 2025
7c8dc96
uptake latest proto file with optional value in GetValueResponse
Jul 10, 2025
4914880
misc feedback
Jul 10, 2025
de1a73b
type annotations
Jul 10, 2025
d23d9c1
Merge remote-tracking branch 'origin/main' into users/mprosser/input-…
Jul 10, 2025
fe0cfc3
improve run/stop button behavior in nidaqmx example
Jul 10, 2025
c7318d6
brad's feedback
Jul 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions examples/all_types/all_types_panel.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
"""A Streamlit visualization panel for the all_types.py example script."""

from enum import Enum, Flag

import streamlit as st
from define_types import all_types_with_values

import nipanel
import nipanel.controls as ni


st.set_page_config(page_title="All Types Example", page_icon="📊", layout="wide")
st.title("All Types Example")

panel = nipanel.get_panel_accessor()
for name in all_types_with_values.keys():
col1, col2 = st.columns([0.4, 0.6])
st.markdown("---")

default_value = all_types_with_values[name]
col1, col2, col3 = st.columns([0.2, 0.2, 0.6])

with col1:
st.write(name)

with col2:
st.write(panel.get_value(name))
if isinstance(default_value, bool):
st.checkbox(label=name, value=default_value, key=name)
elif isinstance(default_value, Flag):
ni.flag_checkboxes(panel, label=name, value=default_value, key=name)
elif isinstance(default_value, Enum) and not isinstance(default_value, Flag):
ni.enum_selectbox(panel, label=name, value=default_value, key=name)
elif isinstance(default_value, int) and not isinstance(default_value, Flag):
st.number_input(label=name, value=default_value, key=name)
elif isinstance(default_value, float):
st.number_input(label=name, value=default_value, key=name, format="%.2f")
elif isinstance(default_value, str):
st.text_input(label=name, value=default_value, key=name)

with col3:
value = panel.get_value(name)
value_with_default = panel.get_value(name, default_value=default_value)
st.write(value_with_default)
if str(value) != str(value_with_default):
st.write("(", value, ")")
50 changes: 43 additions & 7 deletions examples/all_types/define_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ class MyIntFlags(enum.IntFlag):
VALUE4 = 4


class MyIntableFlags(enum.Flag):
"""Example of an Flag enum with int values."""

VALUE8 = 8
VALUE16 = 16
VALUE32 = 32


class MyIntEnum(enum.IntEnum):
"""Example of an IntEnum enum."""

Expand All @@ -23,6 +31,14 @@ class MyIntEnum(enum.IntEnum):
VALUE30 = 30


class MyIntableEnum(enum.Enum):
"""Example of an enum with int values."""

VALUE100 = 100
VALUE200 = 200
VALUE300 = 300


class MyStrEnum(str, enum.Enum):
"""Example of a mixin string enum."""

Expand All @@ -31,23 +47,46 @@ class MyStrEnum(str, enum.Enum):
VALUE3 = "value3"


class MyStringableEnum(enum.Enum):
"""Example of an enum with string values."""

VALUE1 = "value1"
VALUE2 = "value2"
VALUE3 = "value3"


class MyMixedEnum(enum.Enum):
"""Example of an enum with mixed values."""

VALUE1 = "value1"
VALUE2 = 2
VALUE3 = 3.0


all_types_with_values = {
# supported scalar types
"bool": True,
"bytes": b"robotext",
"float": 13.12,
"int": 42,
"str": "sample string",
# supported enum and flag types
"intflags": MyIntFlags.VALUE1 | MyIntFlags.VALUE4,
"intenum": MyIntEnum.VALUE20,
"strenum": MyStrEnum.VALUE3,
"intableenum": MyIntableEnum.VALUE200,
"intableflags": MyIntableFlags.VALUE8 | MyIntableFlags.VALUE32,
"stringableenum": MyStringableEnum.VALUE2,
"mixedenum": MyMixedEnum.VALUE2,
# NI types
"nitypes_Scalar": Scalar(42, "m"),
"nitypes_AnalogWaveform": AnalogWaveform.from_array_1d(np.array([1.0, 2.0, 3.0])),
# supported collection types
"bool_collection": [True, False, True],
"bytes_collection": [b"one", b"two", b"three"],
"float_collection": [1.1, 2.2, 3.3],
"int_collection": [1, 2, 3],
"str_collection": ["one", "two", "three"],
# supported enum and flag types
"intflags": MyIntFlags.VALUE1 | MyIntFlags.VALUE4,
"intenum": MyIntEnum.VALUE20,
"strenum": MyStrEnum.VALUE3,
"intflags_collection": [MyIntFlags.VALUE1, MyIntFlags.VALUE2, MyIntFlags.VALUE4],
"intenum_collection": [MyIntEnum.VALUE10, MyIntEnum.VALUE20, MyIntEnum.VALUE30],
"strenum_collection": [MyStrEnum.VALUE1, MyStrEnum.VALUE2, MyStrEnum.VALUE3],
Expand All @@ -56,9 +95,6 @@ class MyStrEnum(str, enum.Enum):
"tuple": (4, 5, 6),
"set": {7, 8, 9},
"frozenset": frozenset([10, 11, 12]),
# NI types
"nitypes_Scalar": Scalar(42, "m"),
"nitypes_AnalogWaveform": AnalogWaveform.from_array_1d(np.array([1.0, 2.0, 3.0])),
# supported 2D collections
"list_list_float": [[1.0, 2.0], [3.0, 4.0]],
"tuple_tuple_float": ((1.0, 2.0), (3.0, 4.0)),
Expand Down
89 changes: 68 additions & 21 deletions examples/nidaqmx/nidaqmx_continuous_analog_input.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,81 @@
"""Data acquisition script that continuously acquires analog input data."""

import time
from pathlib import Path

import nidaqmx
from nidaqmx.constants import AcquisitionType
from nidaqmx.constants import (
AcquisitionType,
TerminalConfiguration,
CJCSource,
TemperatureUnits,
ThermocoupleType,
LoggingMode,
LoggingOperation,
)

import nipanel

panel_script_path = Path(__file__).with_name("nidaqmx_continuous_analog_input_panel.py")
panel = nipanel.create_panel(panel_script_path)

# How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/
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"Panel URL: {panel.panel_url}")
try:
print(f"Press Ctrl + C to stop")
while True:
data = task.read(
number_of_samples_per_channel=1000 # pyright: ignore[reportArgumentType]
print(f"Press Ctrl + C to quit")
while True:
print(f"Waiting for the 'Run' button to be pressed...")
while not panel.get_value("run_button", False):
time.sleep(0.1)

panel.set_value("is_running", True)

# How to use nidaqmx: https://nidaqmx-python.readthedocs.io/en/stable/
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan(
physical_channel="Dev1/ai0",
min_val=panel.get_value("voltage_min_value", -5.0),
max_val=panel.get_value("voltage_max_value", 5.0),
terminal_config=panel.get_value(
"terminal_configuration", TerminalConfiguration.DEFAULT
),
)
task.ai_channels.add_ai_thrmcpl_chan(
"Dev1/ai1",
min_val=panel.get_value("thermocouple_min_value", 0.0),
max_val=panel.get_value("thermocouple_max_value", 100.0),
units=panel.get_value("thermocouple_units", TemperatureUnits.DEG_C),
thermocouple_type=panel.get_value("thermocouple_type", ThermocoupleType.K),
cjc_source=panel.get_value(
"thermocouple_cjc_source", CJCSource.CONSTANT_USER_VALUE
),
cjc_val=panel.get_value("thermocouple_cjc_val", 25.0),
)
panel.set_value("voltage_data", data[0])
panel.set_value("thermocouple_data", data[1])
except KeyboardInterrupt:
pass
finally:
task.stop()
task.timing.cfg_samp_clk_timing(
rate=panel.get_value("sample_rate_input", 1000.0),
sample_mode=AcquisitionType.CONTINUOUS,
samps_per_chan=panel.get_value("samples_per_channel", 3000),
)
task.in_stream.configure_logging(
file_path=panel.get_value("tdms_file_path", "data.tdms"),
logging_mode=panel.get_value("logging_mode", LoggingMode.OFF),
operation=LoggingOperation.OPEN_OR_CREATE,
)
panel.set_value("sample_rate", task._timing.samp_clk_rate)
try:
print(f"Starting data acquisition...")
task.start()
while not panel.get_value("stop_button", False):
data = task.read(
number_of_samples_per_channel=1000 # pyright: ignore[reportArgumentType]
)
panel.set_value("voltage_data", data[0])
panel.set_value("thermocouple_data", data[1])
except KeyboardInterrupt:
raise
finally:
print(f"Stopping data acquisition...")
task.stop()
panel.set_value("is_running", False)

except KeyboardInterrupt:
pass
Loading