Skip to content

Commit

Permalink
fix read issue with multiple channel types in a single task (#196)
Browse files Browse the repository at this point in the history
* fix read issue with multiple channel types in a single task

* update changelog

* updated issue name

* remove duplicated test
  • Loading branch information
zhindes authored Nov 28, 2022
1 parent e4afcf4 commit 1b79dde
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 40 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ All notable changes to this project will be documented in this file.
* ### Merged Pull Requests
* ...
* ### Resolved Issues
* ...
* [194: Multiple Voltage Measurement Types in the same task causes errors on Read](https://github.com/ni/nidaqmx-python/pull/194)
* ### Major Changes
* ...

Expand Down
10 changes: 8 additions & 2 deletions nidaqmx/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,14 @@ def read(self, number_of_samples_per_channel=NUM_SAMPLES_UNSET,

# Analog Input
if read_chan_type == ChannelType.ANALOG_INPUT:
meas_type = channels_to_read.ai_meas_type
if meas_type == UsageTypeAI.POWER:
has_power_chan = False
for chan in channels_to_read:
meas_type = chan.ai_meas_type
has_power_chan = meas_type == UsageTypeAI.POWER
if has_power_chan:
break

if has_power_chan:
voltages = numpy.zeros(array_shape, dtype=numpy.float64)
currents = numpy.zeros(array_shape, dtype=numpy.float64)

Expand Down
28 changes: 22 additions & 6 deletions nidaqmx/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,34 +78,50 @@ def bridge_device():


@pytest.fixture(scope="module")
def sim_power_device():
def sim_ts_power_device():
system = nidaqmx.system.System.local()

for device in system.devices:
if device.dev_is_simulated and UsageTypeAI.POWER in device.ai_meas_types:
if device.dev_is_simulated and device.product_category == ProductCategory.TEST_SCALE_MODULE and UsageTypeAI.POWER in device.ai_meas_types:
return device

pytest.skip(
"Could not detect a device that meets the requirements to be a simulated power device. "
"Could not detect a device that meets the requirements to be a TestScale simulated power device. "
"Cannot proceed to run tests. Import the NI MAX configuration file located at "
"nidaqmx\\tests\\max_config\\nidaqmxMaxConfig.ini to create these devices."
)
return None


@pytest.fixture(scope="module")
def sim_power_devices():
def sim_ts_voltage_device():
system = nidaqmx.system.System.local()

for device in system.devices:
if device.dev_is_simulated and device.product_category == ProductCategory.TEST_SCALE_MODULE and UsageTypeAI.VOLTAGE in device.ai_meas_types:
return device

pytest.skip(
"Could not detect a device that meets the requirements to be a TestScale simulated voltage device. "
"Cannot proceed to run tests. Import the NI MAX configuration file located at "
"nidaqmx\\tests\\max_config\\nidaqmxMaxConfig.ini to create these devices."
)
return None


@pytest.fixture(scope="module")
def sim_ts_power_devices():
system = nidaqmx.system.System.local()

devices = []
for device in system.devices:
if device.dev_is_simulated and UsageTypeAI.POWER in device.ai_meas_types:
if device.dev_is_simulated and device.product_category == ProductCategory.TEST_SCALE_MODULE and UsageTypeAI.POWER in device.ai_meas_types:
devices.append(device)
if len(devices) == 2:
return devices

pytest.skip(
"Could not detect two or more devices that meets the requirements to be a simulated power "
"Could not detect two or more devices that meets the requirements to be a TestScale simulated power "
"device. Cannot proceed to run tests. Import the NI MAX configuration file located at "
"nidaqmx\\tests\\max_config\\nidaqmxMaxConfig.ini to create these devices."
)
Expand Down
7 changes: 7 additions & 0 deletions nidaqmx/tests/max_config/nidaqmxMaxConfig.ini
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,10 @@ DevIsSimulated = 1
CompactDAQ.ChassisDevName = tsChassisTester
CompactDAQ.SlotNum = 2

[DAQmxCDAQModule tsVoltageTester1]
ProductType = TS-15100
DevSerialNum = 0x0
DevIsSimulated = 1
CompactDAQ.ChassisDevName = tsChassisTester
CompactDAQ.SlotNum = 3

6 changes: 3 additions & 3 deletions nidaqmx/tests/test_channel_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
CurrentShuntResistorLocation, TemperatureUnits, RTDType,
ResistanceConfiguration, ExcitationSource, ResistanceUnits, StrainUnits,
StrainGageBridgeType, BridgeConfiguration)
from nidaqmx.tests.fixtures import sim_power_device, any_x_series_device
from nidaqmx.tests.fixtures import sim_ts_power_device, any_x_series_device
from nidaqmx.tests.helpers import generate_random_seed


Expand Down Expand Up @@ -268,11 +268,11 @@ def test_create_ai_voltage_chan_with_excit(self, any_x_series_device, seed):
assert not ai_channel.ai_excit_use_for_scaling

@pytest.mark.parametrize('seed', [generate_random_seed()])
def test_create_ai_power_chan(self, sim_power_device, seed):
def test_create_ai_power_chan(self, sim_ts_power_device, seed):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

pwr_phys_chan = f"{sim_power_device.name}/power"
pwr_phys_chan = f"{sim_ts_power_device.name}/power"
voltage_setpoint = random.random() * 6.0
current_setpoint = random.random() * 3.0
output_enable = random.choice([True, False])
Expand Down
55 changes: 46 additions & 9 deletions nidaqmx/tests/test_read_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from nidaqmx.constants import (
Edge, TriggerType, AcquisitionType, LineGrouping, Level, TaskMode)
from nidaqmx.utils import flatten_channel_string
from nidaqmx.tests.fixtures import sim_power_device, sim_power_devices, real_x_series_device
from nidaqmx.tests.fixtures import sim_ts_power_device, sim_ts_power_devices, sim_ts_voltage_device, sim_x_series_device, real_x_series_device
from nidaqmx.tests.helpers import generate_random_seed, POWER_ABS_EPSILON


Expand Down Expand Up @@ -613,7 +613,7 @@ class TestPowerRead(TestDAQmxIOBase):
(generate_random_seed(), False)
]
)
def test_power_1_chan_1_samp(self, sim_power_device, seed, output_enable):
def test_power_1_chan_1_samp(self, sim_ts_power_device, seed, output_enable):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -622,7 +622,7 @@ def test_power_1_chan_1_samp(self, sim_power_device, seed, output_enable):

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_power_chan(
f"{sim_power_device.name}/power",
f"{sim_ts_power_device.name}/power",
voltage_setpoint, current_setpoint, output_enable)

read_task.start()
Expand All @@ -644,15 +644,15 @@ def test_power_1_chan_1_samp(self, sim_power_device, seed, output_enable):
(generate_random_seed(), [False, False])
]
)
def test_power_n_chan_1_samp(self, sim_power_devices, seed, output_enables):
def test_power_n_chan_1_samp(self, sim_ts_power_devices, seed, output_enables):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

voltage_setpoint = 0.0
current_setpoint = 0.03

with nidaqmx.Task() as read_task:
for device, output_enable in zip(sim_power_devices, output_enables):
for device, output_enable in zip(sim_ts_power_devices, output_enables):
read_task.ai_channels.add_ai_power_chan(
f"{device.name}/power",
voltage_setpoint, current_setpoint, output_enable)
Expand All @@ -675,7 +675,7 @@ def test_power_n_chan_1_samp(self, sim_power_devices, seed, output_enables):
(generate_random_seed(), False)
]
)
def test_power_1_chan_n_samp(self, sim_power_device, seed, output_enable):
def test_power_1_chan_n_samp(self, sim_ts_power_device, seed, output_enable):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -684,7 +684,7 @@ def test_power_1_chan_n_samp(self, sim_power_device, seed, output_enable):

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_power_chan(
f"{sim_power_device.name}/power",
f"{sim_ts_power_device.name}/power",
voltage_setpoint, current_setpoint, output_enable)

read_task.start()
Expand All @@ -706,15 +706,15 @@ def test_power_1_chan_n_samp(self, sim_power_device, seed, output_enable):
(generate_random_seed(), [False, False])
]
)
def test_power_n_chan_n_samp(self, sim_power_devices, seed, output_enables):
def test_power_n_chan_n_samp(self, sim_ts_power_devices, seed, output_enables):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

voltage_setpoint = 0.0
current_setpoint = 0.03

with nidaqmx.Task() as read_task:
for device, output_enable in zip(sim_power_devices, output_enables):
for device, output_enable in zip(sim_ts_power_devices, output_enables):
read_task.ai_channels.add_ai_power_chan(
f"{device.name}/power",
voltage_setpoint, current_setpoint, output_enable)
Expand All @@ -731,3 +731,40 @@ def test_power_n_chan_n_samp(self, sim_power_devices, seed, output_enables):
else:
assert all(math.isnan(sample.voltage) for sample in channel_values)
assert all(math.isnan(sample.current) for sample in channel_values)

@pytest.mark.parametrize('seed', [generate_random_seed()])
def test_mixed_chans(self, sim_x_series_device, seed):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_voltage_chan(
f"{sim_x_series_device.name}/ai0", max_val=10, min_val=-10)
read_task.ai_channels.add_ai_current_chan(
f"{sim_x_series_device.name}/ai1", max_val=0.01, min_val=-0.01)

read_task.start()
# We aren't validating data, just assuring that it doesn't fail.
values_read = read_task.read(number_of_samples_per_channel=10)

@pytest.mark.parametrize('seed', [generate_random_seed()])
def test_mixed_chans_with_power(self, sim_ts_power_device, sim_ts_voltage_device, seed):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

voltage_setpoint = 0.0
current_setpoint = 0.03
output_enable = False

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_power_chan(
f"{sim_ts_power_device.name}/power",
voltage_setpoint, current_setpoint, output_enable)
read_task.ai_channels.add_ai_voltage_chan(
f"{sim_ts_voltage_device.name}/ai0", max_val=10, min_val=-10)

read_task.start()
# We aren't validating data, just assuring that it fails. The error
# code is currently not very good, so we'll ignore that for now.
with pytest.raises(nidaqmx.errors.DaqReadError):
values_read = read_task.read(number_of_samples_per_channel=10)
38 changes: 19 additions & 19 deletions nidaqmx/tests/test_stream_power_readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import nidaqmx
from nidaqmx.stream_readers import (
PowerSingleChannelReader, PowerMultiChannelReader, PowerBinaryReader)
from nidaqmx.tests.fixtures import sim_power_device, sim_power_devices
from nidaqmx.tests.fixtures import sim_ts_power_device, sim_ts_power_devices
from nidaqmx.tests.helpers import generate_random_seed, POWER_ABS_EPSILON
from nidaqmx.tests.test_read_write import TestDAQmxIOBase

Expand All @@ -31,7 +31,7 @@ class TestPowerSingleChannelReader(TestDAQmxIOBase):
(generate_random_seed(), False)
]
)
def test_power_1_chan_1_samp(self, sim_power_device, seed, output_enable):
def test_power_1_chan_1_samp(self, sim_ts_power_device, seed, output_enable):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -40,7 +40,7 @@ def test_power_1_chan_1_samp(self, sim_power_device, seed, output_enable):

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_power_chan(
f"{sim_power_device.name}/power",
f"{sim_ts_power_device.name}/power",
voltage_setpoint, current_setpoint, output_enable)

reader = PowerSingleChannelReader(read_task.in_stream)
Expand All @@ -62,7 +62,7 @@ def test_power_1_chan_1_samp(self, sim_power_device, seed, output_enable):
(generate_random_seed(), False)
]
)
def test_power_1_chan_n_samp(self, sim_power_device, seed, output_enable):
def test_power_1_chan_n_samp(self, sim_ts_power_device, seed, output_enable):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -76,7 +76,7 @@ def test_power_1_chan_n_samp(self, sim_power_device, seed, output_enable):

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_power_chan(
f"{sim_power_device.name}/power",
f"{sim_ts_power_device.name}/power",
voltage_setpoint, current_setpoint, output_enable)

reader = PowerSingleChannelReader(read_task.in_stream)
Expand Down Expand Up @@ -111,19 +111,19 @@ class TestPowerMultiChannelReader(TestDAQmxIOBase):
(generate_random_seed(), [False, False])
]
)
def test_power_n_chan_1_samp(self, sim_power_devices, seed, output_enables):
def test_power_n_chan_1_samp(self, sim_ts_power_devices, seed, output_enables):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

voltage_setpoint = 0.0
current_setpoint = 0.03

# Fill with bad data to ensure its overwritten by read.
voltage_data = numpy.full(len(sim_power_devices), -1.0, dtype=numpy.float64)
current_data = numpy.full(len(sim_power_devices), -1.0, dtype=numpy.float64)
voltage_data = numpy.full(len(sim_ts_power_devices), -1.0, dtype=numpy.float64)
current_data = numpy.full(len(sim_ts_power_devices), -1.0, dtype=numpy.float64)

with nidaqmx.Task() as read_task:
for device, output_enable in zip(sim_power_devices, output_enables):
for device, output_enable in zip(sim_ts_power_devices, output_enables):
read_task.ai_channels.add_ai_power_chan(
f"{device.name}/power",
voltage_setpoint, current_setpoint, output_enable)
Expand All @@ -150,7 +150,7 @@ def test_power_n_chan_1_samp(self, sim_power_devices, seed, output_enables):
(generate_random_seed(), [False, False])
]
)
def test_power_n_chan_n_samp(self, sim_power_devices, seed, output_enables):
def test_power_n_chan_n_samp(self, sim_ts_power_devices, seed, output_enables):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -159,11 +159,11 @@ def test_power_n_chan_n_samp(self, sim_power_devices, seed, output_enables):
number_of_samples_per_channel = 10

# Fill with bad data to ensure its overwritten by read.
voltage_data = numpy.full((len(sim_power_devices), number_of_samples_per_channel), -1.0, dtype=numpy.float64)
current_data = numpy.full((len(sim_power_devices), number_of_samples_per_channel), -1.0, dtype=numpy.float64)
voltage_data = numpy.full((len(sim_ts_power_devices), number_of_samples_per_channel), -1.0, dtype=numpy.float64)
current_data = numpy.full((len(sim_ts_power_devices), number_of_samples_per_channel), -1.0, dtype=numpy.float64)

with nidaqmx.Task() as read_task:
for device, output_enable in zip(sim_power_devices, output_enables):
for device, output_enable in zip(sim_ts_power_devices, output_enables):
read_task.ai_channels.add_ai_power_chan(
f"{device.name}/power",
voltage_setpoint, current_setpoint, output_enable)
Expand Down Expand Up @@ -203,7 +203,7 @@ class TestPowerBinaryReader(TestDAQmxIOBase):
(generate_random_seed(), False)
]
)
def test_power_1_chan_n_samp_binary(self, sim_power_device, seed, output_enable):
def test_power_1_chan_n_samp_binary(self, sim_ts_power_device, seed, output_enable):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -217,7 +217,7 @@ def test_power_1_chan_n_samp_binary(self, sim_power_device, seed, output_enable)

with nidaqmx.Task() as read_task:
read_task.ai_channels.add_ai_power_chan(
f"{sim_power_device.name}/power",
f"{sim_ts_power_device.name}/power",
voltage_setpoint, current_setpoint, output_enable)

reader = PowerBinaryReader(read_task.in_stream)
Expand Down Expand Up @@ -247,7 +247,7 @@ def test_power_1_chan_n_samp_binary(self, sim_power_device, seed, output_enable)
(generate_random_seed(), [False, False])
]
)
def test_power_n_chan_many_sample_binary(self, sim_power_devices, seed, output_enables):
def test_power_n_chan_many_sample_binary(self, sim_ts_power_devices, seed, output_enables):
# Reset the pseudorandom number generator with seed.
random.seed(seed)

Expand All @@ -256,11 +256,11 @@ def test_power_n_chan_many_sample_binary(self, sim_power_devices, seed, output_e
number_of_samples_per_channel = 10

# Fill with bad data to ensure its overwritten by read.
voltage_data = numpy.full((len(sim_power_devices), number_of_samples_per_channel), -32768, dtype=numpy.int16)
current_data = numpy.full((len(sim_power_devices), number_of_samples_per_channel), -32768, dtype=numpy.int16)
voltage_data = numpy.full((len(sim_ts_power_devices), number_of_samples_per_channel), -32768, dtype=numpy.int16)
current_data = numpy.full((len(sim_ts_power_devices), number_of_samples_per_channel), -32768, dtype=numpy.int16)

with nidaqmx.Task() as read_task:
for device, output_enable in zip(sim_power_devices, output_enables):
for device, output_enable in zip(sim_ts_power_devices, output_enables):
read_task.ai_channels.add_ai_power_chan(
f"{device.name}/power",
voltage_setpoint, current_setpoint, output_enable)
Expand Down

0 comments on commit 1b79dde

Please sign in to comment.