def __getitem__(self, index):
        """
        Indexes a subset of custom scales on this collection.

        Args:
            index: The value of the index. The following index types
                are supported:
                - str: Name of the custom scale. You also can specify
                    a string that contains a list or range of names to
                    this input. If you have a list of names, use the
                    DAQmx Flatten Channel String function to convert
                    the list to a string.
                - int: Index/position of the custom scale in the
                    collection.
                - slice: Range of the indexes/positions of custom scales
                    in the collection.
        Returns:
            List[nidaqmx.system.storage.persisted_scale.PersistedScale]:
            
            Indicates the subset of custom scales indexed.
        """
        if isinstance(index, six.integer_types):
            return PersistedScale(self.scale_names[index])
        elif isinstance(index, slice):
            return [PersistedScale(name) for name in self.scale_names[index]]
        elif isinstance(index, six.string_types):
            names = unflatten_channel_string(index)
            if len(names) == 1:
                return PersistedScale(names[0])
            return [PersistedScale(name) for name in names]
        else:
            raise DaqError(
                'Invalid index type "{0}" used to access collection.'.format(
                    type(index)), DAQmxErrors.UNKNOWN.value)
    def __getitem__(self, index):
        """
        Indexes a subset of physical channels on this physical channel
        collection.

        Args:
            index: The value of the index. The following index types
                are supported:
                - str: Name of the physical channel, without the
                    device name prefix, e.g. 'ai0'. You also can
                    specify a string that contains a list or range of
                    names to this input. If you have a list of names,
                    use the DAQmx Flatten Channel String function to
                    convert the list to a string.
                - int: Index/position of the physical channel in the
                    collection.
                - slice: Range of the indexes/positions of physical
                    channels in the collection.
        Returns:
            nidaqmx.system.physical_channel.PhysicalChannel: 
            
            Indicates the subset of physical channels indexed.
        """
        if isinstance(index, six.integer_types):
            return PhysicalChannel(self.channel_names[index])
        elif isinstance(index, slice):
            return PhysicalChannel(self.channel_names[index])
        elif isinstance(index, six.string_types):
            return PhysicalChannel('{0}/{1}'.format(self._name, index))
        else:
            raise DaqError(
                'Invalid index type "{0}" used to access collection.'.format(
                    type(index)), DAQmxErrors.UNKNOWN.value)
Beispiel #3
0
    def _raise_invalid_write_num_chans_error(
            self, number_of_channels, number_of_channels_in_data):

        raise DaqError(
            'Write cannot be performed, because the number of channels in the '
            'data does not match the number of channels in the task.\n\n'
            'When writing, supply data for all channels in the task. '
            'Alternatively, modify the task to contain the same number of '
            'channels as the data written.\n\n'
            'Number of Channels in Task: {0}\n'
            'Number of Channels in Data: {1}'
            .format(number_of_channels, number_of_channels_in_data),
            DAQmxErrors.WRITE_NUM_CHANS_MISMATCH.value, task_name=self.name)
    def __getitem__(self, index):
        """
        Indexes a subset of virtual channels on this channel collection.

        Args:
            index: The value of the index. The following index types are
                supported:
                - str: Name of the virtual channel. You also can specify a
                    string that contains a list or range of names to this
                    input. If you have a list of names, use the DAQmx
                    Flatten Channel String function to convert the list to a
                    string.
                - int: Index/position of the virtual channel in the collection.
                - slice: Range of the indexes/positions of virtual channels in
                    the collection.
        Returns:
            nidaqmx._task_modules.channels.channel.Channel: 
            
            Indicates a channel object representing the subset of virtual
            channels indexed.
        """
        if isinstance(index, six.integer_types):
            channel_names = self.channel_names[index]
        elif isinstance(index, slice):
            channel_names = flatten_channel_string(self.channel_names[index])
        elif isinstance(index, six.string_types):
            channel_names = index
        else:
            raise DaqError(
                'Invalid index type "{0}" used to access channels.'.format(
                    type(index)), DAQmxErrors.UNKNOWN)

        if channel_names:
            return Channel._factory(self._handle, channel_names)
        else:
            raise DaqError(
                'You cannot specify an empty index when indexing channels.\n'
                'Index used: {0}'.format(index), DAQmxErrors.UNKNOWN)
Beispiel #5
0
 def _raise_invalid_num_lines_error(
         self, num_lines_expected, num_lines_in_data):
     raise DaqError(
         'Specified read or write operation failed, because the number '
         'of lines in the data for a channel does not match the number '
         'of lines in the channel.\n\n'
         'If you are using boolean data, make sure the array dimension '
         'for lines in the data matches the number of lines in the '
         'channel.\n\n'
         'Number of Lines Per Channel in Task: {0}\n'
         'Number of Lines Per Channel in Data: {1}'
         .format(num_lines_expected, num_lines_in_data),
         DAQmxErrors.NUM_LINES_MISMATCH_IN_READ_OR_WRITE.value,
         task_name=self.name)
Beispiel #6
0
    def __getitem__(self, index):
        """
        Indexes an expiration state on this collection.

        Args:
            index (str): Name of the physical channel of which the
                expiration state to retrieve.
        Returns:
            nidaqmx.system._watchdog_modules.expiration_state.ExpirationState:
            
            The object representing the indexed expiration state.
        """
        if isinstance(index, six.string_types):
            return ExpirationState(self._handle, index)
        else:
            raise DaqError(
                'Invalid index type "{0}" used to access expiration states.'.
                format(type(index)), -1)
Beispiel #7
0
def unflatten_channel_string(channel_names):
    """
    Converts a comma-delimited list of channel names to a list of names.

    You can use this method to convert a comma-delimited list or range of
    physical or virtual channels into a list of physical or virtual channel
    names.

    Args:
        channel_names (str): The list or range of physical or virtual channels.
        
    Returns:
        List[str]: 
        
        The list of physical or virtual channel names. Each element of the 
        list contains a single channel.
    """
    channel_list_to_return = []
    channel_list = [c for c in channel_names.strip().split(',') if c]

    for channel in channel_list:
        channel = channel.strip()
        colon_index = channel.find(':')

        if colon_index == -1:
            channel_list_to_return.append(channel)
        else:
            before = channel[:colon_index]
            after = channel[colon_index + 1:]

            m_before = re.match('(.*?)([0-9]+)$', before)
            m_after = re.match('(.*?)([0-9]+)$', after)

            if not m_before or not m_after:
                raise DaqError(_invalid_range_syntax_message,
                               error_code=-200498)

            if m_after.group(1) and (m_before.group(1).lower() !=
                                     m_after.group(1).lower()):
                raise DaqError(_invalid_range_syntax_message,
                               error_code=-200498)

            num_before = int(m_before.group(2))
            num_after = int(m_after.group(2))
            num_max = max([num_before, num_after])
            num_min = min([num_before, num_after])
            number_of_channels = (num_max - num_min) + 1

            if number_of_channels >= 15000:
                raise DaqError(_invalid_range_syntax_message,
                               error_code=-200498)

            colon_expanded_channel = []
            for i in range(number_of_channels):
                colon_expanded_channel.append('{0}{1}'.format(
                    m_before.group(1), num_min + i))

            if num_after < num_before:
                colon_expanded_channel.reverse()

            channel_list_to_return.extend(colon_expanded_channel)

    return channel_list_to_return
Beispiel #8
0
    def read(self, number_of_samples_per_channel=NUM_SAMPLES_UNSET,
             timeout=10.0):
        """
        Reads samples from the task or virtual channels you specify.

        This read method is dynamic, and is capable of inferring an appropriate
        return type based on these factors:
        - The channel type of the task.
        - The number of channels to read.
        - The number of samples per channel.

        The data type of the samples returned is independently determined by
        the channel type of the task.

        For digital input measurements, the data type of the samples returned
        is determined by the line grouping format of the digital lines.
        If the line grouping format is set to "one channel for all lines", the
        data type of the samples returned is int. If the line grouping
        format is set to "one channel per line", the data type of the samples
        returned is boolean.

        If you do not set the number of samples per channel, this method
        assumes one sample was requested. This method then returns either a
        scalar (1 channel to read) or a list (N channels to read).

        If you set the number of samples per channel to ANY value (even 1),
        this method assumes multiple samples were requested. This method then
        returns either a list (1 channel to read) or a list of lists (N
        channels to read).

        Args:
            number_of_samples_per_channel (Optional[int]): Specifies the
                number of samples to read. If this input is not set,
                assumes samples to read is 1. Conversely, if this input
                is set, assumes there are multiple samples to read.

                If you set this input to nidaqmx.constants.
                READ_ALL_AVAILABLE, NI-DAQmx determines how many samples
                to read based on if the task acquires samples
                continuously or acquires a finite number of samples.

                If the task acquires samples continuously and you set
                this input to nidaqmx.constants.READ_ALL_AVAILABLE, this
                method reads all the samples currently available in the
                buffer.

                If the task acquires a finite number of samples and you
                set this input to nidaqmx.constants.READ_ALL_AVAILABLE,
                the method waits for the task to acquire all requested
                samples, then reads those samples. If you set the
                "read_all_avail_samp" property to True, the method reads
                the samples currently available in the buffer and does
                not wait for the task to acquire all requested samples.
            timeout (Optional[float]): Specifies the amount of time in
                seconds to wait for samples to become available. If the
                time elapses, the method returns an error and any
                samples read before the timeout elapsed. The default
                timeout is 10 seconds. If you set timeout to
                nidaqmx.constants.WAIT_INFINITELY, the method waits
                indefinitely. If you set timeout to 0, the method tries
                once to read the requested samples and returns an error
                if it is unable to.
        Returns:
            dynamic:

            The samples requested in the form of a scalar, a list, or a
            list of lists. See method docstring for more info.

            NI-DAQmx scales the data to the units of the measurement,
            including any custom scaling you apply to the channels. Use a
            DAQmx Create Channel method to specify these units.

        Example:
            >>> task = Task()
            >>> task.ai_channels.add_voltage_channel('Dev1/ai0:3')
            >>> data = task.read()
            >>> type(data)
            <type 'list'>
            >>> type(data[0])
            <type 'float'>
        """
        channels_to_read = self.in_stream.channels_to_read
        number_of_channels = len(channels_to_read.channel_names)
        read_chan_type = channels_to_read.chan_type

        num_samples_not_set = (number_of_samples_per_channel is
                               NUM_SAMPLES_UNSET)

        number_of_samples_per_channel = self._calculate_num_samps_per_chan(
            number_of_samples_per_channel)

        # Determine the array shape and size to create
        if number_of_channels > 1:
            if not num_samples_not_set:
                array_shape = (number_of_channels,
                               number_of_samples_per_channel)
            else:
                array_shape = number_of_channels
        else:
            array_shape = number_of_samples_per_channel

        # Analog Input
        if read_chan_type == ChannelType.ANALOG_INPUT:
            data = numpy.zeros(array_shape, dtype=numpy.float64)
            samples_read = _read_analog_f_64(
                self._handle, data, number_of_samples_per_channel, timeout)

        # Digital Input or Digital Output
        elif (read_chan_type == ChannelType.DIGITAL_INPUT or
                read_chan_type == ChannelType.DIGITAL_OUTPUT):
            if self.in_stream.di_num_booleans_per_chan == 1:
                data = numpy.zeros(array_shape, dtype=numpy.bool)
                samples_read = _read_digital_lines(
                    self._handle, data, number_of_samples_per_channel, timeout
                    ).samps_per_chan_read
            else:
                data = numpy.zeros(array_shape, dtype=numpy.uint32)
                samples_read = _read_digital_u_32(
                    self._handle, data, number_of_samples_per_channel, timeout)

        # Counter Input
        elif read_chan_type == ChannelType.COUNTER_INPUT:
            meas_type = channels_to_read.ci_meas_type

            if meas_type == UsageTypeCI.PULSE_FREQ:
                frequencies = numpy.zeros(array_shape, dtype=numpy.float64)
                duty_cycles = numpy.zeros(array_shape, dtype=numpy.float64)

                samples_read = _read_ctr_freq(
                    self._handle, frequencies, duty_cycles,
                    number_of_samples_per_channel, timeout)

                data = []
                for f, d in zip(frequencies, duty_cycles):
                    data.append(CtrFreq(freq=f, duty_cycle=d))

            elif meas_type == UsageTypeCI.PULSE_TIME:
                high_times = numpy.zeros(array_shape, dtype=numpy.float64)
                low_times = numpy.zeros(array_shape, dtype=numpy.float64)

                samples_read = _read_ctr_time(
                    self._handle, high_times, low_times,
                    number_of_samples_per_channel, timeout)
                data = []
                for h, l in zip(high_times, low_times):
                    data.append(CtrTime(high_time=h, low_time=l))

            elif meas_type == UsageTypeCI.PULSE_TICKS:
                high_ticks = numpy.zeros(array_shape, dtype=numpy.uint32)
                low_ticks = numpy.zeros(array_shape, dtype=numpy.uint32)

                samples_read = _read_ctr_ticks(
                    self._handle, high_ticks, low_ticks,
                    number_of_samples_per_channel, timeout)
                data = []
                for h, l in zip(high_ticks, low_ticks):
                    data.append(CtrTick(high_tick=h, low_tick=l))

            elif meas_type == UsageTypeCI.COUNT_EDGES:
                data = numpy.zeros(array_shape, dtype=numpy.uint32)

                samples_read = _read_counter_u_32_ex(
                    self._handle, data, number_of_samples_per_channel, timeout)

            else:
                data = numpy.zeros(array_shape, dtype=numpy.float64)

                samples_read = _read_counter_f_64_ex(
                    self._handle, data, number_of_samples_per_channel, timeout)
        else:
            raise DaqError(
                'Read failed, because there are no channels in this task from '
                'which data can be read.',
                DAQmxErrors.READ_NO_INPUT_CHANS_IN_TASK.value,
                task_name=self.name)

        if (read_chan_type == ChannelType.COUNTER_INPUT and
                (meas_type == UsageTypeCI.PULSE_FREQ or
                 meas_type == UsageTypeCI.PULSE_TICKS or
                 meas_type == UsageTypeCI.PULSE_TIME)):

            if num_samples_not_set and array_shape == 1:
                return data[0]
            # Counter pulse measurements should not have N channel versions.
            if samples_read != number_of_samples_per_channel:
                return data[:samples_read]
            return data

        if num_samples_not_set and array_shape == 1:
            return data.tolist()[0]

        if samples_read != number_of_samples_per_channel:
            if number_of_channels > 1:
                return data[:,:samples_read].tolist()
            else:
                return data[:samples_read].tolist()

        return data.tolist()
Beispiel #9
0
    def write(self, data, auto_start=AUTO_START_UNSET, timeout=10.0):
        """
        Writes samples to the task or virtual channels you specify.

        This write method is dynamic, and is capable of accepting the
        samples to write in the various forms for most operations:
        
        - Scalar: Single sample for 1 channel.
        - List/1D numpy.ndarray: Multiple samples for 1 channel or 1
          sample for multiple channels.
        - List of lists/2D numpy.ndarray: Multiple samples for multiple
          channels.

        The data type of the samples passed in must be appropriate for
        the channel type of the task.

        For counter output pulse operations, this write method only
        accepts samples in these forms:
        
        - Scalar CtrFreq, CtrTime, CtrTick (from nidaqmx.types): 
          Single sample for 1 channel.
        - List of CtrFreq, CtrTime, CtrTick (from nidaqmx.types):
          Multiple samples for 1 channel or 1 sample for multiple 
          channels.

        If the task uses on-demand timing, this method returns only
        after the device generates all samples. On-demand is the default
        timing type if you do not use the timing property on the task to
        configure a sample timing type. If the task uses any timing type
        other than on-demand, this method returns immediately and does
        not wait for the device to generate all samples. Your
        application must determine if the task is done to ensure that
        the device generated all samples.

        Args:
            data (dynamic): Contains the samples to write to the task.

                The data you write must be in the units of the
                generation, including any custom scales. Use the DAQmx
                Create Channel methods to specify these units.
            auto_start (Optional[bool]): Specifies if this method
                automatically starts the task if you did not explicitly
                start it with the DAQmx Start Task method.

                The default value of this parameter depends on whether
                you specify one sample or many samples to write to each
                channel. If one sample per channel was specified, the
                default value is True. If multiple samples per channel
                were specified, the default value is False.
            timeout (Optional[float]): Specifies the amount of time in
                seconds to wait for the method to write all samples.
                NI-DAQmx performs a timeout check only if the method
                must wait before it writes data. This method returns an
                error if the time elapses. The default timeout is 10
                seconds. If you set timeout to
                nidaqmx.constants.WAIT_INFINITELY, the method waits
                indefinitely. If you set timeout to 0, the method tries
                once to write the submitted samples. If the method could
                not write all the submitted samples, it returns an error
                and the number of samples successfully written.
        Returns:
            int:

            Specifies the actual number of samples this method
            successfully wrote.
        """
        channels_to_write = self.channels
        number_of_channels = len(channels_to_write.channel_names)
        write_chan_type = channels_to_write.chan_type

        element = None
        if number_of_channels == 1:
            if isinstance(data, list):
                if isinstance(data[0], list):
                    self._raise_invalid_write_num_chans_error(
                        number_of_channels, len(data))

                number_of_samples_per_channel = len(data)
                element = data[0]

            elif isinstance(data, numpy.ndarray):
                if len(data.shape) == 2:
                    self._raise_invalid_write_num_chans_error(
                        number_of_channels, data.shape[0])

                number_of_samples_per_channel = len(data)
                element = data[0]

            else:
                number_of_samples_per_channel = 1
                element = data

        else:
            if isinstance(data, list):
                if len(data) != number_of_channels:
                    self._raise_invalid_write_num_chans_error(
                        number_of_channels, len(data))

                if isinstance(data[0], list):
                    number_of_samples_per_channel = len(data[0])
                    element = data[0][0]
                else:
                    number_of_samples_per_channel = 1
                    element = data[0]

            elif isinstance(data, numpy.ndarray):
                if data.shape[0] != number_of_channels:
                    self._raise_invalid_write_num_chans_error(
                        number_of_channels, data.shape[0])

                if len(data.shape) == 2:
                    number_of_samples_per_channel = data.shape[1]
                    element = data[0][0]
                else:
                    number_of_samples_per_channel = 1
                    element = data[0]

            else:
                self._raise_invalid_write_num_chans_error(
                    number_of_channels, 1)

        if auto_start is AUTO_START_UNSET:
            if number_of_samples_per_channel > 1:
                auto_start = False
            else:
                auto_start = True

        # Analog Input
        if write_chan_type == ChannelType.ANALOG_OUTPUT:
            data = numpy.asarray(data, dtype=numpy.float64)
            return _write_analog_f_64(
                self._handle, data, number_of_samples_per_channel, auto_start,
                timeout)

        # Digital Input
        elif write_chan_type == ChannelType.DIGITAL_OUTPUT:
            if self.out_stream.do_num_booleans_per_chan == 1:
                if (not isinstance(element, bool) and
                        not isinstance(element, numpy.bool_)):
                    raise DaqError(
                        'Write failed, because this write method only accepts '
                        'boolean samples when there is one digital line per '
                        'channel in a task.\n\n'
                        'Requested sample type: {0}'.format(type(element)),
                        DAQmxErrors.UNKNOWN.value, task_name=self.name)

                data = numpy.asarray(data, dtype=numpy.bool)
                return _write_digital_lines(
                    self._handle, data, number_of_samples_per_channel,
                    auto_start, timeout)
            else:
                if (not isinstance(element, six.integer_types) and
                        not isinstance(element, numpy.uint32)):
                    raise DaqError(
                        'Write failed, because this write method only accepts '
                        'unsigned 32-bit integer samples when there are '
                        'multiple digital lines per channel in a task.\n\n'
                        'Requested sample type: {0}'.format(type(element)),
                        DAQmxErrors.UNKNOWN.value, task_name=self.name)

                data = numpy.asarray(data, dtype=numpy.uint32)
                return _write_digital_u_32(
                    self._handle, data, number_of_samples_per_channel,
                    auto_start, timeout)

        # Counter Input
        elif write_chan_type == ChannelType.COUNTER_OUTPUT:
            output_type = channels_to_write.co_output_type

            if number_of_samples_per_channel == 1:
                data = [data]

            if output_type == UsageTypeCO.PULSE_FREQUENCY:
                frequencies = []
                duty_cycles = []
                for sample in data:
                    frequencies.append(sample.duty_cycle)
                    duty_cycles.append(sample.freq)

                frequencies = numpy.asarray(frequencies, dtype=numpy.float64)
                duty_cycles = numpy.asarray(duty_cycles, dtype=numpy.float64)

                return _write_ctr_freq(
                    self._handle, frequencies, duty_cycles,
                    number_of_samples_per_channel, auto_start, timeout)

            elif output_type == UsageTypeCO.PULSE_TIME:
                high_times = []
                low_times = []
                for sample in data:
                    high_times.append(sample.high_time)
                    low_times.append(sample.low_time)

                high_times = numpy.asarray(high_times, dtype=numpy.float64)
                low_times = numpy.asarray(low_times, dtype=numpy.float64)

                return _write_ctr_time(
                    self._handle, high_times, low_times,
                    number_of_samples_per_channel, auto_start, timeout)

            elif output_type == UsageTypeCO.PULSE_TICKS:
                high_ticks = []
                low_ticks = []
                for sample in data:
                    high_ticks.append(sample.high_tick)
                    low_ticks.append(sample.low_tick)

                high_ticks = numpy.asarray(high_ticks, dtype=numpy.uint32)
                low_ticks = numpy.asarray(low_ticks, dtype=numpy.uint32)

                return _write_ctr_ticks(
                    self._handle, high_ticks, low_ticks,
                    number_of_samples_per_channel, auto_start, timeout)
        else:
            raise DaqError(
                'Write failed, because there are no output channels in this '
                'task to which data can be written.',
                DAQmxErrors.WRITE_NO_OUTPUT_CHANS_IN_TASK.value,
                task_name=self.name)