From 48c9de043c68c5fa9c28c9494fb53f55d93872ce Mon Sep 17 00:00:00 2001 From: Zach Hindes Date: Tue, 20 May 2025 16:42:33 -0500 Subject: [PATCH 1/5] fix numbered virtual channel names in some cases --- generated/nidaqmx/_base_interpreter.py | 4 ++ generated/nidaqmx/_grpc_interpreter.py | 3 + generated/nidaqmx/_library_interpreter.py | 24 +++++++ .../collections/_ai_channel_collection.py | 31 ++++++--- .../collections/_ao_channel_collection.py | 31 ++++++--- .../collections/_ci_channel_collection.py | 31 ++++++--- .../collections/_co_channel_collection.py | 31 ++++++--- .../collections/_di_channel_collection.py | 45 ++++++++----- .../collections/_do_channel_collection.py | 45 ++++++++----- src/codegen/metadata/functions.py | 26 ++++++++ .../templates/_grpc_interpreter.py.mako | 3 + .../_ai_channel_collection.py.mako | 29 +++++--- .../_ao_channel_collection.py.mako | 29 +++++--- .../_ci_channel_collection.py.mako | 29 +++++--- .../_co_channel_collection.py.mako | 29 +++++--- .../_di_channel_collection.py.mako | 45 ++++++++----- .../_do_channel_collection.py.mako | 45 ++++++++----- src/codegen/utilities/interpreter_helpers.py | 1 + .../task/channels/test_ai_channel.py | 15 ++++- .../task/channels/test_ci_channel.py | 9 +++ .../task/channels/test_co_channel.py | 9 +++ .../task/channels/test_di_channel.py | 66 ++++++++++++++++++- .../task/channels/test_do_channel.py | 66 ++++++++++++++++++- 23 files changed, 503 insertions(+), 143 deletions(-) diff --git a/generated/nidaqmx/_base_interpreter.py b/generated/nidaqmx/_base_interpreter.py index ad7b34584..87c970ec8 100644 --- a/generated/nidaqmx/_base_interpreter.py +++ b/generated/nidaqmx/_base_interpreter.py @@ -1168,6 +1168,10 @@ def get_write_attribute_uint32(self, task, attribute): def get_write_attribute_uint64(self, task, attribute): raise NotImplementedError + @abc.abstractmethod + def internal_get_last_created_chan(self): + raise NotImplementedError + @abc.abstractmethod def is_task_done(self, task): raise NotImplementedError diff --git a/generated/nidaqmx/_grpc_interpreter.py b/generated/nidaqmx/_grpc_interpreter.py index 942de74d5..7091a9e70 100644 --- a/generated/nidaqmx/_grpc_interpreter.py +++ b/generated/nidaqmx/_grpc_interpreter.py @@ -3597,6 +3597,9 @@ def set_runtime_environment( self, environment, environment_version, reserved_1, reserved_2): raise NotImplementedError + def internal_get_last_created_chan(self): + raise NotImplementedError + def _assign_numpy_array(numpy_array, grpc_array): """ diff --git a/generated/nidaqmx/_library_interpreter.py b/generated/nidaqmx/_library_interpreter.py index 6de3d2ca3..1f30052ec 100644 --- a/generated/nidaqmx/_library_interpreter.py +++ b/generated/nidaqmx/_library_interpreter.py @@ -4048,6 +4048,30 @@ def get_write_attribute_uint64(self, task, attribute): self.check_for_error(error_code) return value.value + def internal_get_last_created_chan(self): + cfunc = lib_importer.windll.DAQmxInternalGetLastCreatedChan + if cfunc.argtypes is None: + with cfunc.arglock: + if cfunc.argtypes is None: + cfunc.argtypes = [ + ctypes.c_char_p, ctypes.c_uint] + + temp_size = 0 + while True: + value = ctypes.create_string_buffer(temp_size) + size_or_code = cfunc( + value, temp_size) + if is_string_buffer_too_small(size_or_code): + # Buffer size must have changed between calls; check again. + temp_size = 0 + elif size_or_code > 0 and temp_size == 0: + # Buffer size obtained, use to retrieve data. + temp_size = size_or_code + else: + break + self.check_for_error(size_or_code) + return value.value.decode(lib_importer.encoding) + def is_task_done(self, task): is_task_done = c_bool32() diff --git a/generated/nidaqmx/task/collections/_ai_channel_collection.py b/generated/nidaqmx/task/collections/_ai_channel_collection.py index 998fdcbd4..7ecb49450 100644 --- a/generated/nidaqmx/task/collections/_ai_channel_collection.py +++ b/generated/nidaqmx/task/collections/_ai_channel_collection.py @@ -2,6 +2,7 @@ import numpy +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._ai_channel import AIChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -45,18 +46,28 @@ def _create_chan(self, physical_channel, name_to_assign_to_channel=''): Specifies the newly created AIChannel object. """ - if name_to_assign_to_channel: - num_channels = len(unflatten_channel_string(physical_channel)) - - if num_channels > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_channels-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_channels = len(unflatten_channel_string(physical_channel)) + + if num_channels > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_channels-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = physical_channel + virtual_channel_name = physical_channel - return AIChannel(self._handle, name, self._interpreter) + return AIChannel(self._handle, virtual_channel_name, self._interpreter) def add_ai_accel_4_wire_dc_voltage_chan( self, physical_channel, name_to_assign_to_channel="", diff --git a/generated/nidaqmx/task/collections/_ao_channel_collection.py b/generated/nidaqmx/task/collections/_ao_channel_collection.py index dbb4508e9..e0c997b27 100644 --- a/generated/nidaqmx/task/collections/_ao_channel_collection.py +++ b/generated/nidaqmx/task/collections/_ao_channel_collection.py @@ -1,5 +1,6 @@ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._ao_channel import AOChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -31,18 +32,28 @@ def _create_chan(self, physical_channel, name_to_assign_to_channel=''): Specifies the newly created AOChannel object. """ - if name_to_assign_to_channel: - num_channels = len(unflatten_channel_string(physical_channel)) - - if num_channels > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_channels-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_channels = len(unflatten_channel_string(physical_channel)) + + if num_channels > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_channels-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = physical_channel + virtual_channel_name = physical_channel - return AOChannel(self._handle, name, self._interpreter) + return AOChannel(self._handle, virtual_channel_name, self._interpreter) def add_ao_current_chan( self, physical_channel, name_to_assign_to_channel="", min_val=0.0, diff --git a/generated/nidaqmx/task/collections/_ci_channel_collection.py b/generated/nidaqmx/task/collections/_ci_channel_collection.py index c8c1fad60..3ae774927 100644 --- a/generated/nidaqmx/task/collections/_ci_channel_collection.py +++ b/generated/nidaqmx/task/collections/_ci_channel_collection.py @@ -1,5 +1,6 @@ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._ci_channel import CIChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -33,18 +34,28 @@ def _create_chan(self, counter, name_to_assign_to_channel=''): Specifies the newly created CIChannel object. """ - if name_to_assign_to_channel: - num_counters = len(unflatten_channel_string(counter)) - - if num_counters > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_counters-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_counters = len(unflatten_channel_string(counter)) + + if num_counters > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_counters-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = counter + virtual_channel_name = counter - return CIChannel(self._handle, name, self._interpreter) + return CIChannel(self._handle, virtual_channel_name, self._interpreter) def add_ci_ang_encoder_chan( self, counter, name_to_assign_to_channel="", diff --git a/generated/nidaqmx/task/collections/_co_channel_collection.py b/generated/nidaqmx/task/collections/_co_channel_collection.py index ecd9ed5b4..8922ac9ac 100644 --- a/generated/nidaqmx/task/collections/_co_channel_collection.py +++ b/generated/nidaqmx/task/collections/_co_channel_collection.py @@ -1,5 +1,6 @@ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._co_channel import COChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -31,18 +32,28 @@ def _create_chan(self, counter, name_to_assign_to_channel=''): Specifies the newly created COChannel object. """ - if name_to_assign_to_channel: - num_counters = len(unflatten_channel_string(counter)) - - if num_counters > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_counters-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_counters = len(unflatten_channel_string(counter)) + + if num_counters > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_counters-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = counter + virtual_channel_name = counter - return COChannel(self._handle, name, self._interpreter) + return COChannel(self._handle, virtual_channel_name, self._interpreter) def add_co_pulse_chan_freq( self, counter, name_to_assign_to_channel="", diff --git a/generated/nidaqmx/task/collections/_di_channel_collection.py b/generated/nidaqmx/task/collections/_di_channel_collection.py index 7c0af1dc8..9704432c4 100644 --- a/generated/nidaqmx/task/collections/_di_channel_collection.py +++ b/generated/nidaqmx/task/collections/_di_channel_collection.py @@ -1,5 +1,6 @@ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._di_channel import DIChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -34,25 +35,37 @@ def _create_chan(self, lines, line_grouping, name_to_assign_to_lines=''): Specifies the newly created DIChannel object. """ - unflattened_lines = unflatten_channel_string(lines) - num_lines = len(unflattened_lines) - - if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: - if name_to_assign_to_lines or num_lines == 1: - name = lines - else: - name = unflattened_lines[0] + '...' - else: - if name_to_assign_to_lines: - if num_lines > 1: - name = '{}0:{}'.format( - name_to_assign_to_lines, num_lines-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + unflattened_lines = unflatten_channel_string(lines) + num_lines = len(unflattened_lines) + + if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: + if name_to_assign_to_lines: + virtual_channel_name = name_to_assign_to_lines + elif num_lines == 1: + virtual_channel_name = lines else: - name = name_to_assign_to_lines + virtual_channel_name = unflattened_lines[0] + '...' else: - name = lines + if name_to_assign_to_lines: + if num_lines > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_lines, num_lines-1) + else: + virtual_channel_name = name_to_assign_to_lines + else: + virtual_channel_name = lines - return DIChannel(self._handle, name, self._interpreter) + return DIChannel(self._handle, virtual_channel_name, self._interpreter) def add_di_chan( self, lines, name_to_assign_to_lines="", diff --git a/generated/nidaqmx/task/collections/_do_channel_collection.py b/generated/nidaqmx/task/collections/_do_channel_collection.py index 58e4a26ac..f096a90d2 100644 --- a/generated/nidaqmx/task/collections/_do_channel_collection.py +++ b/generated/nidaqmx/task/collections/_do_channel_collection.py @@ -1,5 +1,6 @@ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._do_channel import DOChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -34,25 +35,37 @@ def _create_chan(self, lines, line_grouping, name_to_assign_to_lines=''): Specifies the newly created DOChannel object. """ - unflattened_lines = unflatten_channel_string(lines) - num_lines = len(unflattened_lines) - - if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: - if name_to_assign_to_lines or num_lines == 1: - name = lines - else: - name = unflattened_lines[0] + '...' - else: - if name_to_assign_to_lines: - if num_lines > 1: - name = '{}0:{}'.format( - name_to_assign_to_lines, num_lines-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + unflattened_lines = unflatten_channel_string(lines) + num_lines = len(unflattened_lines) + + if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: + if name_to_assign_to_lines: + virtual_channel_name = name_to_assign_to_lines + elif num_lines == 1: + virtual_channel_name = lines else: - name = name_to_assign_to_lines + virtual_channel_name = unflattened_lines[0] + '...' else: - name = lines + if name_to_assign_to_lines: + if num_lines > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_lines, num_lines-1) + else: + virtual_channel_name = name_to_assign_to_lines + else: + virtual_channel_name = lines - return DOChannel(self._handle, name, self._interpreter) + return DOChannel(self._handle, virtual_channel_name, self._interpreter) def add_do_chan( self, lines, name_to_assign_to_lines="", diff --git a/src/codegen/metadata/functions.py b/src/codegen/metadata/functions.py index da47c3f32..49980a2f6 100644 --- a/src/codegen/metadata/functions.py +++ b/src/codegen/metadata/functions.py @@ -17186,6 +17186,32 @@ 'python_codegen_method': 'CustomCode', 'returns': 'int32' }, + 'InternalGetLastCreatedChan': { + 'calling_convention': 'StdCall', + 'codegen_method': 'private', + 'parameters': [ + { + 'ctypes_data_type': 'ctypes.c_char_p', + 'direction': 'out', + 'name': 'value', + 'python_data_type': 'str', + 'size': { + 'mechanism': 'ivi-dance', + 'value': 'size' + }, + 'type': 'char[]' + }, + { + 'ctypes_data_type': 'ctypes.c_uint32', + 'direction': 'in', + 'name': 'size', + 'python_data_type': 'int', + 'type': 'uInt32' + } + ], + 'python_codegen_method': 'CustomCode', + 'returns': 'int32' + }, 'IsTaskDone': { 'calling_convention': 'StdCall', 'handle_parameter': { diff --git a/src/codegen/templates/_grpc_interpreter.py.mako b/src/codegen/templates/_grpc_interpreter.py.mako index 6d1b4cec2..5a64b136f 100644 --- a/src/codegen/templates/_grpc_interpreter.py.mako +++ b/src/codegen/templates/_grpc_interpreter.py.mako @@ -242,6 +242,9 @@ class GrpcStubInterpreter(BaseInterpreter): self, environment, environment_version, reserved_1, reserved_2): raise NotImplementedError + def internal_get_last_created_chan(self): + raise NotImplementedError + def _assign_numpy_array(numpy_array, grpc_array): """ diff --git a/src/codegen/templates/task/collections/_ai_channel_collection.py.mako b/src/codegen/templates/task/collections/_ai_channel_collection.py.mako index c10f2ce76..fb039a417 100644 --- a/src/codegen/templates/task/collections/_ai_channel_collection.py.mako +++ b/src/codegen/templates/task/collections/_ai_channel_collection.py.mako @@ -8,6 +8,7 @@ import numpy +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._ai_channel import AIChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -41,18 +42,28 @@ class AIChannelCollection(ChannelCollection): Specifies the newly created AIChannel object. """ - if name_to_assign_to_channel: - num_channels = len(unflatten_channel_string(physical_channel)) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass - if num_channels > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_channels-1) + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_channels = len(unflatten_channel_string(physical_channel)) + + if num_channels > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_channels-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = physical_channel + virtual_channel_name = physical_channel - return AIChannel(self._handle, name, self._interpreter) + return AIChannel(self._handle, virtual_channel_name, self._interpreter) <%namespace name="function_template" file="/function_template.py.mako"/>\ %for function_object in functions: diff --git a/src/codegen/templates/task/collections/_ao_channel_collection.py.mako b/src/codegen/templates/task/collections/_ao_channel_collection.py.mako index 5be2fce8c..640ed8798 100644 --- a/src/codegen/templates/task/collections/_ao_channel_collection.py.mako +++ b/src/codegen/templates/task/collections/_ao_channel_collection.py.mako @@ -6,6 +6,7 @@ %>\ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._ao_channel import AOChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -39,18 +40,28 @@ class AOChannelCollection(ChannelCollection): Specifies the newly created AOChannel object. """ - if name_to_assign_to_channel: - num_channels = len(unflatten_channel_string(physical_channel)) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass - if num_channels > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_channels-1) + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_channels = len(unflatten_channel_string(physical_channel)) + + if num_channels > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_channels-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = physical_channel + virtual_channel_name = physical_channel - return AOChannel(self._handle, name, self._interpreter) + return AOChannel(self._handle, virtual_channel_name, self._interpreter) <%namespace name="function_template" file="/function_template.py.mako"/>\ %for function_object in functions: diff --git a/src/codegen/templates/task/collections/_ci_channel_collection.py.mako b/src/codegen/templates/task/collections/_ci_channel_collection.py.mako index 7f4fabe55..a0ae77fed 100644 --- a/src/codegen/templates/task/collections/_ci_channel_collection.py.mako +++ b/src/codegen/templates/task/collections/_ci_channel_collection.py.mako @@ -6,6 +6,7 @@ %>\ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._ci_channel import CIChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -39,18 +40,28 @@ class CIChannelCollection(ChannelCollection): Specifies the newly created CIChannel object. """ - if name_to_assign_to_channel: - num_counters = len(unflatten_channel_string(counter)) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass - if num_counters > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_counters-1) + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_counters = len(unflatten_channel_string(counter)) + + if num_counters > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_counters-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = counter + virtual_channel_name = counter - return CIChannel(self._handle, name, self._interpreter) + return CIChannel(self._handle, virtual_channel_name, self._interpreter) <%namespace name="function_template" file="/function_template.py.mako"/>\ %for function_object in functions: diff --git a/src/codegen/templates/task/collections/_co_channel_collection.py.mako b/src/codegen/templates/task/collections/_co_channel_collection.py.mako index 4fd83d282..a0e36da36 100644 --- a/src/codegen/templates/task/collections/_co_channel_collection.py.mako +++ b/src/codegen/templates/task/collections/_co_channel_collection.py.mako @@ -6,6 +6,7 @@ %>\ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._co_channel import COChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -39,18 +40,28 @@ class COChannelCollection(ChannelCollection): Specifies the newly created COChannel object. """ - if name_to_assign_to_channel: - num_counters = len(unflatten_channel_string(counter)) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass - if num_counters > 1: - name = '{}0:{}'.format( - name_to_assign_to_channel, num_counters-1) + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + if name_to_assign_to_channel: + num_counters = len(unflatten_channel_string(counter)) + + if num_counters > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_channel, num_counters-1) + else: + virtual_channel_name = name_to_assign_to_channel else: - name = name_to_assign_to_channel - else: - name = counter + virtual_channel_name = counter - return COChannel(self._handle, name, self._interpreter) + return COChannel(self._handle, virtual_channel_name, self._interpreter) <%namespace name="function_template" file="/function_template.py.mako"/>\ %for function_object in functions: diff --git a/src/codegen/templates/task/collections/_di_channel_collection.py.mako b/src/codegen/templates/task/collections/_di_channel_collection.py.mako index e80c2aefc..3486716fe 100644 --- a/src/codegen/templates/task/collections/_di_channel_collection.py.mako +++ b/src/codegen/templates/task/collections/_di_channel_collection.py.mako @@ -6,6 +6,7 @@ %>\ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._di_channel import DIChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -42,25 +43,37 @@ class DIChannelCollection(ChannelCollection): Specifies the newly created DIChannel object. """ - unflattened_lines = unflatten_channel_string(lines) - num_lines = len(unflattened_lines) - - if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: - if name_to_assign_to_lines or num_lines == 1: - name = lines - else: - name = unflattened_lines[0] + '...' - else: - if name_to_assign_to_lines: - if num_lines > 1: - name = '{}0:{}'.format( - name_to_assign_to_lines, num_lines-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + unflattened_lines = unflatten_channel_string(lines) + num_lines = len(unflattened_lines) + + if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: + if name_to_assign_to_lines: + virtual_channel_name = name_to_assign_to_lines + elif num_lines == 1: + virtual_channel_name = lines else: - name = name_to_assign_to_lines + virtual_channel_name = unflattened_lines[0] + '...' else: - name = lines + if name_to_assign_to_lines: + if num_lines > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_lines, num_lines-1) + else: + virtual_channel_name = name_to_assign_to_lines + else: + virtual_channel_name = lines - return DIChannel(self._handle, name, self._interpreter) + return DIChannel(self._handle, virtual_channel_name, self._interpreter) <%namespace name="function_template" file="/function_template.py.mako"/>\ %for function_object in functions: diff --git a/src/codegen/templates/task/collections/_do_channel_collection.py.mako b/src/codegen/templates/task/collections/_do_channel_collection.py.mako index 0ad283de4..782a16572 100644 --- a/src/codegen/templates/task/collections/_do_channel_collection.py.mako +++ b/src/codegen/templates/task/collections/_do_channel_collection.py.mako @@ -6,6 +6,7 @@ %>\ # Do not edit this file; it was automatically generated. +from nidaqmx.errors import DaqFunctionNotSupportedError from nidaqmx.task.channels._do_channel import DOChannel from nidaqmx.task.collections._channel_collection import ChannelCollection from nidaqmx.utils import unflatten_channel_string @@ -42,25 +43,37 @@ class DOChannelCollection(ChannelCollection): Specifies the newly created DOChannel object. """ - unflattened_lines = unflatten_channel_string(lines) - num_lines = len(unflattened_lines) - - if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: - if name_to_assign_to_lines or num_lines == 1: - name = lines - else: - name = unflattened_lines[0] + '...' - else: - if name_to_assign_to_lines: - if num_lines > 1: - name = '{}0:{}'.format( - name_to_assign_to_lines, num_lines-1) + # Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library + # interpreter. + virtual_channel_name = None + try: + virtual_channel_name = self._interpreter.internal_get_last_created_chan() + except (NotImplementedError, DaqFunctionNotSupportedError): + pass + + # Fallback implementation is sometimes incorrect. + if virtual_channel_name is None: + unflattened_lines = unflatten_channel_string(lines) + num_lines = len(unflattened_lines) + + if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES: + if name_to_assign_to_lines: + virtual_channel_name = name_to_assign_to_lines + elif num_lines == 1: + virtual_channel_name = lines else: - name = name_to_assign_to_lines + virtual_channel_name = unflattened_lines[0] + '...' else: - name = lines + if name_to_assign_to_lines: + if num_lines > 1: + virtual_channel_name = '{}0:{}'.format( + name_to_assign_to_lines, num_lines-1) + else: + virtual_channel_name = name_to_assign_to_lines + else: + virtual_channel_name = lines - return DOChannel(self._handle, name, self._interpreter) + return DOChannel(self._handle, virtual_channel_name, self._interpreter) <%namespace name="function_template" file="/function_template.py.mako"/>\ %for function_object in functions: diff --git a/src/codegen/utilities/interpreter_helpers.py b/src/codegen/utilities/interpreter_helpers.py index c0f4b1ae6..a47c8436f 100644 --- a/src/codegen/utilities/interpreter_helpers.py +++ b/src/codegen/utilities/interpreter_helpers.py @@ -68,6 +68,7 @@ "get_error_string", "read_id_pin_memory", "set_runtime_environment", + "internal_get_last_created_chan", ] LIBRARY_INTERPRETER_IGNORED_FUNCTIONS = [ diff --git a/tests/component/task/channels/test_ai_channel.py b/tests/component/task/channels/test_ai_channel.py index 6f4a3d1f8..e6659e3bb 100644 --- a/tests/component/task/channels/test_ai_channel.py +++ b/tests/component/task/channels/test_ai_channel.py @@ -34,7 +34,7 @@ ) from nidaqmx.error_codes import DAQmxErrors from nidaqmx.errors import DaqError -from nidaqmx.system import Device +from nidaqmx.system import Device, System from nidaqmx.task.channels import AIChannel from tests.helpers import configure_teds @@ -1294,3 +1294,16 @@ def test___task___add_teds_ai_voltage_chan_with_excit___raises_teds_sensor_not_d ) assert exc_info.value.error_code == DAQmxErrors.TEDS_SENSOR_NOT_DETECTED + + +@pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) +def test___task___add_ai_chans___sets_channel_name( + task: Task, + sim_6363_device: Device, +) -> None: + if System.local().driver_version < (24, 5, 0): + pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") + + chan: AIChannel = task.ai_channels.add_ai_voltage_chan(f"{sim_6363_device.name}/ai0:3", name_to_assign_to_channel="myChan09") + + assert chan.name == "myChan09:11" \ No newline at end of file diff --git a/tests/component/task/channels/test_ci_channel.py b/tests/component/task/channels/test_ci_channel.py index c4923723f..7a8dc82a2 100644 --- a/tests/component/task/channels/test_ci_channel.py +++ b/tests/component/task/channels/test_ci_channel.py @@ -383,3 +383,12 @@ def test___task___add_ci_two_edge_sep_chan___sets_channel_attributes( assert chan.ci_meas_type == UsageTypeCI.PULSE_WIDTH_DIGITAL_TWO_EDGE_SEPARATION assert chan.ci_two_edge_sep_first_edge == first_edge assert chan.ci_two_edge_sep_second_edge == second_edge + + +def test___task___add_ci_chans___sets_channel_name( + task: Task, + sim_6363_device: Device, +) -> None: + chan: CIChannel = task.ci_channels.add_ci_count_edges_chan(sim_6363_device.ci_physical_chans[0].name, name_to_assign_to_channel="myChan") + + assert chan.name == "myChan" \ No newline at end of file diff --git a/tests/component/task/channels/test_co_channel.py b/tests/component/task/channels/test_co_channel.py index d2239bc58..d692c691a 100644 --- a/tests/component/task/channels/test_co_channel.py +++ b/tests/component/task/channels/test_co_channel.py @@ -98,3 +98,12 @@ def test___task___add_co_pulse_chan_time___sets_channel_attributes( assert chan.co_pulse_time_initial_delay == initial_delay assert chan.co_pulse_low_time == low_time assert chan.co_pulse_high_time == high_time + + +def test___task___add_co_chans___sets_channel_name( + task: Task, + sim_6363_device: Device, +) -> None: + chan: COChannel = task.co_channels.add_co_pulse_chan_freq(sim_6363_device.co_physical_chans[0].name, name_to_assign_to_channel="myChan") + + assert chan.name == "myChan" \ No newline at end of file diff --git a/tests/component/task/channels/test_di_channel.py b/tests/component/task/channels/test_di_channel.py index e12d71c0a..f702d111b 100644 --- a/tests/component/task/channels/test_di_channel.py +++ b/tests/component/task/channels/test_di_channel.py @@ -2,7 +2,7 @@ from nidaqmx import Task from nidaqmx.constants import ChannelType, LineGrouping -from nidaqmx.system import Device +from nidaqmx.system import Device, System from nidaqmx.task.channels import DIChannel from nidaqmx.utils import flatten_channel_string @@ -44,3 +44,67 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( for chan in chans: assert chan.chan_type == ChannelType.DIGITAL_INPUT assert chan.di_num_lines == 1 + + +@pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) +@pytest.mark.parametrize( + "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name, xfail_if_old", + [ + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "", False, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "", False, False), + (["port0/line0", "port0/line1"], LineGrouping.CHAN_PER_LINE, "", "port0/line0:1", True, False), + (["port0/line0", "port0/line1"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0...", True, False), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False, False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), + # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're using gRPC (which we skip entirely here) + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "", False, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "", False, True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False, True), + (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan", False, True), + (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, " myFirstChan , mySecondChan ", "myFirstChan, mySecondChan", False, True), + (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan, , mySecondChan", "myFirstChan, mySecondChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan0:6", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan2:5,mySecondChan34", "myFirstChan2:5, mySecondChan34:37", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:9", "myFirstChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myFirstChan0:9", "myFirstChan0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:6, mySecondChan, myThirdChan", "myFirstChan0:6, mySecondChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1:7", False, True), + ], +) +def test___task___add_di_chans___sets_channel_name( + task: Task, + sim_6363_device: Device, + phys_chan_list: list[str], + line_grouping: LineGrouping, + name_to_assign_to_lines: str, + expected_virtual_channel_name: str, + qualify_expected_virtual_channel_name: bool, + xfail_if_old: bool, +) -> None: + if xfail_if_old and System.local().driver_version < (24, 5, 0): + pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") + + qualified_physical_channel_name = flatten_channel_string([f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list]) + chan: DIChannel = task.di_channels.add_di_chan( + qualified_physical_channel_name, + line_grouping=line_grouping, + name_to_assign_to_lines=name_to_assign_to_lines,) + + if qualify_expected_virtual_channel_name: + expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" + + if expected_virtual_channel_name: + assert chan.name == expected_virtual_channel_name + else: + assert chan.name == qualified_physical_channel_name \ No newline at end of file diff --git a/tests/component/task/channels/test_do_channel.py b/tests/component/task/channels/test_do_channel.py index f9fc1249a..0b99f9535 100644 --- a/tests/component/task/channels/test_do_channel.py +++ b/tests/component/task/channels/test_do_channel.py @@ -2,7 +2,7 @@ from nidaqmx import Task from nidaqmx.constants import ChannelType, LineGrouping -from nidaqmx.system import Device +from nidaqmx.system import Device, System from nidaqmx.task.channels import DOChannel from nidaqmx.utils import flatten_channel_string @@ -44,3 +44,67 @@ def test___task___add_do_chan_chan_per_line___sets_channel_attributes( for chan in chans: assert chan.chan_type == ChannelType.DIGITAL_OUTPUT assert chan.do_num_lines == 1 + + +@pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) +@pytest.mark.parametrize( + "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name, xfail_if_old", + [ + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "", False, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "", False, False), + (["port0/line0", "port0/line1"], LineGrouping.CHAN_PER_LINE, "", "port0/line0:1", True, False), + (["port0/line0", "port0/line1"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0...", True, False), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False, False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), + # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're using gRPC (which we skip entirely here) + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "", False, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "", False, True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False, True), + (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan", False, True), + (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, " myFirstChan , mySecondChan ", "myFirstChan, mySecondChan", False, True), + (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan, , mySecondChan", "myFirstChan, mySecondChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan0:6", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan2:5,mySecondChan34", "myFirstChan2:5, mySecondChan34:37", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:9", "myFirstChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myFirstChan0:9", "myFirstChan0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:6, mySecondChan, myThirdChan", "myFirstChan0:6, mySecondChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1:7", False, True), + ], +) +def test___task___add_do_chans___sets_channel_name( + task: Task, + sim_6363_device: Device, + phys_chan_list: list[str], + line_grouping: LineGrouping, + name_to_assign_to_lines: str, + expected_virtual_channel_name: str, + qualify_expected_virtual_channel_name: bool, + xfail_if_old: bool, +) -> None: + if xfail_if_old and System.local().driver_version < (24, 5, 0): + pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") + + qualified_physical_channel_name = flatten_channel_string([f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list]) + chan: DOChannel = task.do_channels.add_do_chan( + qualified_physical_channel_name, + line_grouping=line_grouping, + name_to_assign_to_lines=name_to_assign_to_lines,) + + if qualify_expected_virtual_channel_name: + expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" + + if expected_virtual_channel_name: + assert chan.name == expected_virtual_channel_name + else: + assert chan.name == qualified_physical_channel_name From 44cd4c3107dadb5bbb44dfe357af692bf7e24908 Mon Sep 17 00:00:00 2001 From: Zach Hindes Date: Wed, 21 May 2025 13:05:38 -0500 Subject: [PATCH 2/5] cleanup --- .../task/channels/test_ai_channel.py | 4 +- .../task/channels/test_ci_channel.py | 1 + .../task/channels/test_co_channel.py | 1 + .../task/channels/test_di_channel.py | 19 +++--- .../task/channels/test_do_channel.py | 61 +++---------------- 5 files changed, 21 insertions(+), 65 deletions(-) diff --git a/tests/component/task/channels/test_ai_channel.py b/tests/component/task/channels/test_ai_channel.py index e6659e3bb..18e29c81b 100644 --- a/tests/component/task/channels/test_ai_channel.py +++ b/tests/component/task/channels/test_ai_channel.py @@ -36,6 +36,7 @@ from nidaqmx.errors import DaqError from nidaqmx.system import Device, System from nidaqmx.task.channels import AIChannel +from nidaqmx.utils import unflatten_channel_string from tests.helpers import configure_teds @@ -1296,6 +1297,7 @@ def test___task___add_teds_ai_voltage_chan_with_excit___raises_teds_sensor_not_d assert exc_info.value.error_code == DAQmxErrors.TEDS_SENSOR_NOT_DETECTED +# For more extensive virtual channel name testing, refer to test_di_channel.py @pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) def test___task___add_ai_chans___sets_channel_name( task: Task, @@ -1306,4 +1308,4 @@ def test___task___add_ai_chans___sets_channel_name( chan: AIChannel = task.ai_channels.add_ai_voltage_chan(f"{sim_6363_device.name}/ai0:3", name_to_assign_to_channel="myChan09") - assert chan.name == "myChan09:11" \ No newline at end of file + assert unflatten_channel_string(chan.name) == unflatten_channel_string("myChan09:12") \ No newline at end of file diff --git a/tests/component/task/channels/test_ci_channel.py b/tests/component/task/channels/test_ci_channel.py index 7a8dc82a2..0b1c0cc5b 100644 --- a/tests/component/task/channels/test_ci_channel.py +++ b/tests/component/task/channels/test_ci_channel.py @@ -385,6 +385,7 @@ def test___task___add_ci_two_edge_sep_chan___sets_channel_attributes( assert chan.ci_two_edge_sep_second_edge == second_edge +# For more extensive virtual channel name testing, refer to test_di_channel.py def test___task___add_ci_chans___sets_channel_name( task: Task, sim_6363_device: Device, diff --git a/tests/component/task/channels/test_co_channel.py b/tests/component/task/channels/test_co_channel.py index d692c691a..f918c86e2 100644 --- a/tests/component/task/channels/test_co_channel.py +++ b/tests/component/task/channels/test_co_channel.py @@ -100,6 +100,7 @@ def test___task___add_co_pulse_chan_time___sets_channel_attributes( assert chan.co_pulse_high_time == high_time +# For more extensive virtual channel name testing, refer to test_di_channel.py def test___task___add_co_chans___sets_channel_name( task: Task, sim_6363_device: Device, diff --git a/tests/component/task/channels/test_di_channel.py b/tests/component/task/channels/test_di_channel.py index f702d111b..e01f3aea3 100644 --- a/tests/component/task/channels/test_di_channel.py +++ b/tests/component/task/channels/test_di_channel.py @@ -4,7 +4,7 @@ from nidaqmx.constants import ChannelType, LineGrouping from nidaqmx.system import Device, System from nidaqmx.task.channels import DIChannel -from nidaqmx.utils import flatten_channel_string +from nidaqmx.utils import flatten_channel_string, unflatten_channel_string @pytest.mark.parametrize( @@ -50,8 +50,8 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( @pytest.mark.parametrize( "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name, xfail_if_old", [ - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "", False, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "", False, False), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "port0/line0", True, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0", True, False), (["port0/line0", "port0/line1"], LineGrouping.CHAN_PER_LINE, "", "port0/line0:1", True, False), (["port0/line0", "port0/line1"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0...", True, False), (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), @@ -60,8 +60,8 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're using gRPC (which we skip entirely here) - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "", False, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "", False, True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "port0/line0", True, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "port0/line0", True, True), (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), @@ -79,7 +79,7 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:9", "myFirstChan0:7", False, True), (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myFirstChan0:9", "myFirstChan0", False, True), (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:6, mySecondChan, myThirdChan", "myFirstChan0:6, mySecondChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1", False, True), ], ) def test___task___add_di_chans___sets_channel_name( @@ -101,10 +101,9 @@ def test___task___add_di_chans___sets_channel_name( line_grouping=line_grouping, name_to_assign_to_lines=name_to_assign_to_lines,) + # If a user doesn't specify a virtual channel name, DAQmx will use the physical channel name, which we now need to + # fully quality. if qualify_expected_virtual_channel_name: expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" - if expected_virtual_channel_name: - assert chan.name == expected_virtual_channel_name - else: - assert chan.name == qualified_physical_channel_name \ No newline at end of file + assert unflatten_channel_string(chan.name) == unflatten_channel_string(expected_virtual_channel_name) \ No newline at end of file diff --git a/tests/component/task/channels/test_do_channel.py b/tests/component/task/channels/test_do_channel.py index 0b99f9535..60d734352 100644 --- a/tests/component/task/channels/test_do_channel.py +++ b/tests/component/task/channels/test_do_channel.py @@ -4,7 +4,7 @@ from nidaqmx.constants import ChannelType, LineGrouping from nidaqmx.system import Device, System from nidaqmx.task.channels import DOChannel -from nidaqmx.utils import flatten_channel_string +from nidaqmx.utils import flatten_channel_string, unflatten_channel_string @pytest.mark.parametrize( @@ -46,65 +46,18 @@ def test___task___add_do_chan_chan_per_line___sets_channel_attributes( assert chan.do_num_lines == 1 +# For more extensive virtual channel name testing, refer to test_di_channel.py @pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) -@pytest.mark.parametrize( - "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name, xfail_if_old", - [ - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "", False, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "", False, False), - (["port0/line0", "port0/line1"], LineGrouping.CHAN_PER_LINE, "", "port0/line0:1", True, False), - (["port0/line0", "port0/line1"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0...", True, False), - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False, False), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), - # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're using gRPC (which we skip entirely here) - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "", False, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "", False, True), - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False, True), - (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan", False, True), - (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, " myFirstChan , mySecondChan ", "myFirstChan, mySecondChan", False, True), - (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan, , mySecondChan", "myFirstChan, mySecondChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan0:6", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan2:5,mySecondChan34", "myFirstChan2:5, mySecondChan34:37", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:9", "myFirstChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myFirstChan0:9", "myFirstChan0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:6, mySecondChan, myThirdChan", "myFirstChan0:6, mySecondChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1:7", False, True), - ], -) def test___task___add_do_chans___sets_channel_name( task: Task, sim_6363_device: Device, - phys_chan_list: list[str], - line_grouping: LineGrouping, - name_to_assign_to_lines: str, - expected_virtual_channel_name: str, - qualify_expected_virtual_channel_name: bool, - xfail_if_old: bool, ) -> None: - if xfail_if_old and System.local().driver_version < (24, 5, 0): + if System.local().driver_version < (24, 5, 0): pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") - qualified_physical_channel_name = flatten_channel_string([f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list]) chan: DOChannel = task.do_channels.add_do_chan( - qualified_physical_channel_name, - line_grouping=line_grouping, - name_to_assign_to_lines=name_to_assign_to_lines,) - - if qualify_expected_virtual_channel_name: - expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" + f"{sim_6363_device.name}/port0/line0:7", + line_grouping=LineGrouping.CHAN_PER_LINE, + name_to_assign_to_lines="myChan0") - if expected_virtual_channel_name: - assert chan.name == expected_virtual_channel_name - else: - assert chan.name == qualified_physical_channel_name + assert unflatten_channel_string(chan.name) == unflatten_channel_string("myChan0:7") From 87da4728cd4bfb9989cad6e13f6c66dee94596c4 Mon Sep 17 00:00:00 2001 From: Zach Hindes Date: Wed, 21 May 2025 13:10:56 -0500 Subject: [PATCH 3/5] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de5e911a8..e378fee00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to this project will be documented in this file. * ### Resolved Issues * Fix PEP 660 builds by setting `build-system` to use `poetry-core` + * [579: nidaqmx does not generate numbered virtual channel names correctly](https://github.com/ni/nidaqmx-python/issues/579) * ### Major Changes * Removed the `docs` extra and converted it to a Poetry dependency group. From a245ec874ee1155943da5dfc5ba291b2e7f90a02 Mon Sep 17 00:00:00 2001 From: Zach Hindes Date: Wed, 21 May 2025 13:20:20 -0500 Subject: [PATCH 4/5] lint --- .../task/channels/test_ai_channel.py | 10 +- .../task/channels/test_ci_channel.py | 6 +- .../task/channels/test_co_channel.py | 6 +- .../task/channels/test_di_channel.py | 157 ++++++++++++++---- .../task/channels/test_do_channel.py | 7 +- 5 files changed, 141 insertions(+), 45 deletions(-) diff --git a/tests/component/task/channels/test_ai_channel.py b/tests/component/task/channels/test_ai_channel.py index 18e29c81b..280e82c7f 100644 --- a/tests/component/task/channels/test_ai_channel.py +++ b/tests/component/task/channels/test_ai_channel.py @@ -1298,7 +1298,9 @@ def test___task___add_teds_ai_voltage_chan_with_excit___raises_teds_sensor_not_d # For more extensive virtual channel name testing, refer to test_di_channel.py -@pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) +@pytest.mark.library_only( + reason="Internal method we use for retrieving a channel name isn't supported on gRPC", +) def test___task___add_ai_chans___sets_channel_name( task: Task, sim_6363_device: Device, @@ -1306,6 +1308,8 @@ def test___task___add_ai_chans___sets_channel_name( if System.local().driver_version < (24, 5, 0): pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") - chan: AIChannel = task.ai_channels.add_ai_voltage_chan(f"{sim_6363_device.name}/ai0:3", name_to_assign_to_channel="myChan09") + chan: AIChannel = task.ai_channels.add_ai_voltage_chan( + f"{sim_6363_device.name}/ai0:3", name_to_assign_to_channel="myChan09" + ) - assert unflatten_channel_string(chan.name) == unflatten_channel_string("myChan09:12") \ No newline at end of file + assert unflatten_channel_string(chan.name) == unflatten_channel_string("myChan09:12") diff --git a/tests/component/task/channels/test_ci_channel.py b/tests/component/task/channels/test_ci_channel.py index 0b1c0cc5b..9277fa181 100644 --- a/tests/component/task/channels/test_ci_channel.py +++ b/tests/component/task/channels/test_ci_channel.py @@ -390,6 +390,8 @@ def test___task___add_ci_chans___sets_channel_name( task: Task, sim_6363_device: Device, ) -> None: - chan: CIChannel = task.ci_channels.add_ci_count_edges_chan(sim_6363_device.ci_physical_chans[0].name, name_to_assign_to_channel="myChan") + chan: CIChannel = task.ci_channels.add_ci_count_edges_chan( + sim_6363_device.ci_physical_chans[0].name, name_to_assign_to_channel="myChan" + ) - assert chan.name == "myChan" \ No newline at end of file + assert chan.name == "myChan" diff --git a/tests/component/task/channels/test_co_channel.py b/tests/component/task/channels/test_co_channel.py index f918c86e2..7e13c21f9 100644 --- a/tests/component/task/channels/test_co_channel.py +++ b/tests/component/task/channels/test_co_channel.py @@ -105,6 +105,8 @@ def test___task___add_co_chans___sets_channel_name( task: Task, sim_6363_device: Device, ) -> None: - chan: COChannel = task.co_channels.add_co_pulse_chan_freq(sim_6363_device.co_physical_chans[0].name, name_to_assign_to_channel="myChan") + chan: COChannel = task.co_channels.add_co_pulse_chan_freq( + sim_6363_device.co_physical_chans[0].name, name_to_assign_to_channel="myChan" + ) - assert chan.name == "myChan" \ No newline at end of file + assert chan.name == "myChan" diff --git a/tests/component/task/channels/test_di_channel.py b/tests/component/task/channels/test_di_channel.py index e01f3aea3..f0248b591 100644 --- a/tests/component/task/channels/test_di_channel.py +++ b/tests/component/task/channels/test_di_channel.py @@ -46,40 +46,120 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( assert chan.di_num_lines == 1 -@pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) +@pytest.mark.library_only( + reason="Internal method we use for retrieving a channel name isn't supported on gRPC", +) @pytest.mark.parametrize( "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name, xfail_if_old", [ - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "port0/line0", True, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0", True, False), - (["port0/line0", "port0/line1"], LineGrouping.CHAN_PER_LINE, "", "port0/line0:1", True, False), - (["port0/line0", "port0/line1"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0...", True, False), - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False, False), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), - # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're using gRPC (which we skip entirely here) - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "port0/line0", True, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "port0/line0", True, True), - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False, True), - (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan", False, True), - (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, " myFirstChan , mySecondChan ", "myFirstChan, mySecondChan", False, True), - (["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan, , mySecondChan", "myFirstChan, mySecondChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan0:6", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan2:5,mySecondChan34", "myFirstChan2:5, mySecondChan34:37", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:9", "myFirstChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myFirstChan0:9", "myFirstChan0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:6, mySecondChan, myThirdChan", "myFirstChan0:6, mySecondChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1", False, True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "port0/line0", True, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0", True, False), + ( + ["port0/line0", "port0/line1"], + LineGrouping.CHAN_PER_LINE, + "", + "port0/line0:1", + True, + False, + ), + ( + ["port0/line0", "port0/line1"], + LineGrouping.CHAN_FOR_ALL_LINES, + "", + "port0/line0...", + True, + False, + ), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False, False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), + # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're + # using gRPC (which we skip entirely here) + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "port0/line0", True, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "port0/line0", True, True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False, True), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False, True), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False, True), + ( + ["port0/line0:1"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan,mySecondChan", + "myFirstChan, mySecondChan", + False, + True, + ), + ( + ["port0/line0:1"], + LineGrouping.CHAN_PER_LINE, + " myFirstChan , mySecondChan ", + "myFirstChan, mySecondChan", + False, + True, + ), + ( + ["port0/line0:1"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan, , mySecondChan", + "myFirstChan, mySecondChan", + False, + True, + ), + ( + ["port0/line0:7"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan,mySecondChan", + "myFirstChan, mySecondChan0:6", + False, + True, + ), + ( + ["port0/line0:7"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan2:5,mySecondChan34", + "myFirstChan2:5, mySecondChan34:37", + False, + True, + ), + ( + ["port0/line0:7"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan0:9", + "myFirstChan0:7", + False, + True, + ), + ( + ["port0/line0:7"], + LineGrouping.CHAN_FOR_ALL_LINES, + "myFirstChan0:9", + "myFirstChan0", + False, + True, + ), + ( + ["port0/line0:7"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan0:6, mySecondChan, myThirdChan", + "myFirstChan0:6, mySecondChan", + False, + True, + ), + ( + ["port0/line0:7"], + LineGrouping.CHAN_PER_LINE, + "myFirstChan0:5, mySecondChan0:5", + "myFirstChan0:5, mySecondChan0:1", + False, + True, + ), ], ) def test___task___add_di_chans___sets_channel_name( @@ -95,15 +175,20 @@ def test___task___add_di_chans___sets_channel_name( if xfail_if_old and System.local().driver_version < (24, 5, 0): pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") - qualified_physical_channel_name = flatten_channel_string([f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list]) + qualified_physical_channel_name = flatten_channel_string( + [f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list] + ) chan: DIChannel = task.di_channels.add_di_chan( qualified_physical_channel_name, line_grouping=line_grouping, - name_to_assign_to_lines=name_to_assign_to_lines,) + name_to_assign_to_lines=name_to_assign_to_lines, + ) - # If a user doesn't specify a virtual channel name, DAQmx will use the physical channel name, which we now need to - # fully quality. + # If a user doesn't specify a virtual channel name, DAQmx will use the physical channel name, + # which we now need to fully quality. if qualify_expected_virtual_channel_name: expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" - assert unflatten_channel_string(chan.name) == unflatten_channel_string(expected_virtual_channel_name) \ No newline at end of file + assert unflatten_channel_string(chan.name) == unflatten_channel_string( + expected_virtual_channel_name + ) diff --git a/tests/component/task/channels/test_do_channel.py b/tests/component/task/channels/test_do_channel.py index 60d734352..9f6af72e1 100644 --- a/tests/component/task/channels/test_do_channel.py +++ b/tests/component/task/channels/test_do_channel.py @@ -47,7 +47,9 @@ def test___task___add_do_chan_chan_per_line___sets_channel_attributes( # For more extensive virtual channel name testing, refer to test_di_channel.py -@pytest.mark.library_only(reason="Internal method we use for retrieving a channel name isn't supported on gRPC",) +@pytest.mark.library_only( + reason="Internal method we use for retrieving a channel name isn't supported on gRPC", +) def test___task___add_do_chans___sets_channel_name( task: Task, sim_6363_device: Device, @@ -58,6 +60,7 @@ def test___task___add_do_chans___sets_channel_name( chan: DOChannel = task.do_channels.add_do_chan( f"{sim_6363_device.name}/port0/line0:7", line_grouping=LineGrouping.CHAN_PER_LINE, - name_to_assign_to_lines="myChan0") + name_to_assign_to_lines="myChan0", + ) assert unflatten_channel_string(chan.name) == unflatten_channel_string("myChan0:7") From 200b5da370b4c191efcbfe7f824a1ff071f504b1 Mon Sep 17 00:00:00 2001 From: Zach Hindes Date: Thu, 22 May 2025 10:27:19 -0500 Subject: [PATCH 5/5] cleanup testing --- .../task/channels/test_ai_channel.py | 13 +- .../task/channels/test_ci_channel.py | 2 +- .../task/channels/test_co_channel.py | 2 +- .../task/channels/test_di_channel.py | 144 +++++++++++------- .../task/channels/test_do_channel.py | 11 +- 5 files changed, 103 insertions(+), 69 deletions(-) diff --git a/tests/component/task/channels/test_ai_channel.py b/tests/component/task/channels/test_ai_channel.py index 280e82c7f..8ac3c3f2b 100644 --- a/tests/component/task/channels/test_ai_channel.py +++ b/tests/component/task/channels/test_ai_channel.py @@ -1298,16 +1298,17 @@ def test___task___add_teds_ai_voltage_chan_with_excit___raises_teds_sensor_not_d # For more extensive virtual channel name testing, refer to test_di_channel.py -@pytest.mark.library_only( - reason="Internal method we use for retrieving a channel name isn't supported on gRPC", +@pytest.mark.skipif( + System.local().driver_version < (24, 5, 0), + reason="The fix for this test requires DAQmx 24.5.0 and later", ) -def test___task___add_ai_chans___sets_channel_name( +@pytest.mark.grpc_xfail( + reason="The fix for this test isn't supported on gRPC", +) +def test___task___add_ai_chans_with_name___sets_channel_name( task: Task, sim_6363_device: Device, ) -> None: - if System.local().driver_version < (24, 5, 0): - pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") - chan: AIChannel = task.ai_channels.add_ai_voltage_chan( f"{sim_6363_device.name}/ai0:3", name_to_assign_to_channel="myChan09" ) diff --git a/tests/component/task/channels/test_ci_channel.py b/tests/component/task/channels/test_ci_channel.py index 9277fa181..ac16c72d3 100644 --- a/tests/component/task/channels/test_ci_channel.py +++ b/tests/component/task/channels/test_ci_channel.py @@ -386,7 +386,7 @@ def test___task___add_ci_two_edge_sep_chan___sets_channel_attributes( # For more extensive virtual channel name testing, refer to test_di_channel.py -def test___task___add_ci_chans___sets_channel_name( +def test___task___add_ci_chans_with_name___sets_channel_name( task: Task, sim_6363_device: Device, ) -> None: diff --git a/tests/component/task/channels/test_co_channel.py b/tests/component/task/channels/test_co_channel.py index 7e13c21f9..7ed31a500 100644 --- a/tests/component/task/channels/test_co_channel.py +++ b/tests/component/task/channels/test_co_channel.py @@ -101,7 +101,7 @@ def test___task___add_co_pulse_chan_time___sets_channel_attributes( # For more extensive virtual channel name testing, refer to test_di_channel.py -def test___task___add_co_chans___sets_channel_name( +def test___task___add_co_chans_with_name___sets_channel_name( task: Task, sim_6363_device: Device, ) -> None: diff --git a/tests/component/task/channels/test_di_channel.py b/tests/component/task/channels/test_di_channel.py index f0248b591..0b7352489 100644 --- a/tests/component/task/channels/test_di_channel.py +++ b/tests/component/task/channels/test_di_channel.py @@ -46,21 +46,45 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( assert chan.di_num_lines == 1 -@pytest.mark.library_only( - reason="Internal method we use for retrieving a channel name isn't supported on gRPC", -) +def _test___task___add_di_chans_with_name___sets_channel_name( + task: Task, + sim_6363_device: Device, + phys_chan_list: list[str], + line_grouping: LineGrouping, + name_to_assign_to_lines: str, + expected_virtual_channel_name: str, + qualify_expected_virtual_channel_name: bool, +) -> None: + qualified_physical_channel_name = flatten_channel_string( + [f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list] + ) + chan: DIChannel = task.di_channels.add_di_chan( + qualified_physical_channel_name, + line_grouping=line_grouping, + name_to_assign_to_lines=name_to_assign_to_lines, + ) + + # If a user doesn't specify a virtual channel name, DAQmx will use the physical channel name, + # which we now need to fully quality. + if qualify_expected_virtual_channel_name: + expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" + + assert unflatten_channel_string(chan.name) == unflatten_channel_string( + expected_virtual_channel_name + ) + + @pytest.mark.parametrize( - "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name, xfail_if_old", + "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name", [ - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "port0/line0", True, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0", True, False), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "", "port0/line0", True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "", "port0/line0", True), ( ["port0/line0", "port0/line1"], LineGrouping.CHAN_PER_LINE, "", "port0/line0:1", True, - False, ), ( ["port0/line0", "port0/line1"], @@ -68,33 +92,61 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "", "port0/line0...", True, - False, ), - (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False, False), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False, False), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False, False), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False, False), - # All of these tests cases will fail if the driver version is older than 24.5.0 or if you're - # using gRPC (which we skip entirely here) - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "port0/line0", True, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "port0/line0", True, True), - (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False, True), - (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False, True), - (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False, True), - (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False, True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan", False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan", "myChan0:7", False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan", "myChan", False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, "myChan0", "myChan0", False), + ], +) +def test___task___add_di_chans_with_simple_name___sets_channel_name( + task: Task, + sim_6363_device: Device, + phys_chan_list: list[str], + line_grouping: LineGrouping, + name_to_assign_to_lines: str, + expected_virtual_channel_name: str, + qualify_expected_virtual_channel_name: bool, +) -> None: + _test___task___add_di_chans_with_name___sets_channel_name( + task, + sim_6363_device, + phys_chan_list, + line_grouping, + name_to_assign_to_lines, + expected_virtual_channel_name, + qualify_expected_virtual_channel_name, + ) + + +@pytest.mark.skipif( + System.local().driver_version < (24, 5, 0), + reason="The fix for this test requires DAQmx 24.5.0 and later", +) +@pytest.mark.grpc_xfail( + reason="The fix for this test isn't supported on gRPC", +) +@pytest.mark.parametrize( + "phys_chan_list, line_grouping, name_to_assign_to_lines, expected_virtual_channel_name, qualify_expected_virtual_channel_name", + [ + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " ", "port0/line0", True), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " ", "port0/line0", True), + (["port0/line0"], LineGrouping.CHAN_PER_LINE, " myChan ", "myChan", False), + (["port0/line0"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan ", "myChan", False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan0", "myChan0:7", False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan0 ", "myChan0:7", False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan0 ", "myChan0", False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, " myChan 0 ", "myChan0:7", False), + (["port0/line0:7"], LineGrouping.CHAN_FOR_ALL_LINES, " myChan 0 ", "myChan 0", False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan8", "myChan8:15", False), + (["port0/line0:7"], LineGrouping.CHAN_PER_LINE, "myChan008", "myChan008:015", False), ( ["port0/line0:1"], LineGrouping.CHAN_PER_LINE, "myFirstChan,mySecondChan", "myFirstChan, mySecondChan", False, - True, ), ( ["port0/line0:1"], @@ -102,7 +154,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( " myFirstChan , mySecondChan ", "myFirstChan, mySecondChan", False, - True, ), ( ["port0/line0:1"], @@ -110,7 +161,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan, , mySecondChan", "myFirstChan, mySecondChan", False, - True, ), ( ["port0/line0:7"], @@ -118,7 +168,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan,mySecondChan", "myFirstChan, mySecondChan0:6", False, - True, ), ( ["port0/line0:7"], @@ -126,7 +175,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan2:5,mySecondChan34", "myFirstChan2:5, mySecondChan34:37", False, - True, ), ( ["port0/line0:7"], @@ -134,7 +182,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan0:9", "myFirstChan0:7", False, - True, ), ( ["port0/line0:7"], @@ -142,7 +189,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan0:9", "myFirstChan0", False, - True, ), ( ["port0/line0:7"], @@ -150,7 +196,6 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan0:6, mySecondChan, myThirdChan", "myFirstChan0:6, mySecondChan", False, - True, ), ( ["port0/line0:7"], @@ -158,11 +203,10 @@ def test___task___add_di_chan_chan_per_line___sets_channel_attributes( "myFirstChan0:5, mySecondChan0:5", "myFirstChan0:5, mySecondChan0:1", False, - True, ), ], ) -def test___task___add_di_chans___sets_channel_name( +def test___task___add_di_chans_with_complex_name___sets_channel_name( task: Task, sim_6363_device: Device, phys_chan_list: list[str], @@ -170,25 +214,13 @@ def test___task___add_di_chans___sets_channel_name( name_to_assign_to_lines: str, expected_virtual_channel_name: str, qualify_expected_virtual_channel_name: bool, - xfail_if_old: bool, ) -> None: - if xfail_if_old and System.local().driver_version < (24, 5, 0): - pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") - - qualified_physical_channel_name = flatten_channel_string( - [f"{sim_6363_device.name}/{chan}" for chan in phys_chan_list] - ) - chan: DIChannel = task.di_channels.add_di_chan( - qualified_physical_channel_name, - line_grouping=line_grouping, - name_to_assign_to_lines=name_to_assign_to_lines, - ) - - # If a user doesn't specify a virtual channel name, DAQmx will use the physical channel name, - # which we now need to fully quality. - if qualify_expected_virtual_channel_name: - expected_virtual_channel_name = f"{sim_6363_device.name}/{expected_virtual_channel_name}" - - assert unflatten_channel_string(chan.name) == unflatten_channel_string( - expected_virtual_channel_name + _test___task___add_di_chans_with_name___sets_channel_name( + task, + sim_6363_device, + phys_chan_list, + line_grouping, + name_to_assign_to_lines, + expected_virtual_channel_name, + qualify_expected_virtual_channel_name, ) diff --git a/tests/component/task/channels/test_do_channel.py b/tests/component/task/channels/test_do_channel.py index 9f6af72e1..3698c9ce1 100644 --- a/tests/component/task/channels/test_do_channel.py +++ b/tests/component/task/channels/test_do_channel.py @@ -47,16 +47,17 @@ def test___task___add_do_chan_chan_per_line___sets_channel_attributes( # For more extensive virtual channel name testing, refer to test_di_channel.py +@pytest.mark.skipif( + System.local().driver_version < (24, 5, 0), + reason="The fix for this test requires DAQmx 24.5.0 and later", +) @pytest.mark.library_only( - reason="Internal method we use for retrieving a channel name isn't supported on gRPC", + reason="The fix for this test isn't supported on gRPC", ) -def test___task___add_do_chans___sets_channel_name( +def test___task___add_do_chans_with_name___sets_channel_name( task: Task, sim_6363_device: Device, ) -> None: - if System.local().driver_version < (24, 5, 0): - pytest.xfail("The fix for this test requires DAQmx 24.5.0 and later") - chan: DOChannel = task.do_channels.add_do_chan( f"{sim_6363_device.name}/port0/line0:7", line_grouping=LineGrouping.CHAN_PER_LINE,