Skip to content

Commit 1b59ca3

Browse files
authored
fix virtual channel name logic with library interpreter and DAQmx 24.5+ (#723)
* fix fallback implementation (used in older driver versions and gRPC) in some cases
1 parent 4c3211e commit 1b59ca3

24 files changed

+592
-145
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ All notable changes to this project will be documented in this file.
2929

3030
* ### Resolved Issues
3131
* Fix PEP 660 builds by setting `build-system` to use `poetry-core`
32+
* [579: nidaqmx does not generate numbered virtual channel names correctly](https://github.com/ni/nidaqmx-python/issues/579)
3233

3334
* ### Major Changes
3435
* Removed the `docs` extra and converted it to a Poetry dependency group.

generated/nidaqmx/_base_interpreter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,10 @@ def get_write_attribute_uint32(self, task, attribute):
11681168
def get_write_attribute_uint64(self, task, attribute):
11691169
raise NotImplementedError
11701170

1171+
@abc.abstractmethod
1172+
def internal_get_last_created_chan(self):
1173+
raise NotImplementedError
1174+
11711175
@abc.abstractmethod
11721176
def is_task_done(self, task):
11731177
raise NotImplementedError

generated/nidaqmx/_grpc_interpreter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3597,6 +3597,9 @@ def set_runtime_environment(
35973597
self, environment, environment_version, reserved_1, reserved_2):
35983598
raise NotImplementedError
35993599

3600+
def internal_get_last_created_chan(self):
3601+
raise NotImplementedError
3602+
36003603

36013604
def _assign_numpy_array(numpy_array, grpc_array):
36023605
"""

generated/nidaqmx/_library_interpreter.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4048,6 +4048,30 @@ def get_write_attribute_uint64(self, task, attribute):
40484048
self.check_for_error(error_code)
40494049
return value.value
40504050

4051+
def internal_get_last_created_chan(self):
4052+
cfunc = lib_importer.windll.DAQmxInternalGetLastCreatedChan
4053+
if cfunc.argtypes is None:
4054+
with cfunc.arglock:
4055+
if cfunc.argtypes is None:
4056+
cfunc.argtypes = [
4057+
ctypes.c_char_p, ctypes.c_uint]
4058+
4059+
temp_size = 0
4060+
while True:
4061+
value = ctypes.create_string_buffer(temp_size)
4062+
size_or_code = cfunc(
4063+
value, temp_size)
4064+
if is_string_buffer_too_small(size_or_code):
4065+
# Buffer size must have changed between calls; check again.
4066+
temp_size = 0
4067+
elif size_or_code > 0 and temp_size == 0:
4068+
# Buffer size obtained, use to retrieve data.
4069+
temp_size = size_or_code
4070+
else:
4071+
break
4072+
self.check_for_error(size_or_code)
4073+
return value.value.decode(lib_importer.encoding)
4074+
40514075
def is_task_done(self, task):
40524076
is_task_done = c_bool32()
40534077

generated/nidaqmx/task/collections/_ai_channel_collection.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import numpy
44

5+
from nidaqmx.errors import DaqFunctionNotSupportedError
56
from nidaqmx.task.channels._ai_channel import AIChannel
67
from nidaqmx.task.collections._channel_collection import ChannelCollection
78
from nidaqmx.utils import unflatten_channel_string
@@ -45,18 +46,28 @@ def _create_chan(self, physical_channel, name_to_assign_to_channel=''):
4546
4647
Specifies the newly created AIChannel object.
4748
"""
48-
if name_to_assign_to_channel:
49-
num_channels = len(unflatten_channel_string(physical_channel))
50-
51-
if num_channels > 1:
52-
name = '{}0:{}'.format(
53-
name_to_assign_to_channel, num_channels-1)
49+
# Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library
50+
# interpreter.
51+
virtual_channel_name = None
52+
try:
53+
virtual_channel_name = self._interpreter.internal_get_last_created_chan()
54+
except (NotImplementedError, DaqFunctionNotSupportedError):
55+
pass
56+
57+
# Fallback implementation is sometimes incorrect.
58+
if virtual_channel_name is None:
59+
if name_to_assign_to_channel:
60+
num_channels = len(unflatten_channel_string(physical_channel))
61+
62+
if num_channels > 1:
63+
virtual_channel_name = '{}0:{}'.format(
64+
name_to_assign_to_channel, num_channels-1)
65+
else:
66+
virtual_channel_name = name_to_assign_to_channel
5467
else:
55-
name = name_to_assign_to_channel
56-
else:
57-
name = physical_channel
68+
virtual_channel_name = physical_channel
5869

59-
return AIChannel(self._handle, name, self._interpreter)
70+
return AIChannel(self._handle, virtual_channel_name, self._interpreter)
6071

6172
def add_ai_accel_4_wire_dc_voltage_chan(
6273
self, physical_channel, name_to_assign_to_channel="",

generated/nidaqmx/task/collections/_ao_channel_collection.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Do not edit this file; it was automatically generated.
22

3+
from nidaqmx.errors import DaqFunctionNotSupportedError
34
from nidaqmx.task.channels._ao_channel import AOChannel
45
from nidaqmx.task.collections._channel_collection import ChannelCollection
56
from nidaqmx.utils import unflatten_channel_string
@@ -31,18 +32,28 @@ def _create_chan(self, physical_channel, name_to_assign_to_channel=''):
3132
3233
Specifies the newly created AOChannel object.
3334
"""
34-
if name_to_assign_to_channel:
35-
num_channels = len(unflatten_channel_string(physical_channel))
36-
37-
if num_channels > 1:
38-
name = '{}0:{}'.format(
39-
name_to_assign_to_channel, num_channels-1)
35+
# Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library
36+
# interpreter.
37+
virtual_channel_name = None
38+
try:
39+
virtual_channel_name = self._interpreter.internal_get_last_created_chan()
40+
except (NotImplementedError, DaqFunctionNotSupportedError):
41+
pass
42+
43+
# Fallback implementation is sometimes incorrect.
44+
if virtual_channel_name is None:
45+
if name_to_assign_to_channel:
46+
num_channels = len(unflatten_channel_string(physical_channel))
47+
48+
if num_channels > 1:
49+
virtual_channel_name = '{}0:{}'.format(
50+
name_to_assign_to_channel, num_channels-1)
51+
else:
52+
virtual_channel_name = name_to_assign_to_channel
4053
else:
41-
name = name_to_assign_to_channel
42-
else:
43-
name = physical_channel
54+
virtual_channel_name = physical_channel
4455

45-
return AOChannel(self._handle, name, self._interpreter)
56+
return AOChannel(self._handle, virtual_channel_name, self._interpreter)
4657

4758
def add_ao_current_chan(
4859
self, physical_channel, name_to_assign_to_channel="", min_val=0.0,

generated/nidaqmx/task/collections/_ci_channel_collection.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Do not edit this file; it was automatically generated.
22

3+
from nidaqmx.errors import DaqFunctionNotSupportedError
34
from nidaqmx.task.channels._ci_channel import CIChannel
45
from nidaqmx.task.collections._channel_collection import ChannelCollection
56
from nidaqmx.utils import unflatten_channel_string
@@ -33,18 +34,28 @@ def _create_chan(self, counter, name_to_assign_to_channel=''):
3334
3435
Specifies the newly created CIChannel object.
3536
"""
36-
if name_to_assign_to_channel:
37-
num_counters = len(unflatten_channel_string(counter))
38-
39-
if num_counters > 1:
40-
name = '{}0:{}'.format(
41-
name_to_assign_to_channel, num_counters-1)
37+
# Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library
38+
# interpreter.
39+
virtual_channel_name = None
40+
try:
41+
virtual_channel_name = self._interpreter.internal_get_last_created_chan()
42+
except (NotImplementedError, DaqFunctionNotSupportedError):
43+
pass
44+
45+
# Fallback implementation is sometimes incorrect.
46+
if virtual_channel_name is None:
47+
if name_to_assign_to_channel:
48+
num_counters = len(unflatten_channel_string(counter))
49+
50+
if num_counters > 1:
51+
virtual_channel_name = '{}0:{}'.format(
52+
name_to_assign_to_channel, num_counters-1)
53+
else:
54+
virtual_channel_name = name_to_assign_to_channel
4255
else:
43-
name = name_to_assign_to_channel
44-
else:
45-
name = counter
56+
virtual_channel_name = counter
4657

47-
return CIChannel(self._handle, name, self._interpreter)
58+
return CIChannel(self._handle, virtual_channel_name, self._interpreter)
4859

4960
def add_ci_ang_encoder_chan(
5061
self, counter, name_to_assign_to_channel="",

generated/nidaqmx/task/collections/_co_channel_collection.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Do not edit this file; it was automatically generated.
22

3+
from nidaqmx.errors import DaqFunctionNotSupportedError
34
from nidaqmx.task.channels._co_channel import COChannel
45
from nidaqmx.task.collections._channel_collection import ChannelCollection
56
from nidaqmx.utils import unflatten_channel_string
@@ -31,18 +32,28 @@ def _create_chan(self, counter, name_to_assign_to_channel=''):
3132
3233
Specifies the newly created COChannel object.
3334
"""
34-
if name_to_assign_to_channel:
35-
num_counters = len(unflatten_channel_string(counter))
36-
37-
if num_counters > 1:
38-
name = '{}0:{}'.format(
39-
name_to_assign_to_channel, num_counters-1)
35+
# Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library
36+
# interpreter.
37+
virtual_channel_name = None
38+
try:
39+
virtual_channel_name = self._interpreter.internal_get_last_created_chan()
40+
except (NotImplementedError, DaqFunctionNotSupportedError):
41+
pass
42+
43+
# Fallback implementation is sometimes incorrect.
44+
if virtual_channel_name is None:
45+
if name_to_assign_to_channel:
46+
num_counters = len(unflatten_channel_string(counter))
47+
48+
if num_counters > 1:
49+
virtual_channel_name = '{}0:{}'.format(
50+
name_to_assign_to_channel, num_counters-1)
51+
else:
52+
virtual_channel_name = name_to_assign_to_channel
4053
else:
41-
name = name_to_assign_to_channel
42-
else:
43-
name = counter
54+
virtual_channel_name = counter
4455

45-
return COChannel(self._handle, name, self._interpreter)
56+
return COChannel(self._handle, virtual_channel_name, self._interpreter)
4657

4758
def add_co_pulse_chan_freq(
4859
self, counter, name_to_assign_to_channel="",

generated/nidaqmx/task/collections/_di_channel_collection.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Do not edit this file; it was automatically generated.
22

3+
from nidaqmx.errors import DaqFunctionNotSupportedError
34
from nidaqmx.task.channels._di_channel import DIChannel
45
from nidaqmx.task.collections._channel_collection import ChannelCollection
56
from nidaqmx.utils import unflatten_channel_string
@@ -34,25 +35,37 @@ def _create_chan(self, lines, line_grouping, name_to_assign_to_lines=''):
3435
3536
Specifies the newly created DIChannel object.
3637
"""
37-
unflattened_lines = unflatten_channel_string(lines)
38-
num_lines = len(unflattened_lines)
39-
40-
if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES:
41-
if name_to_assign_to_lines or num_lines == 1:
42-
name = lines
43-
else:
44-
name = unflattened_lines[0] + '...'
45-
else:
46-
if name_to_assign_to_lines:
47-
if num_lines > 1:
48-
name = '{}0:{}'.format(
49-
name_to_assign_to_lines, num_lines-1)
38+
# Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library
39+
# interpreter.
40+
virtual_channel_name = None
41+
try:
42+
virtual_channel_name = self._interpreter.internal_get_last_created_chan()
43+
except (NotImplementedError, DaqFunctionNotSupportedError):
44+
pass
45+
46+
# Fallback implementation is sometimes incorrect.
47+
if virtual_channel_name is None:
48+
unflattened_lines = unflatten_channel_string(lines)
49+
num_lines = len(unflattened_lines)
50+
51+
if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES:
52+
if name_to_assign_to_lines:
53+
virtual_channel_name = name_to_assign_to_lines
54+
elif num_lines == 1:
55+
virtual_channel_name = lines
5056
else:
51-
name = name_to_assign_to_lines
57+
virtual_channel_name = unflattened_lines[0] + '...'
5258
else:
53-
name = lines
59+
if name_to_assign_to_lines:
60+
if num_lines > 1:
61+
virtual_channel_name = '{}0:{}'.format(
62+
name_to_assign_to_lines, num_lines-1)
63+
else:
64+
virtual_channel_name = name_to_assign_to_lines
65+
else:
66+
virtual_channel_name = lines
5467

55-
return DIChannel(self._handle, name, self._interpreter)
68+
return DIChannel(self._handle, virtual_channel_name, self._interpreter)
5669

5770
def add_di_chan(
5871
self, lines, name_to_assign_to_lines="",

generated/nidaqmx/task/collections/_do_channel_collection.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Do not edit this file; it was automatically generated.
22

3+
from nidaqmx.errors import DaqFunctionNotSupportedError
34
from nidaqmx.task.channels._do_channel import DOChannel
45
from nidaqmx.task.collections._channel_collection import ChannelCollection
56
from nidaqmx.utils import unflatten_channel_string
@@ -34,25 +35,37 @@ def _create_chan(self, lines, line_grouping, name_to_assign_to_lines=''):
3435
3536
Specifies the newly created DOChannel object.
3637
"""
37-
unflattened_lines = unflatten_channel_string(lines)
38-
num_lines = len(unflattened_lines)
39-
40-
if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES:
41-
if name_to_assign_to_lines or num_lines == 1:
42-
name = lines
43-
else:
44-
name = unflattened_lines[0] + '...'
45-
else:
46-
if name_to_assign_to_lines:
47-
if num_lines > 1:
48-
name = '{}0:{}'.format(
49-
name_to_assign_to_lines, num_lines-1)
38+
# Attempt to retrieve the last created channel name. This is only supported on DAQmx 24Q3+ with the library
39+
# interpreter.
40+
virtual_channel_name = None
41+
try:
42+
virtual_channel_name = self._interpreter.internal_get_last_created_chan()
43+
except (NotImplementedError, DaqFunctionNotSupportedError):
44+
pass
45+
46+
# Fallback implementation is sometimes incorrect.
47+
if virtual_channel_name is None:
48+
unflattened_lines = unflatten_channel_string(lines)
49+
num_lines = len(unflattened_lines)
50+
51+
if line_grouping == LineGrouping.CHAN_FOR_ALL_LINES:
52+
if name_to_assign_to_lines:
53+
virtual_channel_name = name_to_assign_to_lines
54+
elif num_lines == 1:
55+
virtual_channel_name = lines
5056
else:
51-
name = name_to_assign_to_lines
57+
virtual_channel_name = unflattened_lines[0] + '...'
5258
else:
53-
name = lines
59+
if name_to_assign_to_lines:
60+
if num_lines > 1:
61+
virtual_channel_name = '{}0:{}'.format(
62+
name_to_assign_to_lines, num_lines-1)
63+
else:
64+
virtual_channel_name = name_to_assign_to_lines
65+
else:
66+
virtual_channel_name = lines
5467

55-
return DOChannel(self._handle, name, self._interpreter)
68+
return DOChannel(self._handle, virtual_channel_name, self._interpreter)
5669

5770
def add_do_chan(
5871
self, lines, name_to_assign_to_lines="",

src/codegen/metadata/functions.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17186,6 +17186,32 @@
1718617186
'python_codegen_method': 'CustomCode',
1718717187
'returns': 'int32'
1718817188
},
17189+
'InternalGetLastCreatedChan': {
17190+
'calling_convention': 'StdCall',
17191+
'codegen_method': 'private',
17192+
'parameters': [
17193+
{
17194+
'ctypes_data_type': 'ctypes.c_char_p',
17195+
'direction': 'out',
17196+
'name': 'value',
17197+
'python_data_type': 'str',
17198+
'size': {
17199+
'mechanism': 'ivi-dance',
17200+
'value': 'size'
17201+
},
17202+
'type': 'char[]'
17203+
},
17204+
{
17205+
'ctypes_data_type': 'ctypes.c_uint32',
17206+
'direction': 'in',
17207+
'name': 'size',
17208+
'python_data_type': 'int',
17209+
'type': 'uInt32'
17210+
}
17211+
],
17212+
'python_codegen_method': 'CustomCode',
17213+
'returns': 'int32'
17214+
},
1718917215
'IsTaskDone': {
1719017216
'calling_convention': 'StdCall',
1719117217
'handle_parameter': {

0 commit comments

Comments
 (0)