Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

US1198694: add nidcpower.Session.get_channel_names() #1578

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,24 @@ All notable changes to this project will be documented in this file.
* #### Removed
* ### `nidcpower` (NI-DCPower)
* #### Added
* `get_channel_names` - [#1588](https://github.com/ni/nimi-python/issues/1588)
* API parity with NI-DCPower 20.7.0 by adding Output Cutoff functionality.
* Properties added:
* `output_cutoff_current_change_limit_high`
* `output_cutoff_current_change_limit_low`
* `output_cutoff_current_measure_limit_high`
* `output_cutoff_current_measure_limit_low`
* `output_cutoff_current_overrange_enabled`
* `output_cutoff_enabled`
* `output_cutoff_voltage_change_limit_high`
* `output_cutoff_voltage_change_limit_low`
* `output_cutoff_voltage_output_limit_high`
* `output_cutoff_voltage_output_limit_low`
* Methods added:
* `clear_latched_output_cutoff_state`
* `query_latched_output_cutoff_state`
* Properties added:
* `output_cutoff_current_change_limit_high`
* `output_cutoff_current_change_limit_low`
* `output_cutoff_current_measure_limit_high`
* `output_cutoff_current_measure_limit_low`
* `output_cutoff_current_overrange_enabled`
* `output_cutoff_enabled`
* `output_cutoff_voltage_change_limit_high`
* `output_cutoff_voltage_change_limit_low`
* `output_cutoff_voltage_output_limit_high`
* `output_cutoff_voltage_output_limit_low`
* Methods added:
* `clear_latched_output_cutoff_state`
* `query_latched_output_cutoff_state`
* #### Changed
* #### Removed
* #### Changed
* #### Removed
* ### `nidigital` (NI-Digital Pattern Driver)
Expand Down
7 changes: 6 additions & 1 deletion build/helper/metadata_add_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def _add_python_parameter_name(parameter):
'''Adds a python_name key/value pair to the parameter metadata'''
if 'python_name' not in parameter:
parameter['python_name'] = camelcase_to_snakecase(parameter['name'])
else:
parameter['python_name_override'] = True
return parameter


Expand Down Expand Up @@ -271,7 +273,10 @@ def _add_render_in_session_base(f):
def _add_is_repeated_capability(parameter):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole method is one big hack. We're inferring if a parameter is a repeated capability based on the name of the parameter; unless explicitly denoted. There are historical reasons for it, but I strongly question it nowadays:

Shouldn't we fix this in metadata? Then (I think) we can remove the inferring code and we can remove all this camel case conversion business.

Here's the thing: converting snakecase_to_camelcase in order to reverse the python name is a bit sketchy. It assumes that python name to C name are always camelCase to snake_case. Which happens to be true right now but doesn't really have to be true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we fix this in metadata? Then (I think) we can remove the inferring code and we can remove all this camel case conversion business.

Agreed, that the right fix, but it involves retrofitting metadata for all the drivers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Between the comments in this PR, and now the ones here in this PR, this relatively small user story is beginning to feel like a giant exercise of pulling at threads to see how can much can be unraveled. I won't dispute that there was some technical debt in the code that should probably be addressed at some point -- although I'm not the expert here, I can certainly agree that making assumptions on repeated capabilities based on the name of the parameter doesn't sound great. But I really wonder whether an "inner source" project in which we were asked to help complete the CXP API changes for nidcpower is the best vehicle for solving all this tech debt. After all, we also have a lot of tech debt in TSM that is waiting for attention too. The user story was ostensibly about adding support for instrument-based repeated capabilities for attributes (not even for function parameters). Here we are, almost a month later, and I'm no longer sure what the scope of the user story is any more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I stand corrected. This PR isn't even for the repeated capabilities user story (it's been so long, all these PRs are beginning to blur into each other). This one was just for the get_channel_names entry point, which isn't even related to repeated capabilities. It just exposed this issue because I needed to add support in the metadata for python_name in function parameters, which then introduced the need to update the _repeated_capability_parameter_names looksups . So I guess it's more relevant to Marcos' comment than my first comment implied. But we're still pretty far afield from the original user story.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spoke with @luisgomes252 offline and suggested to remove the workaround in _add_is_repeated_capability and add is_repeated_capability = false to nidcpower metadata to prevent the rep cap tip from getting added to the docstring.

The inferring logic will still be present and we should clean it up, not that's outside the scope of this PR.

Even setting is_repeated_capability = false can be done in a separate PR since others are blocked on this PR going in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the conversation with @sbethur, I've reverted the changes that I had made to use the python_name metadata in determining whether to add the repeated capability. Currently, get_channel_names is incorrectly being generated with metadata, but I'll fix that by updating the dcpower metadata in a subsequent PR so that I can get this one submitted as soon as possible, because a) other PRs are being gated by this one, and b) if I have to grab a new metadata export, that will also pull in a bunch of unrelated changes into this diff. So I'd like to split the PRs, if possible. @marcoskirsch: if you agree, and you have no other objections, please approve this PR when you have a chance.

'''Adds a boolean 'is_repeated_capability' to the parameter metadata by inferring it from its name, if not previously populated.'''
if 'is_repeated_capability' not in parameter:
if parameter['name'] in _repeated_capability_parameter_names:
param_name = parameter['name']
if 'python_name_override' in parameter and parameter['python_name_override']:
param_name = parameter['python_name']
if param_name in _repeated_capability_parameter_names:
parameter['is_repeated_capability'] = True
parameter['repeated_capability_type'] = 'channels'
else:
Expand Down
39 changes: 39 additions & 0 deletions docs/nidcpower/class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,45 @@ get_channel_name



get_channel_names
-----------------

.. py:currentmodule:: nidcpower.Session

.. py:method:: get_channel_names(indices)

Returns a list of channel names for given channel indices.





:param indices:


Index list for the channels in the session. Valid values are from zero to the total number of channels in the session minus one. The index string can be one of the following formats:

- A comma-separated list—for example, "0,2,3,1"
- A range using a hyphen—for example, "0-3"
- A range using a colon—for example, "0:3 "

You can combine comma-separated lists and ranges that use a hyphen or colon. Both out-of-order and repeated indices are supported ("2,3,0," "1,2,2,3"). White space characters, including spaces, tabs, feeds, and carriage returns, are allowed between characters. Ranges can be incrementing or decrementing.




:type indices: basic sequence types or str or int

:rtype: list of str
:return:


The channel name(s) at the specified indices.





get_ext_cal_last_date_and_time
------------------------------

Expand Down
9 changes: 9 additions & 0 deletions generated/nidcpower/nidcpower/_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(self, ctypes_library):
self.niDCPower_GetAttributeViReal64_cfunc = None
self.niDCPower_GetAttributeViString_cfunc = None
self.niDCPower_GetChannelName_cfunc = None
self.niDCPower_GetChannelNameFromString_cfunc = None
self.niDCPower_GetError_cfunc = None
self.niDCPower_GetExtCalLastDateAndTime_cfunc = None
self.niDCPower_GetExtCalLastTemp_cfunc = None
Expand Down Expand Up @@ -225,6 +226,14 @@ def niDCPower_GetChannelName(self, vi, index, buffer_size, channel_name): # noq
self.niDCPower_GetChannelName_cfunc.restype = ViStatus # noqa: F405
return self.niDCPower_GetChannelName_cfunc(vi, index, buffer_size, channel_name)

def niDCPower_GetChannelNameFromString(self, vi, indices, buffer_size, names): # noqa: N802
with self._func_lock:
if self.niDCPower_GetChannelNameFromString_cfunc is None:
self.niDCPower_GetChannelNameFromString_cfunc = self._get_library_function('niDCPower_GetChannelNameFromString')
self.niDCPower_GetChannelNameFromString_cfunc.argtypes = [ViSession, ctypes.POINTER(ViChar), ViInt32, ctypes.POINTER(ViChar)] # noqa: F405
self.niDCPower_GetChannelNameFromString_cfunc.restype = ViStatus # noqa: F405
return self.niDCPower_GetChannelNameFromString_cfunc(vi, indices, buffer_size, names)

def niDCPower_GetError(self, vi, code, buffer_size, description): # noqa: N802
with self._func_lock:
if self.niDCPower_GetError_cfunc is None:
Expand Down
32 changes: 32 additions & 0 deletions generated/nidcpower/nidcpower/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -5185,6 +5185,38 @@ def get_channel_name(self, index):
errors.handle_error(self, error_code, ignore_warnings=False, is_error_handling=False)
return channel_name_ctype.value.decode(self._encoding)

@ivi_synchronized
def get_channel_names(self, indices):
r'''get_channel_names

Returns a list of channel names for given channel indices.

Args:
indices (basic sequence types or str or int): Index list for the channels in the session. Valid values are from zero to the total number of channels in the session minus one. The index string can be one of the following formats:

- A comma-separated list—for example, "0,2,3,1"
- A range using a hyphen—for example, "0-3"
- A range using a colon—for example, "0:3 "

You can combine comma-separated lists and ranges that use a hyphen or colon. Both out-of-order and repeated indices are supported ("2,3,0," "1,2,2,3"). White space characters, including spaces, tabs, feeds, and carriage returns, are allowed between characters. Ranges can be incrementing or decrementing.


Returns:
names (list of str): The channel name(s) at the specified indices.

'''
vi_ctype = _visatype.ViSession(self._vi) # case S110
indices_ctype = ctypes.create_string_buffer(_converters.convert_repeated_capabilities_without_prefix(indices).encode(self._encoding)) # case C040
buffer_size_ctype = _visatype.ViInt32() # case S170
names_ctype = None # case C050
error_code = self._library.niDCPower_GetChannelNameFromString(vi_ctype, indices_ctype, buffer_size_ctype, names_ctype)
errors.handle_error(self, error_code, ignore_warnings=True, is_error_handling=False)
buffer_size_ctype = _visatype.ViInt32(error_code) # case S180
names_ctype = (_visatype.ViChar * buffer_size_ctype.value)() # case C060
error_code = self._library.niDCPower_GetChannelNameFromString(vi_ctype, indices_ctype, buffer_size_ctype, names_ctype)
errors.handle_error(self, error_code, ignore_warnings=False, is_error_handling=False)
return _converters.convert_comma_separated_string_to_list(names_ctype.value.decode(self._encoding))

@ivi_synchronized
def _get_ext_cal_last_date_and_time(self):
r'''_get_ext_cal_last_date_and_time
Expand Down
15 changes: 15 additions & 0 deletions generated/nidcpower/nidcpower/unit_tests/_mock_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ def __init__(self):
self._defaults['GetChannelName'] = {}
self._defaults['GetChannelName']['return'] = 0
self._defaults['GetChannelName']['channelName'] = None
self._defaults['GetChannelNameFromString'] = {}
self._defaults['GetChannelNameFromString']['return'] = 0
self._defaults['GetChannelNameFromString']['channelName'] = None
self._defaults['GetError'] = {}
self._defaults['GetError']['return'] = 0
self._defaults['GetError']['code'] = None
Expand Down Expand Up @@ -342,6 +345,16 @@ def niDCPower_GetChannelName(self, vi, index, buffer_size, channel_name): # noq
channel_name.value = self._defaults['GetChannelName']['channelName'].encode('ascii')
return self._defaults['GetChannelName']['return']

def niDCPower_GetChannelNameFromString(self, vi, indices, buffer_size, names): # noqa: N802
if self._defaults['GetChannelNameFromString']['return'] != 0:
return self._defaults['GetChannelNameFromString']['return']
if self._defaults['GetChannelNameFromString']['channelName'] is None:
raise MockFunctionCallError("niDCPower_GetChannelNameFromString", param='channelName')
if buffer_size.value == 0:
return len(self._defaults['GetChannelNameFromString']['channelName'])
names.value = self._defaults['GetChannelNameFromString']['channelName'].encode('ascii')
return self._defaults['GetChannelNameFromString']['return']

def niDCPower_GetError(self, vi, code, buffer_size, description): # noqa: N802
if self._defaults['GetError']['return'] != 0:
return self._defaults['GetError']['return']
Expand Down Expand Up @@ -740,6 +753,8 @@ def set_side_effects_and_return_values(self, mock_library):
mock_library.niDCPower_GetAttributeViString.return_value = 0
mock_library.niDCPower_GetChannelName.side_effect = MockFunctionCallError("niDCPower_GetChannelName")
mock_library.niDCPower_GetChannelName.return_value = 0
mock_library.niDCPower_GetChannelNameFromString.side_effect = MockFunctionCallError("niDCPower_GetChannelNameFromString")
mock_library.niDCPower_GetChannelNameFromString.return_value = 0
mock_library.niDCPower_GetError.side_effect = MockFunctionCallError("niDCPower_GetError")
mock_library.niDCPower_GetError.return_value = 0
mock_library.niDCPower_GetExtCalLastDateAndTime.side_effect = MockFunctionCallError("niDCPower_GetExtCalLastDateAndTime")
Expand Down
2 changes: 1 addition & 1 deletion src/nidcpower/metadata/attributes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# This file is generated from NI-DCPower API metadata version 21.0.0d52
# This file is generated from NI-DCPower API metadata version 21.0.0d69
attributes = {
1050003: {
'access': 'read-write',
Expand Down
4 changes: 2 additions & 2 deletions src/nidcpower/metadata/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# This file is generated from NI-DCPower API metadata version 21.0.0d52
# This file is generated from NI-DCPower API metadata version 21.0.0d69
config = {
'api_version': '21.0.0d52',
'api_version': '21.0.0d69',
'c_function_prefix': 'niDCPower_',
'close_function': 'close',
'context_manager_name': {
Expand Down
2 changes: 1 addition & 1 deletion src/nidcpower/metadata/enums.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# This file is generated from NI-DCPower API metadata version 21.0.0d52
# This file is generated from NI-DCPower API metadata version 21.0.0d69
enums = {
'ApertureTimeUnits': {
'values': [
Expand Down
53 changes: 52 additions & 1 deletion src/nidcpower/metadata/functions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# This file is generated from NI-DCPower API metadata version 21.0.0d52
# This file is generated from NI-DCPower API metadata version 21.0.0d69
functions = {
'AbortWithChannels': {
'documentation': {
Expand Down Expand Up @@ -1453,6 +1453,57 @@
],
'returns': 'ViStatus'
},
'GetChannelNameFromString': {
'documentation': {
'description': '\nReturns a list of channel names for given channel indices.'
},
'parameters': [
{
'direction': 'in',
'documentation': {
'description': '\nIdentifies a particular instrument session. **vi** is obtained from the\nniDCPower_InitializeWithChannels function.\n'
},
'name': 'vi',
'type': 'ViSession'
},
{
'direction': 'in',
'documentation': {
'description': '\nIndex list for the channels in the session. Valid values are from zero to the total number of channels in the session minus one. The index string can be one of the following formats:\n\n- A comma-separated list—for example, "0,2,3,1"\n- A range using a hyphen—for example, "0-3"\n- A range using a colon—for example, "0:3 "\n\nYou can combine comma-separated lists and ranges that use a hyphen or colon. Both out-of-order and repeated indices are supported ("2,3,0," "1,2,2,3"). White space characters, including spaces, tabs, feeds, and carriage returns, are allowed between characters. Ranges can be incrementing or decrementing.\n'
},
'name': 'index',
'python_api_converter_name': 'convert_repeated_capabilities_without_prefix',
'python_name': 'indices',
'type': 'ViConstString',
'type_in_documentation': 'basic sequence types or str or int'
},
{
'direction': 'in',
'documentation': {
'description': '\nThe number of elements in the ViChar array you specify for name.\n'
},
'name': 'bufferSize',
'type': 'ViInt32'
},
{
'direction': 'out',
'documentation': {
'description': 'The channel name(s) at the specified indices.'
},
'name': 'channelName',
'python_api_converter_name': 'convert_comma_separated_string_to_list',
'python_name': 'names',
'size': {
'mechanism': 'ivi-dance',
'value': 'bufferSize'
},
'type': 'ViChar[]',
'type_in_documentation': 'list of str'
}
],
'python_name': 'get_channel_names',
'returns': 'ViStatus'
},
'GetError': {
'codegen_method': 'private',
'documentation': {
Expand Down
17 changes: 17 additions & 0 deletions src/nidcpower/system_tests/test_system_nidcpower.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ def session():
yield simulated_session


@pytest.fixture(scope='function')
def multi_instrument_session():
instruments = ['PXI1Slot2', 'PXI1Slot5']
with nidcpower.Session(resource_name=','.join(instruments), options='Simulate=1, DriverSetup=Model:4162; BoardType:PXIe') as simulated_session:
yield simulated_session


@pytest.fixture(scope='function')
def single_channel_session():
with nidcpower.Session('4162', '0', False, 'Simulate=1, DriverSetup=Model:4162; BoardType:PXIe') as simulated_session:
Expand All @@ -36,6 +43,16 @@ def test_get_channel_name(session):
assert name == '0'


def test_get_channel_names(multi_instrument_session):
# Once we have support for independent channels, we should update this test to include
# the instrument names in the expected channel names -- or possibly add a separate test
# expected_string = ['{0}/{1}'.format(instruments[0], x) for x in range(12)]
# (Tracked on GitHub by #1582)
expected_string = ['{0}'.format(x) for x in range(12)]
channel_indices = ['0-1, 2, 3:4', 5, (6, 7), range(8, 10), slice(10, 12)]
assert multi_instrument_session.get_channel_names(indices=channel_indices) == expected_string


def test_get_attribute_string(session):
model = session.instrument_model
assert model == 'NI PXIe-4162'
Expand Down