crappy.blocks.ioblock — Crappy 1.5.3 documentation

# Source code for crappy.blocks.ioblock

    # coding: utf-8

    from .block import Block
    from ..inout import inandout_dict, in_dict, out_dict, inout_dict

    class IOBlock(Block):
      """This block is used to communicate with :ref:`In / Out` objects.

      They can be used as sensor, actuators or both.
      """

      def __init__(self,
                   name,
                   freq=None,
                   verbose=False,
                   labels=None,
                   cmd_labels=None,
                   trigger=None,
                   streamer=False,
                   initial_cmd=0,
                   exit_values=None,
                   spam=False,
                   **kwargs):
        """Sets the args and initializes the parent class.

        Args:
          name (:obj:`str`): The name of the :ref:`In / Out` class to instantiate.
          freq (:obj:`float`, optional): The looping frequency of the block, if
            :obj:`None` will go as fast as possible.
          verbose (:obj:`bool`, optional): Prints extra information if :obj:`True`.
          labels (:obj:`list`, optional): A :obj:`list` of the output labels.
          cmd_labels (:obj:`list`, optional): The :obj:`list` of the labels
            considered as inputs for this block. Will call :meth:`set_cmd`  in the
            :ref:`In / Out` object with the values received on this labels.
          trigger (:obj:`int`, optional): If the block is triggered by another
            block, this must specify the index of the input considered as a
            trigger. The data going through this link is discarded, add another
            link if the block should also consider it as an input.
          streamer (:obj:`bool`, optional): If :obj:`False`, will call
            :meth:`get_data` else, will call :meth:`get_stream` in the
            :ref:`In / Out` object (only if it has these methods, of course).
          initial_cmd (:obj:`list`, optional): The initial values for the outputs,
            sent during :meth:`prepare`. If it is a single value, then it will send
            this same value for all the output labels.
          exit_values (:obj:`list`, optional): If not :obj:`None`, the outputs will
            be set to these values when Crappy is ending (or crashing).
          spam (:obj:`bool`, optional): If False (default), the block will only
            call set_cmd on the InOut object if the command changed
          **kwargs: The arguments to be passed to the :ref:`In / Out` class.
        """

        Block.__init__(self)
        self.niceness = -10
        self.freq = freq
        self.verbose = verbose
        self.labels = labels
        self.cmd_labels = [] if cmd_labels is None else cmd_labels
        self.trigger = trigger
        self.streamer = streamer
        self.initial_cmd = initial_cmd
        self.exit_values = exit_values
        self.spam = spam

        if self.labels is None:
          if self.streamer:
            self.labels = ['t(s)', 'stream']
          else:
            self.labels = ['t(s)'] + \
                          [str(c) for c in kwargs.get("channels", ['1'])]
        self.device_name = name.capitalize()
        self.device_kwargs = kwargs
        self.stream_idle = True
        if not isinstance(self.initial_cmd, list):
          self.initial_cmd = [self.initial_cmd] * len(self.cmd_labels)
        if not isinstance(self.exit_values, list) and self.exit_values is not None:
          self.exit_values = [self.exit_values] * len(self.cmd_labels)
        if self.exit_values is not None:
          assert len(self.exit_values) == len(self.cmd_labels),\
              'Invalid number of exit values!'
        self.device = inout_dict[self.device_name](**self.device_kwargs)

      def prepare(self):
        self.to_get = list(range(len(self.inputs)))
        if self.trigger is not None:
          self.to_get.remove(self.trigger)
        self.mode = 'r' if self.outputs else ''
        self.mode += 'w' if self.to_get else ''
        assert self.mode != '', "ERROR: IOBlock is neither an input nor an output!"
        if 'w' in self.mode:
          assert self.cmd_labels, "ERROR: IOBlock has an input block but no " \
                                  "cmd_labels specified!"
        if self.mode == 'rw' and self.device_name not in inandout_dict:
          raise IOError("The IOBlock has inputs and outputs but the Inout class "
                        "is not rw")
        elif self.mode == 'r' and self.device_name not in in_dict:
          raise IOError("The IOBlock has inputs but the Inout class is write-only")
        elif self.mode == 'w' and self.device_name not in out_dict:
          raise IOError("The IOBlock has outputs but the Inout class is read-only")
        self.device.open()
        if 'w' in self.mode:
          self.device.set_cmd(*self.initial_cmd)
          self.last = None

      def read(self):
        """Will read the device and send the data."""

        if self.streamer:
          if self.stream_idle:
            self.device.start_stream()
            self.stream_idle = False
          data = self.device.get_stream()
        else:
          data = self.device.get_data()
        if isinstance(data, dict):
          pass
        elif isinstance(data[0], list):
          data[0] = [i - self.t0 for i in data[0]]
        else:
          data[0] -= self.t0
        self.send(data)

      def loop(self):
        if 'r' in self.mode:
          if self.trigger is not None:
            # To avoid useless loops if triggered input only
            if self.mode == 'r' or self.inputs[self.trigger].poll():
              self.inputs[self.trigger].recv()
              self.read()
          else:
            self.read()
        if 'w' in self.mode:
          lst = self.get_last(self.to_get)
          cmd = [lst[label] for label in self.cmd_labels]
          if cmd != self.last or self.spam:
            self.device.set_cmd(*cmd)
            self.last = cmd

      def finish(self):
        if self.streamer:
          self.device.stop_stream()
        if self.exit_values is not None:
          self.device.set_cmd(*self.exit_values)
        self.device.close()