From 023f5bc957f097159c927925407c6417c9448dbb Mon Sep 17 00:00:00 2001 From: Joo Liang Cheah Date: Fri, 7 Jun 2024 10:33:09 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20incomplete=20timestamp=20support=20and=20?= =?UTF-8?q?wait=5Ffor=5Fvalid=5Ftimestamp=20not=20ret=E2=80=A6=20(#592)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix incomplete timestamp support and wait_for_valid_timestamp not returning a value * fixed style --- generated/nidaqmx/task/_task.py | 45 +++++----- generated/nidaqmx/task/_timing.py | 82 +++++++++++++++++++ .../task/triggering/_arm_start_trigger.py | 9 ++ .../task/triggering/_reference_trigger.py | 9 ++ .../nidaqmx/task/triggering/_start_trigger.py | 9 ++ src/codegen/utilities/attribute_helpers.py | 11 --- src/handwritten/task/_task.py | 45 +++++----- tests/component/task/test_triggers.py | 15 +++- 8 files changed, 170 insertions(+), 55 deletions(-) diff --git a/generated/nidaqmx/task/_task.py b/generated/nidaqmx/task/_task.py index b70c536e3..498aa8059 100644 --- a/generated/nidaqmx/task/_task.py +++ b/generated/nidaqmx/task/_task.py @@ -234,7 +234,7 @@ def export_signals(self) -> ExportSignals: return self._export_signals @property - def in_stream(self) -> InStream: + def in_stream(self) -> InStream: """ Gets the read configurations for the task. """ @@ -253,7 +253,7 @@ def timing(self) -> Timing: Gets the timing configurations for the task. """ return self._timing - + @property def triggers(self) -> Triggers: """ @@ -405,7 +405,7 @@ def perform_bridge_offset_nulling_cal(self, channel="", skip_unsupported_channel if some channels in the task have open thermocouple detection disabled. If the input is empty, this VI attempts to calibrate all virtual channels in the task. - skip_unsupported_channels: specifies whether or not to skip channels that do not + skip_unsupported_channels: specifies whether or not to skip channels that do not support calibration. If skip unsupported channels is TRUE, this VI calibrates only supported channels. If FALSE, this VI calibrates the channels specified by channels. The default is FALSE. @@ -426,18 +426,18 @@ def perform_strain_shunt_cal( calibration instructions. Args: - channel: Specifies a subset of virtual channels in the task that you + channel: Specifies a subset of virtual channels in the task that you want to calibrate. Use this input if you do not want to calibrate all the channels in the task or if some channels in the task measure - non-bridge-based sensors. If the input is empty, this method attempts + non-bridge-based sensors. If the input is empty, this method attempts to calibrate all virtual channels in the task. shunt_resistor_value: Specifies the shunt resistance in ohms. shunt_resistor_location: Specifies the location of the shunt resistor. shunt_resistor_select: Specifies which shunt calibration switch to enable. shunt_resistor_source: Specifies which shunt to use. - skip_unsupported_channels: Specifies whether or not to skip channels that + skip_unsupported_channels: Specifies whether or not to skip channels that do not support calibration. If skip_unsupported_channels is True, this - method calibrates only supported channels. If False, this method calibrates + method calibrates only supported channels. If False, this method calibrates the channels specified by channels. The default is False. """ self._interpreter.perform_strain_shunt_cal_ex( @@ -457,10 +457,10 @@ def perform_bridge_shunt_cal( calibration instructions. Args: - channel: Specifies a subset of virtual channels in the task that you + channel: Specifies a subset of virtual channels in the task that you want to calibrate. Use this input if you do not want to calibrate all the channels in the task or if some channels in the task measure - non-bridge-based sensors. If the input is empty, this method attempts + non-bridge-based sensors. If the input is empty, this method attempts to calibrate all virtual channels in the task. shunt_resistor_value: Specifies the shunt resistance in ohms. shunt_resistor_location: Specifies the location of the shunt resistor. @@ -469,23 +469,23 @@ def perform_bridge_shunt_cal( bridge_resistance: Specifies the bridge resistance in ohms. A value of -1 means to use the nominal bridge resistance specified when you created the virtual channel. - skip_unsupported_channels: Specifies whether or not to skip channels that + skip_unsupported_channels: Specifies whether or not to skip channels that do not support calibration. If skip_unsupported_channels is True, this - method calibrates only supported channels. If False, this method calibrates + method calibrates only supported channels. If False, this method calibrates the channels specified by channels. The default is False. """ self._interpreter.perform_bridge_shunt_cal_ex( self._handle, channel, shunt_resistor_value, shunt_resistor_location.value, shunt_resistor_select.value, shunt_resistor_source.value, bridge_resistance, - skip_unsupported_channels) + skip_unsupported_channels) def perform_thrmcpl_lead_offset_nulling_cal(self, channel="", skip_unsupported_channels=False): """ Perform thermocouple lead offset nulling calibration on the channels in the task. This is to compensate for offsets introduced by open thermocouple detection. - Keep the measured temperature as constant as possible while performing this + Keep the measured temperature as constant as possible while performing this adjustment. Args: @@ -494,7 +494,7 @@ def perform_thrmcpl_lead_offset_nulling_cal(self, channel="", skip_unsupported_c if some channels in the task have open thermocouple detection disabled. If the input is empty, this VI attempts to calibrate all virtual channels in the task. - skip_unsupported_channels: specifies whether or not to skip channels that do not + skip_unsupported_channels: specifies whether or not to skip channels that do not support calibration. If skip unsupported channels is TRUE, this VI calibrates only supported channels. If FALSE, this VI calibrates the channels specified by channels. The default is FALSE. @@ -665,7 +665,7 @@ def read(self, number_of_samples_per_channel=NUM_SAMPLES_UNSET, return data[:samples_read].tolist() return data.tolist() - + def _read_ctr_pulse( self, array_shape: Tuple[int, ...], @@ -707,7 +707,7 @@ def _read_ctr_pulse( else: assert False, f"{meas_type} is not a counter pulse measurement type." - + if num_samples_not_set and array_shape == (1,): return data[0] @@ -1028,14 +1028,19 @@ def wait_for_valid_timestamp(self, timestamp_event, timeout=10.0): Use this method to ensure the timestamp has a valid value to prevent an error when querying a timestamp value. Args: - timestamp_event(nidaqmx.constants.TimestampEvent): Specifies the timestamp type to wait on. + timestamp_event(nidaqmx.constants.TimestampEvent): Specifies the timestamp type to wait on. timeout (float): Specifies the maximum amount of time in seconds to wait for a valid timestamp. This method returns an error if the time elapses. The default is 10. If you set timeout (sec) to nidaqmx.WAIT_INFINITELY, the method waits indefinitely. + + Returns: + datetime: + + The timestamp value of timestamp_event. """ - self._interpreter.wait_for_valid_timestamp(self._handle, timestamp_event.value, timeout) + return self._interpreter.wait_for_valid_timestamp(self._handle, timestamp_event.value, timeout) def wait_until_done(self, timeout=10.0): """ @@ -1285,7 +1290,7 @@ def write(self, data, auto_start=AUTO_START_UNSET, timeout=10.0): return self._interpreter.write_ctr_ticks( self._handle, number_of_samples_per_channel, auto_start, timeout, FillMode.GROUP_BY_CHANNEL.value, high_ticks, low_ticks) - + else: raise DaqError( "Write failed, because the output type is not supported.\n\n" @@ -1300,7 +1305,7 @@ def write(self, data, auto_start=AUTO_START_UNSET, timeout=10.0): 'task to which data can be written.', DAQmxErrors.WRITE_NO_OUTPUT_CHANS_IN_TASK, task_name=self.name) - + class _TaskAlternateConstructor(Task): """ diff --git a/generated/nidaqmx/task/_timing.py b/generated/nidaqmx/task/_timing.py index 89cca8831..23e3da0c5 100644 --- a/generated/nidaqmx/task/_timing.py +++ b/generated/nidaqmx/task/_timing.py @@ -323,6 +323,62 @@ def delay_from_samp_clk_delay_units(self, val): def delay_from_samp_clk_delay_units(self): self._interpreter.reset_timing_attribute(self._handle, 0x1304) + @property + def first_samp_clk_offset(self): + """ + float: Specifies, in seconds, the offset to apply to the + **first_samp_clk_when** value. This offset modifies when the + first sample clock occurs and is used to account for known + delays in the signal path. + """ + + val = self._interpreter.get_timing_attribute_double(self._handle, 0x31aa) + return val + + @first_samp_clk_offset.setter + def first_samp_clk_offset(self, val): + self._interpreter.set_timing_attribute_double(self._handle, 0x31aa, val) + + @first_samp_clk_offset.deleter + def first_samp_clk_offset(self): + self._interpreter.reset_timing_attribute(self._handle, 0x31aa) + + @property + def first_samp_clk_timescale(self): + """ + :class:`nidaqmx.constants.Timescale`: Specifies the timescale to + be used for the value of **first_samp_clk_when**. + """ + + val = self._interpreter.get_timing_attribute_int32(self._handle, 0x3183) + return Timescale(val) + + @first_samp_clk_timescale.setter + def first_samp_clk_timescale(self, val): + val = val.value + self._interpreter.set_timing_attribute_int32(self._handle, 0x3183, val) + + @first_samp_clk_timescale.deleter + def first_samp_clk_timescale(self): + self._interpreter.reset_timing_attribute(self._handle, 0x3183) + + @property + def first_samp_clk_when(self): + """ + datetime: Specifies the time of the first sample clock pulse. + """ + + val = self._interpreter.get_timing_attribute_timestamp(self._handle, 0x3182) + return val + + @first_samp_clk_when.setter + def first_samp_clk_when(self, val): + self._interpreter.set_timing_attribute_timestamp(self._handle, 0x3182, val) + + @first_samp_clk_when.deleter + def first_samp_clk_when(self): + self._interpreter.reset_timing_attribute(self._handle, 0x3182) + @property def first_samp_timestamp_enable(self): """ @@ -359,6 +415,15 @@ def first_samp_timestamp_timescale(self, val): def first_samp_timestamp_timescale(self): self._interpreter.reset_timing_attribute(self._handle, 0x313b) + @property + def first_samp_timestamp_val(self): + """ + datetime: Indicates the timestamp of the first sample. + """ + + val = self._interpreter.get_timing_attribute_timestamp(self._handle, 0x313a) + return val + @property def hshk_delay_after_xfer(self): """ @@ -1085,6 +1150,23 @@ def sync_pulse_time_timescale(self, val): def sync_pulse_time_timescale(self): self._interpreter.reset_timing_attribute(self._handle, 0x3138) + @property + def sync_pulse_time_when(self): + """ + datetime: Specifies the start time of the sync pulse. + """ + + val = self._interpreter.get_timing_attribute_timestamp(self._handle, 0x3137) + return val + + @sync_pulse_time_when.setter + def sync_pulse_time_when(self, val): + self._interpreter.set_timing_attribute_timestamp(self._handle, 0x3137, val) + + @sync_pulse_time_when.deleter + def sync_pulse_time_when(self): + self._interpreter.reset_timing_attribute(self._handle, 0x3137) + @property def sync_pulse_type(self): """ diff --git a/generated/nidaqmx/task/triggering/_arm_start_trigger.py b/generated/nidaqmx/task/triggering/_arm_start_trigger.py index 05c217295..f7d56203e 100644 --- a/generated/nidaqmx/task/triggering/_arm_start_trigger.py +++ b/generated/nidaqmx/task/triggering/_arm_start_trigger.py @@ -226,6 +226,15 @@ def timestamp_timescale(self, val): def timestamp_timescale(self): self._interpreter.reset_trig_attribute(self._handle, 0x3135) + @property + def timestamp_val(self): + """ + datetime: Indicates the arm start trigger timestamp value. + """ + + val = self._interpreter.get_trig_attribute_timestamp(self._handle, 0x3134) + return val + @property def trig_type(self): """ diff --git a/generated/nidaqmx/task/triggering/_reference_trigger.py b/generated/nidaqmx/task/triggering/_reference_trigger.py index e74207b50..5bcfc3136 100644 --- a/generated/nidaqmx/task/triggering/_reference_trigger.py +++ b/generated/nidaqmx/task/triggering/_reference_trigger.py @@ -910,6 +910,15 @@ def timestamp_timescale(self, val): def timestamp_timescale(self): self._interpreter.reset_trig_attribute(self._handle, 0x3130) + @property + def timestamp_val(self): + """ + datetime: Indicates the reference trigger timestamp value. + """ + + val = self._interpreter.get_trig_attribute_timestamp(self._handle, 0x312f) + return val + @property def trig_type(self): """ diff --git a/generated/nidaqmx/task/triggering/_start_trigger.py b/generated/nidaqmx/task/triggering/_start_trigger.py index 81f575819..416991273 100644 --- a/generated/nidaqmx/task/triggering/_start_trigger.py +++ b/generated/nidaqmx/task/triggering/_start_trigger.py @@ -917,6 +917,15 @@ def timestamp_timescale(self, val): def timestamp_timescale(self): self._interpreter.reset_trig_attribute(self._handle, 0x312d) + @property + def timestamp_val(self): + """ + datetime: Indicates the start trigger timestamp value. + """ + + val = self._interpreter.get_trig_attribute_timestamp(self._handle, 0x314b) + return val + @property def trig_type(self): """ diff --git a/src/codegen/utilities/attribute_helpers.py b/src/codegen/utilities/attribute_helpers.py index d44ad2a21..73a5b58e6 100644 --- a/src/codegen/utilities/attribute_helpers.py +++ b/src/codegen/utilities/attribute_helpers.py @@ -29,18 +29,7 @@ "DO_LINES", "CI_PHYSICAL_CHANS", "CO_PHYSICAL_CHANS", - "ARM_START_TRIG_TIMESTAMP_VAL", - "REF_TRIG_TIMESTAMP_VAL", - "START_TRIG_TIMESTAMP_VAL", - "FIRST_SAMP_CLK_OFFSET", - "FIRST_SAMP_CLK_TIMESCALE", - "FIRST_SAMP_CLK_WHEN", - "FIRST_SAMP_TIMESTAMP_VAL", - "SYNC_PULSE_TIME_WHEN", "TIMING_SYNC_PULSE_FORCE", - "ARM_START_TRIG_TIMESTAMP_VAL", - "REF_TRIG_TIMESTAMP_VAL", - "START_TRIG_TIMESTAMP_VAL", ] DEPRECATED_ATTRIBUTES = { diff --git a/src/handwritten/task/_task.py b/src/handwritten/task/_task.py index b70c536e3..498aa8059 100644 --- a/src/handwritten/task/_task.py +++ b/src/handwritten/task/_task.py @@ -234,7 +234,7 @@ def export_signals(self) -> ExportSignals: return self._export_signals @property - def in_stream(self) -> InStream: + def in_stream(self) -> InStream: """ Gets the read configurations for the task. """ @@ -253,7 +253,7 @@ def timing(self) -> Timing: Gets the timing configurations for the task. """ return self._timing - + @property def triggers(self) -> Triggers: """ @@ -405,7 +405,7 @@ def perform_bridge_offset_nulling_cal(self, channel="", skip_unsupported_channel if some channels in the task have open thermocouple detection disabled. If the input is empty, this VI attempts to calibrate all virtual channels in the task. - skip_unsupported_channels: specifies whether or not to skip channels that do not + skip_unsupported_channels: specifies whether or not to skip channels that do not support calibration. If skip unsupported channels is TRUE, this VI calibrates only supported channels. If FALSE, this VI calibrates the channels specified by channels. The default is FALSE. @@ -426,18 +426,18 @@ def perform_strain_shunt_cal( calibration instructions. Args: - channel: Specifies a subset of virtual channels in the task that you + channel: Specifies a subset of virtual channels in the task that you want to calibrate. Use this input if you do not want to calibrate all the channels in the task or if some channels in the task measure - non-bridge-based sensors. If the input is empty, this method attempts + non-bridge-based sensors. If the input is empty, this method attempts to calibrate all virtual channels in the task. shunt_resistor_value: Specifies the shunt resistance in ohms. shunt_resistor_location: Specifies the location of the shunt resistor. shunt_resistor_select: Specifies which shunt calibration switch to enable. shunt_resistor_source: Specifies which shunt to use. - skip_unsupported_channels: Specifies whether or not to skip channels that + skip_unsupported_channels: Specifies whether or not to skip channels that do not support calibration. If skip_unsupported_channels is True, this - method calibrates only supported channels. If False, this method calibrates + method calibrates only supported channels. If False, this method calibrates the channels specified by channels. The default is False. """ self._interpreter.perform_strain_shunt_cal_ex( @@ -457,10 +457,10 @@ def perform_bridge_shunt_cal( calibration instructions. Args: - channel: Specifies a subset of virtual channels in the task that you + channel: Specifies a subset of virtual channels in the task that you want to calibrate. Use this input if you do not want to calibrate all the channels in the task or if some channels in the task measure - non-bridge-based sensors. If the input is empty, this method attempts + non-bridge-based sensors. If the input is empty, this method attempts to calibrate all virtual channels in the task. shunt_resistor_value: Specifies the shunt resistance in ohms. shunt_resistor_location: Specifies the location of the shunt resistor. @@ -469,23 +469,23 @@ def perform_bridge_shunt_cal( bridge_resistance: Specifies the bridge resistance in ohms. A value of -1 means to use the nominal bridge resistance specified when you created the virtual channel. - skip_unsupported_channels: Specifies whether or not to skip channels that + skip_unsupported_channels: Specifies whether or not to skip channels that do not support calibration. If skip_unsupported_channels is True, this - method calibrates only supported channels. If False, this method calibrates + method calibrates only supported channels. If False, this method calibrates the channels specified by channels. The default is False. """ self._interpreter.perform_bridge_shunt_cal_ex( self._handle, channel, shunt_resistor_value, shunt_resistor_location.value, shunt_resistor_select.value, shunt_resistor_source.value, bridge_resistance, - skip_unsupported_channels) + skip_unsupported_channels) def perform_thrmcpl_lead_offset_nulling_cal(self, channel="", skip_unsupported_channels=False): """ Perform thermocouple lead offset nulling calibration on the channels in the task. This is to compensate for offsets introduced by open thermocouple detection. - Keep the measured temperature as constant as possible while performing this + Keep the measured temperature as constant as possible while performing this adjustment. Args: @@ -494,7 +494,7 @@ def perform_thrmcpl_lead_offset_nulling_cal(self, channel="", skip_unsupported_c if some channels in the task have open thermocouple detection disabled. If the input is empty, this VI attempts to calibrate all virtual channels in the task. - skip_unsupported_channels: specifies whether or not to skip channels that do not + skip_unsupported_channels: specifies whether or not to skip channels that do not support calibration. If skip unsupported channels is TRUE, this VI calibrates only supported channels. If FALSE, this VI calibrates the channels specified by channels. The default is FALSE. @@ -665,7 +665,7 @@ def read(self, number_of_samples_per_channel=NUM_SAMPLES_UNSET, return data[:samples_read].tolist() return data.tolist() - + def _read_ctr_pulse( self, array_shape: Tuple[int, ...], @@ -707,7 +707,7 @@ def _read_ctr_pulse( else: assert False, f"{meas_type} is not a counter pulse measurement type." - + if num_samples_not_set and array_shape == (1,): return data[0] @@ -1028,14 +1028,19 @@ def wait_for_valid_timestamp(self, timestamp_event, timeout=10.0): Use this method to ensure the timestamp has a valid value to prevent an error when querying a timestamp value. Args: - timestamp_event(nidaqmx.constants.TimestampEvent): Specifies the timestamp type to wait on. + timestamp_event(nidaqmx.constants.TimestampEvent): Specifies the timestamp type to wait on. timeout (float): Specifies the maximum amount of time in seconds to wait for a valid timestamp. This method returns an error if the time elapses. The default is 10. If you set timeout (sec) to nidaqmx.WAIT_INFINITELY, the method waits indefinitely. + + Returns: + datetime: + + The timestamp value of timestamp_event. """ - self._interpreter.wait_for_valid_timestamp(self._handle, timestamp_event.value, timeout) + return self._interpreter.wait_for_valid_timestamp(self._handle, timestamp_event.value, timeout) def wait_until_done(self, timeout=10.0): """ @@ -1285,7 +1290,7 @@ def write(self, data, auto_start=AUTO_START_UNSET, timeout=10.0): return self._interpreter.write_ctr_ticks( self._handle, number_of_samples_per_channel, auto_start, timeout, FillMode.GROUP_BY_CHANNEL.value, high_ticks, low_ticks) - + else: raise DaqError( "Write failed, because the output type is not supported.\n\n" @@ -1300,7 +1305,7 @@ def write(self, data, auto_start=AUTO_START_UNSET, timeout=10.0): 'task to which data can be written.', DAQmxErrors.WRITE_NO_OUTPUT_CHANS_IN_TASK, task_name=self.name) - + class _TaskAlternateConstructor(Task): """ diff --git a/tests/component/task/test_triggers.py b/tests/component/task/test_triggers.py index 202f106a9..7357aabff 100644 --- a/tests/component/task/test_triggers.py +++ b/tests/component/task/test_triggers.py @@ -91,7 +91,9 @@ def test___start_trigger___wait_for_valid_timestamp___no_errors( ai_voltage_task.triggers.start_trigger.timestamp_enable = True ai_voltage_task.start() - ai_voltage_task.wait_for_valid_timestamp(TimestampEvent.START_TRIGGER) + timestamp = ai_voltage_task.wait_for_valid_timestamp(TimestampEvent.START_TRIGGER) + + assert isinstance(timestamp, ht_datetime) def test___reference_trigger___wait_for_valid_timestamp___no_errors( @@ -101,7 +103,9 @@ def test___reference_trigger___wait_for_valid_timestamp___no_errors( ai_voltage_task.triggers.reference_trigger.timestamp_enable = True ai_voltage_task.start() - ai_voltage_task.wait_for_valid_timestamp(TimestampEvent.REFERENCE_TRIGGER) + timestamp = ai_voltage_task.wait_for_valid_timestamp(TimestampEvent.REFERENCE_TRIGGER) + + assert isinstance(timestamp, ht_datetime) def test___arm_start_trigger___wait_for_valid_timestamp___no_errors( @@ -112,7 +116,9 @@ def test___arm_start_trigger___wait_for_valid_timestamp___no_errors( ci_count_edges_task.triggers.arm_start_trigger.timestamp_enable = True ci_count_edges_task.start() - ci_count_edges_task.wait_for_valid_timestamp(TimestampEvent.ARM_START_TRIGGER) + timestamp = ci_count_edges_task.wait_for_valid_timestamp(TimestampEvent.ARM_START_TRIGGER) + + assert isinstance(timestamp, ht_datetime) def test___first_sample_trigger___wait_for_valid_timestamp___no_errors( @@ -121,8 +127,9 @@ def test___first_sample_trigger___wait_for_valid_timestamp___no_errors( ai_voltage_task.timing.cfg_samp_clk_timing(1000) ai_voltage_task.start() - ai_voltage_task.wait_for_valid_timestamp(TimestampEvent.FIRST_SAMPLE) + timestamp = ai_voltage_task.wait_for_valid_timestamp(TimestampEvent.FIRST_SAMPLE) + assert isinstance(timestamp, ht_datetime) assert ai_voltage_task.timing.first_samp_timestamp_enable assert ai_voltage_task.timing.first_samp_timestamp_timescale == Timescale.USE_HOST