Exemplo n.º 1
0
    def construct_subsystem(self, args: argparse.Namespace) -> object:
        """
        We use object instead of Node because the Node class requires generated code to be generated.
        """
        from pyuavcan import application
        from pyuavcan.application import heartbeat_publisher

        node_info = pyuavcan.dsdl.update_from_builtin(application.NodeInfo(), args.node_info_fields)
        _logger.debug('Node info: %r', node_info)

        transport = self._transport_factory.construct_subsystem(args)
        presentation = pyuavcan.presentation.Presentation(transport)
        node = application.Node(presentation, info=node_info)
        try:
            # Configure the heartbeat publisher.
            if args.heartbeat_fields.pop('uptime', None) is not None:
                _logger.warning('Specifying uptime has no effect because it will be overridden by the node.')
            node.heartbeat_publisher.health = \
                args.heartbeat_fields.pop('health', heartbeat_publisher.Health.NOMINAL)
            node.heartbeat_publisher.mode = \
                args.heartbeat_fields.pop('mode', heartbeat_publisher.Mode.OPERATIONAL)
            node.heartbeat_publisher.vendor_specific_status_code = args.heartbeat_fields.pop(
                'vendor_specific_status_code',
                os.getpid() & (2 ** min(pyuavcan.dsdl.get_model(heartbeat_publisher.Heartbeat)
                                        ['vendor_specific_status_code'].data_type.bit_length_set) - 1)
            )
            _logger.debug('Node heartbeat: %r', node.heartbeat_publisher.make_message())
            if args.heartbeat_fields:
                raise ValueError(f'Unrecognized heartbeat fields: {args.heartbeat_fields}')

            # Check the node-ID configuration.
            if not self._allow_anonymous and node.presentation.transport.local_node_id is None:
                raise ValueError('The specified transport is configured in anonymous mode, '
                                 'which cannot be used with the selected command. '
                                 'Please specify the node-ID explicitly, or use a different transport.')

            # Configure the transfer-ID map.
            # Register save on exit even if we're anonymous because the local node-ID may be provided later.
            self._register_output_transfer_id_map_save_at_exit(node.presentation)
            # Restore if we have a node-ID. If we don't, no restoration will take place even if the node-ID is
            # provided later. This behavior is acceptable for CLI; a regular UAVCAN application will not need
            # to deal with saving/restoration at all since this use case is specific to CLI only.
            if node.presentation.transport.local_node_id is not None:
                tid_map_path = _get_output_transfer_id_file_path(node.presentation.transport.local_node_id,
                                                                 node.presentation.transport.descriptor)
                _logger.debug('Output TID map file: %s', tid_map_path)
                tid_map = self._restore_output_transfer_id_map(tid_map_path)
                _logger.debug('Output TID map with %d records from %s', len(tid_map), tid_map_path)
                _logger.debug('Output TID map dump: %r', tid_map)
                # noinspection PyTypeChecker
                presentation.output_transfer_id_map.update(tid_map)  # type: ignore
            else:
                _logger.debug('Output TID map not restored because the local node is anonymous.')

            return node
        except Exception:
            node.close()
            raise
Exemplo n.º 2
0
    def __call__(self, transport: Transport, name_suffix: str,
                 allow_anonymous: bool) -> object:
        """
        We use ``object`` for return type instead of Node because the Node class requires generated code
        to be generated.
        """
        _logger.debug("Constructing node using %r with %r and name %r", self,
                      transport, name_suffix)
        if not re.match(r"[a-z][a-z0-9_]*[a-z0-9]",
                        name_suffix):  # pragma: no cover
            raise ValueError(
                f"Internal error: Poorly chosen node name suffix: {name_suffix!r}"
            )

        try:
            from pyuavcan import application
        except ImportError as ex:
            from yakut.cmd.compile import make_usage_suggestion

            raise click.UsageError(make_usage_suggestion(ex.name))

        try:
            node_info = pyuavcan.dsdl.update_from_builtin(
                application.NodeInfo(), self.node_info)
        except (ValueError, TypeError) as ex:
            raise click.UsageError(
                f"Node info fields are not valid: {ex}") from ex
        if len(node_info.name) == 0:
            node_info.name = f"org.uavcan.yakut.{name_suffix}"
        _logger.debug("Node info: %r", node_info)

        presentation = pyuavcan.presentation.Presentation(transport)
        node = application.Node(presentation, info=node_info)
        try:
            # Configure the heartbeat publisher.
            try:
                if self.heartbeat_period is not None:
                    node.heartbeat_publisher.period = self.heartbeat_period
                if self.heartbeat_priority is not None:
                    node.heartbeat_publisher.priority = self.heartbeat_priority
                if self.heartbeat_vssc is not None:
                    node.heartbeat_publisher.vendor_specific_status_code = self.heartbeat_vssc
                else:
                    node.heartbeat_publisher.vendor_specific_status_code = os.getpid(
                    ) % 100
            except ValueError as ex:
                raise click.UsageError(
                    f"Invalid heartbeat parameters: {ex}") from ex
            _logger.debug(
                "Node heartbeat: %s, period: %s, priority: %s",
                node.heartbeat_publisher.make_message(),
                node.heartbeat_publisher.period,
                node.heartbeat_publisher.priority,
            )

            # Check the node-ID configuration.
            if not allow_anonymous and node.presentation.transport.local_node_id is None:
                raise click.UsageError(
                    "The specified transport is configured in anonymous mode, which cannot be used with this command."
                )

            # Configure the transfer-ID map.
            # Register save on exit even if we're anonymous because the local node-ID may be provided later.
            _register_output_transfer_id_map_save_at_exit(node.presentation)
            # Restore if we have a node-ID. If we don't, no restoration will take place even if the node-ID is
            # provided later. This behavior is acceptable for CLI; a regular UAVCAN application will not need
            # to deal with saving/restoration at all since this use case is specific to CLI only.
            path = _get_output_transfer_id_map_path(
                node.presentation.transport)
            tid_map_restored = False
            if path is not None:
                tid_map = _restore_output_transfer_id_map(path)
                if tid_map:
                    _logger.debug("Restored output TID map from %s: %r", path,
                                  tid_map)
                    presentation.output_transfer_id_map.update(tid_map)
                    tid_map_restored = True
            if not tid_map_restored:
                _logger.debug("Could not restore output TID map from %s", path)

            _logger.info("Constructed node: %s", node)
            return node
        except Exception:
            node.close()
            raise