示例#1
0
    def __init__(
        self,
        specifier: pyuavcan.transport.InputSessionSpecifier,
        payload_metadata: pyuavcan.transport.PayloadMetadata,
        finalizer: typing.Callable[[], None],
    ):
        """
        Do not call this directly, use the factory method instead.
        """
        self._statistics_impl = SelectiveUDPInputSessionStatistics()

        source_node_id = specifier.remote_node_id
        assert source_node_id is not None, "Internal protocol violation"

        def on_reassembly_error(error: TransferReassembler.Error) -> None:
            self._statistics.errors += 1
            try:
                self._statistics.reassembly_errors[error] += 1
            except LookupError:
                self._statistics.reassembly_errors[error] = 1

        self._reassembler = TransferReassembler(
            source_node_id=source_node_id,
            extent_bytes=payload_metadata.extent_bytes,
            on_error_callback=on_reassembly_error,
        )

        super().__init__(specifier=specifier, payload_metadata=payload_metadata, finalizer=finalizer)
示例#2
0
    def _process_frame(self, timestamp: Timestamp, frame: SerialFrame) -> None:
        """
        This is a part of the transport-internal API. It's a public method despite the name because Python's
        visibility handling capabilities are limited. I guess we could define a private abstract base to
        handle this but it feels like too much work. Why can't we have protected visibility in Python?
        """
        assert frame.data_specifier == self._specifier.data_specifier, "Internal protocol violation"
        self._statistics.frames += 1

        transfer: typing.Optional[pyuavcan.transport.TransferFrom]
        if frame.source_node_id is None:
            transfer = TransferReassembler.construct_anonymous_transfer(
                timestamp, frame)
            if transfer is None:
                self._statistics.errors += 1
                _logger.debug("%s: Invalid anonymous frame: %s", self, frame)
        else:
            transfer = self._get_reassembler(
                frame.source_node_id).process_frame(timestamp, frame,
                                                    self._transfer_id_timeout)
        if transfer is not None:
            self._statistics.transfers += 1
            self._statistics.payload_bytes += sum(
                map(len, transfer.fragmented_payload))
            _logger.debug("%s: Received transfer: %s; current stats: %s", self,
                          transfer, self._statistics)
            try:
                self._queue.put_nowait(transfer)
            except asyncio.QueueFull:  # pragma: no cover
                # TODO: make the queue capacity configurable
                self._statistics.drops += len(transfer.fragmented_payload)
示例#3
0
    def _get_reassembler(self, source_node_id: int) -> TransferReassembler:
        assert isinstance(
            source_node_id,
            int) and source_node_id >= 0, 'Internal protocol violation'
        try:
            return self._reassemblers[source_node_id]
        except LookupError:

            def on_reassembly_error(error: TransferReassembler.Error) -> None:
                self._statistics.errors += 1
                d = self._statistics.reassembly_errors_per_source_node_id[
                    source_node_id]
                try:
                    d[error] += 1
                except LookupError:
                    d[error] = 1

            self._statistics.reassembly_errors_per_source_node_id.setdefault(
                source_node_id, {})
            reasm = TransferReassembler(
                source_node_id=source_node_id,
                max_payload_size_bytes=self._payload_metadata.max_size_bytes,
                on_error_callback=on_reassembly_error)
            self._reassemblers[source_node_id] = reasm
            _logger.debug('%s: New %s (%d total)', self, reasm,
                          len(self._reassemblers))
            return reasm
示例#4
0
    def _get_reassembler(self, source_node_id: int) -> TransferReassembler:
        try:
            return self._reassemblers[source_node_id]
        except LookupError:

            def on_reassembly_error(error: TransferReassembler.Error) -> None:
                self._statistics.errors += 1
                d = self._statistics.reassembly_errors_per_source_node_id[
                    source_node_id]
                try:
                    d[error] += 1
                except LookupError:
                    d[error] = 1

            self._statistics.reassembly_errors_per_source_node_id.setdefault(
                source_node_id, {})
            reasm = TransferReassembler(
                source_node_id=source_node_id,
                extent_bytes=self._payload_metadata.extent_bytes,
                on_error_callback=on_reassembly_error,
            )
            self._reassemblers[source_node_id] = reasm
            _logger.debug("%s: New %s (%d total)", self, reasm,
                          len(self._reassemblers))
            return reasm
示例#5
0
    def update(self, timestamp: Timestamp,
               frame: SerialFrame) -> typing.Optional[Trace]:
        reasm = self._reassembler
        tid_timeout = reasm.transfer_id_timeout if reasm is not None else 0.0

        tr: typing.Union[TransferFrom, TransferReassembler.Error, None]
        if reasm is not None:
            tr = reasm.process_frame(timestamp, frame)
        else:
            tr = TransferReassembler.construct_anonymous_transfer(
                timestamp, frame)

        if isinstance(tr, TransferReassembler.Error):
            return SerialErrorTrace(timestamp=timestamp, error=tr)

        if isinstance(tr, TransferFrom):
            meta = AlienTransferMetadata(tr.priority, tr.transfer_id,
                                         self._specifier)
            return TransferTrace(timestamp,
                                 AlienTransfer(meta, tr.fragmented_payload),
                                 tid_timeout)

        assert tr is None
        return None