Example #1
0
async def _unittest_can_mock_media() -> None:
    peers: typing.Set[MockMedia] = set()

    me = MockMedia(peers, 64, 3)
    assert len(peers) == 1 and me in peers
    assert me.mtu == 64
    assert me.number_of_acceptance_filters == 3
    assert not me.automatic_retransmission_enabled
    assert str(me) == f"MockMedia('mock@{id(peers):08x}', mtu=64)"

    me_collector = FrameCollector()
    me.start(me_collector.give, False)
    assert me.automatic_retransmission_enabled

    # Will drop the loopback because of the acceptance filters
    await me.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"abc")),
                     loopback=False),
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"def")),
                     loopback=True),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    assert me_collector.empty

    me.configure_acceptance_filters([FilterConfiguration.new_promiscuous()])
    # Now the loopback will be accepted because we have reconfigured the filters
    await me.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"abc")),
                     loopback=False),
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"def")),
                     loopback=True),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    assert me_collector.pop()[1].frame == DataFrame(FrameFormat.EXTENDED, 123,
                                                    bytearray(b"def"))
    assert me_collector.empty

    pe = MockMedia(peers, 8, 1)
    assert peers == {me, pe}

    pe_collector = FrameCollector()
    pe.start(pe_collector.give, False)

    me.raise_on_send_once(RuntimeError("Hello world!"))
    with pytest.raises(RuntimeError, match="Hello world!"):
        await me.send([], asyncio.get_event_loop().time() + 1.0)

    await me.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"abc")),
                     loopback=False),
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"def")),
                     loopback=True),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    assert pe_collector.empty

    pe.configure_acceptance_filters([FilterConfiguration(123, 127, None)])
    await me.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"abc")),
                     loopback=False),
            Envelope(DataFrame(FrameFormat.EXTENDED, 123, bytearray(b"def")),
                     loopback=True),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    await me.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 456, bytearray(b"ghi")),
                     loopback=False),  # Dropped by the filters
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    assert pe_collector.pop()[1].frame == DataFrame(FrameFormat.EXTENDED, 123,
                                                    bytearray(b"abc"))
    assert pe_collector.pop()[1].frame == DataFrame(FrameFormat.EXTENDED, 123,
                                                    bytearray(b"def"))
    assert pe_collector.empty

    me.close()
    me.close()  # Idempotency.
    assert peers == {pe}
    with pytest.raises(pyuavcan.transport.ResourceClosedError):
        await me.send([], asyncio.get_event_loop().time() + 1.0)
    with pytest.raises(pyuavcan.transport.ResourceClosedError):
        me.configure_acceptance_filters([])
    await asyncio.sleep(
        1
    )  # Let all pending tasks finalize properly to avoid stack traces in the output.
Example #2
0
 def _make_dead_filter() -> FilterConfiguration:
     fmt = FrameFormat.BASE
     return FilterConfiguration(0, 2**int(fmt) - 1, fmt)
Example #3
0
async def _unittest_can_socketcan() -> None:
    from pyuavcan.transport import Timestamp
    from pyuavcan.transport.can.media import TimestampedDataFrame, DataFrame, FrameFormat, FilterConfiguration

    if sys.platform != 'linux':  # pragma: no cover
        pytest.skip(
            'SocketCAN test skipped because we do not seem to be on a GNU/Linux-based system'
        )

    from pyuavcan.transport.can.media.socketcan import SocketCANMedia
    available = SocketCANMedia.list_available_interface_names()
    print('Available SocketCAN ifaces:', available)
    assert 'vcan0' in available, \
        'Either the interface listing method is not working or the environment is not configured correctly. ' \
        'Please ensure that the virtual SocketCAN interface "vcan0" is available, and its MTU is set to 64+8.'

    media_a = SocketCANMedia('vcan0', 12)
    media_b = SocketCANMedia('vcan0', 64)

    assert media_a.mtu == 12
    assert media_b.mtu == 64
    assert media_a.interface_name == 'vcan0'
    assert media_b.interface_name == 'vcan0'
    assert media_a.number_of_acceptance_filters == media_b.number_of_acceptance_filters
    assert media_a._maybe_thread is None
    assert media_b._maybe_thread is None

    media_a.configure_acceptance_filters(
        [FilterConfiguration.new_promiscuous()])
    media_b.configure_acceptance_filters(
        [FilterConfiguration.new_promiscuous()])

    rx_a: typing.List[TimestampedDataFrame] = []

    def on_rx_a(frames: typing.Iterable[TimestampedDataFrame]) -> None:
        nonlocal rx_a
        frames = list(frames)
        print('RX A:', frames)
        rx_a += frames

    def on_rx_b(frames: typing.Iterable[TimestampedDataFrame]) -> None:
        frames = list(frames)
        print('RX B:', frames)
        asyncio.ensure_future(
            media_b.send_until(frames,
                               asyncio.get_event_loop().time() + 1.0))

    media_a.start(on_rx_a, False)
    media_b.start(on_rx_b, True)

    assert media_a._maybe_thread is not None
    assert media_b._maybe_thread is not None

    await asyncio.sleep(
        2.0
    )  # This wait is needed to ensure that the RX thread handles select() timeout properly

    ts_begin = Timestamp.now()
    await media_a.send_until([
        DataFrame(identifier=0xbadc0fe,
                  data=bytearray(range(8)),
                  format=FrameFormat.EXTENDED,
                  loopback=True),
        DataFrame(identifier=0x12345678,
                  data=bytearray(range(0)),
                  format=FrameFormat.EXTENDED,
                  loopback=False),
        DataFrame(identifier=0x123,
                  data=bytearray(range(6)),
                  format=FrameFormat.BASE,
                  loopback=True),
    ],
                             asyncio.get_event_loop().time() + 1.0)
    await asyncio.sleep(0.1)
    ts_end = Timestamp.now()

    print('rx_a:', rx_a)
    # Three sent back from the other end, two loopback
    assert len(rx_a) == 5
    for f in rx_a:
        assert ts_begin.monotonic_ns <= f.timestamp.monotonic_ns <= ts_end.monotonic_ns
        assert ts_begin.system_ns <= f.timestamp.system_ns <= ts_end.system_ns

    rx_loopback = list(filter(lambda x: x.loopback, rx_a))
    rx_external = list(filter(lambda x: not x.loopback, rx_a))
    assert len(rx_loopback) == 2 and len(rx_external) == 3

    assert rx_loopback[0].identifier == 0xbadc0fe
    assert rx_loopback[0].data == bytearray(range(8))
    assert rx_loopback[0].format == FrameFormat.EXTENDED

    assert rx_loopback[1].identifier == 0x123
    assert rx_loopback[1].data == bytearray(range(6))
    assert rx_loopback[1].format == FrameFormat.BASE

    assert rx_external[0].identifier == 0xbadc0fe
    assert rx_external[0].data == bytearray(range(8))
    assert rx_external[0].format == FrameFormat.EXTENDED

    assert rx_external[1].identifier == 0x12345678
    assert rx_external[1].data == bytearray(range(0))
    assert rx_external[1].format == FrameFormat.EXTENDED

    assert rx_external[2].identifier == 0x123
    assert rx_external[2].data == bytearray(range(6))
    assert rx_external[2].format == FrameFormat.BASE

    media_a.close()
    media_b.close()

    await asyncio.sleep(
        1
    )  # Let all pending tasks finalize properly to avoid stack traces in the output.
Example #4
0
async def _unittest_can_socketcan() -> None:
    from pyuavcan.transport import Timestamp
    from pyuavcan.transport.can.media import Envelope, DataFrame, FrameFormat, FilterConfiguration

    from pyuavcan.transport.can.media.socketcan import SocketCANMedia

    available = SocketCANMedia.list_available_interface_names()
    print("Available SocketCAN ifaces:", available)
    assert "vcan0" in available, (
        "Either the interface listing method is not working or the environment is not configured correctly. "
        'Please ensure that the virtual SocketCAN interface "vcan0" is available, and its MTU is set to 64+8.'
    )

    media_a = SocketCANMedia("vcan0", 12)
    media_b = SocketCANMedia("vcan0", 64)

    assert media_a.mtu == 12
    assert media_b.mtu == 64
    assert media_a.interface_name == "vcan0"
    assert media_b.interface_name == "vcan0"
    assert media_a.number_of_acceptance_filters == media_b.number_of_acceptance_filters
    assert media_a._maybe_thread is None  # pylint: disable=protected-access
    assert media_b._maybe_thread is None  # pylint: disable=protected-access

    media_a.configure_acceptance_filters(
        [FilterConfiguration.new_promiscuous()])
    media_b.configure_acceptance_filters(
        [FilterConfiguration.new_promiscuous()])

    rx_a: typing.List[typing.Tuple[Timestamp, Envelope]] = []

    def on_rx_a(
            frames: typing.Iterable[typing.Tuple[Timestamp,
                                                 Envelope]]) -> None:
        nonlocal rx_a
        frames = list(frames)
        print("RX A:", frames)
        rx_a += frames

    def on_rx_b(
            frames: typing.Iterable[typing.Tuple[Timestamp,
                                                 Envelope]]) -> None:
        frames = list(frames)
        print("RX B:", frames)
        asyncio.ensure_future(
            media_b.send((e for _, e in frames),
                         asyncio.get_event_loop().time() + 1.0))

    media_a.start(on_rx_a, False)
    media_b.start(on_rx_b, True)

    assert media_a._maybe_thread is not None  # pylint: disable=protected-access
    assert media_b._maybe_thread is not None  # pylint: disable=protected-access

    await asyncio.sleep(
        2.0
    )  # This wait is needed to ensure that the RX thread handles select() timeout properly

    ts_begin = Timestamp.now()
    await media_a.send(
        [
            Envelope(DataFrame(FrameFormat.BASE, 0x123, bytearray(range(6))),
                     loopback=True),
            Envelope(DataFrame(FrameFormat.EXTENDED, 0x1BADC0FE,
                               bytearray(range(8))),
                     loopback=True),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    await media_a.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 0x1FF45678,
                               bytearray(range(0))),
                     loopback=False),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    await asyncio.sleep(1.0)
    ts_end = Timestamp.now()

    print("rx_a:", rx_a)
    # Three sent back from the other end, two loopback
    assert len(rx_a) == 5
    for t, _ in rx_a:
        assert ts_begin.monotonic_ns <= t.monotonic_ns <= ts_end.monotonic_ns
        assert ts_begin.system_ns <= t.system_ns <= ts_end.system_ns

    rx_loopback = [e.frame for t, e in rx_a if e.loopback]
    rx_external = [e.frame for t, e in rx_a if not e.loopback]
    assert len(rx_loopback) == 2 and len(rx_external) == 3

    assert rx_loopback[0].identifier == 0x123
    assert rx_loopback[0].data == bytearray(range(6))
    assert rx_loopback[0].format == FrameFormat.BASE

    assert rx_loopback[1].identifier == 0x1BADC0FE
    assert rx_loopback[1].data == bytearray(range(8))
    assert rx_loopback[1].format == FrameFormat.EXTENDED

    assert rx_external[0].identifier == 0x123
    assert rx_external[0].data == bytearray(range(6))
    assert rx_external[0].format == FrameFormat.BASE

    assert rx_external[1].identifier == 0x1BADC0FE
    assert rx_external[1].data == bytearray(range(8))
    assert rx_external[1].format == FrameFormat.EXTENDED

    assert rx_external[2].identifier == 0x1FF45678
    assert rx_external[2].data == bytearray(range(0))
    assert rx_external[2].format == FrameFormat.EXTENDED

    media_a.close()
    media_b.close()

    await asyncio.sleep(
        1
    )  # Let all pending tasks finalize properly to avoid stack traces in the output.
async def _unittest_can_mock_media() -> None:
    import asyncio
    from pyuavcan.transport.can.media import DataFrame, FrameFormat, FilterConfiguration

    peers: typing.Set[MockMedia] = set()

    me = MockMedia(peers, 64, 3)
    assert len(peers) == 1 and me in peers
    assert me.mtu == 64
    assert me.number_of_acceptance_filters == 3
    assert not me.automatic_retransmission_enabled
    assert str(me) == f"MockMedia(interface_name='mock@{id(peers):08x}', mtu=64)"

    me_collector = FrameCollector()
    me.start(me_collector.give, False)
    assert me.automatic_retransmission_enabled

    # Will drop the loopback because of the acceptance filters
    await me.send_until([
        DataFrame(123, bytearray(b'abc'), FrameFormat.EXTENDED, loopback=False),
        DataFrame(123, bytearray(b'def'), FrameFormat.EXTENDED, loopback=True),
    ], asyncio.get_event_loop().time() + 1.0)
    assert me_collector.empty

    me.configure_acceptance_filters([FilterConfiguration.new_promiscuous()])
    # Now the loopback will be accepted because we have reconfigured the filters
    await me.send_until([
        DataFrame(123, bytearray(b'abc'), FrameFormat.EXTENDED, loopback=False),
        DataFrame(123, bytearray(b'def'), FrameFormat.EXTENDED, loopback=True),
    ], asyncio.get_event_loop().time() + 1.0)
    assert me_collector.pop().is_same_manifestation(
        DataFrame(123, bytearray(b'def'), FrameFormat.EXTENDED, loopback=True))
    assert me_collector.empty

    pe = MockMedia(peers, 8, 1)
    assert peers == {me, pe}

    pe_collector = FrameCollector()
    pe.start(pe_collector.give, False)

    me.raise_on_send_once(RuntimeError('Hello world!'))
    with pytest.raises(RuntimeError, match='Hello world!'):
        await me.send_until([], asyncio.get_event_loop().time() + 1.0)

    await me.send_until([
        DataFrame(123, bytearray(b'abc'), FrameFormat.EXTENDED, loopback=False),
        DataFrame(123, bytearray(b'def'), FrameFormat.EXTENDED, loopback=True),
    ], asyncio.get_event_loop().time() + 1.0)
    assert pe_collector.empty

    pe.configure_acceptance_filters([FilterConfiguration(123, 127, None)])
    await me.send_until([
        DataFrame(123, bytearray(b'abc'), FrameFormat.EXTENDED, loopback=False),
        DataFrame(123, bytearray(b'def'), FrameFormat.EXTENDED, loopback=True),
    ], asyncio.get_event_loop().time() + 1.0)
    await me.send_until([
        DataFrame(456, bytearray(b'ghi'), FrameFormat.EXTENDED, loopback=False),    # Dropped by the filters
    ], asyncio.get_event_loop().time() + 1.0)
    assert pe_collector.pop().is_same_manifestation(
        DataFrame(123, bytearray(b'abc'), FrameFormat.EXTENDED, loopback=False))
    assert pe_collector.pop().is_same_manifestation(
        DataFrame(123, bytearray(b'def'), FrameFormat.EXTENDED, loopback=False))
    assert pe_collector.empty

    me.close()
    assert peers == {pe}
    with pytest.raises(pyuavcan.transport.ResourceClosedError):
        await me.send_until([], asyncio.get_event_loop().time() + 1.0)
    with pytest.raises(pyuavcan.transport.ResourceClosedError):
        me.configure_acceptance_filters([])
    with pytest.raises(pyuavcan.transport.ResourceClosedError):
        me.close()
Example #6
0
async def _unittest_can_capture_trace() -> None:
    from pyuavcan.transport import MessageDataSpecifier, PayloadMetadata, Transfer, Priority, Timestamp
    from pyuavcan.transport import InputSessionSpecifier, OutputSessionSpecifier, TransferTrace
    from .media.mock import MockMedia
    from pyuavcan.transport.can import CANCapture
    from pyuavcan.transport.can.media import FilterConfiguration, FrameFormat

    asyncio.get_running_loop().slow_callback_duration = 5.0

    ts = Timestamp.now()

    peers: typing.Set[MockMedia] = set()
    media = MockMedia(peers, 64, 2)
    media2 = MockMedia(peers, 64, 2)

    tr = can.CANTransport(media, None)
    tr2 = can.CANTransport(media2, 51)

    captures: typing.List[CANCapture] = []
    captures_other: typing.List[CANCapture] = []

    def add_capture(cap: pyuavcan.transport.Capture) -> None:
        assert isinstance(cap, CANCapture)
        captures.append(cap)

    def add_capture_other(cap: pyuavcan.transport.Capture) -> None:
        assert isinstance(cap, CANCapture)
        captures_other.append(cap)

    tr.begin_capture(add_capture)
    tr.begin_capture(add_capture_other)
    assert media.acceptance_filters == [
        FilterConfiguration.new_promiscuous(FrameFormat.BASE),
        FilterConfiguration.new_promiscuous(FrameFormat.EXTENDED),
    ]

    a_out = tr.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(2345), None), PayloadMetadata(800))
    b_out = tr2.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(5432), None), PayloadMetadata(800))

    # Ensure the filter configuration is not reset when creating new subscriptions.
    a_in = tr2.get_input_session(InputSessionSpecifier(MessageDataSpecifier(2345), None), PayloadMetadata(800))
    assert media.acceptance_filters == [
        FilterConfiguration.new_promiscuous(FrameFormat.BASE),
        FilterConfiguration.new_promiscuous(FrameFormat.EXTENDED),
    ]

    # Send transfers to collect some captures.
    assert await a_out.send(
        Transfer(ts, Priority.NOMINAL, transfer_id=11, fragmented_payload=[memoryview(b"first")]),
        monotonic_deadline=tr.loop.time() + 2.0,
    )
    await asyncio.sleep(1.0)  # Let messages propagate.
    assert await b_out.send(
        Transfer(ts, Priority.NOMINAL, transfer_id=22, fragmented_payload=[memoryview(b"second")]),
        monotonic_deadline=tr.loop.time() + 2.0,
    )
    transfer = await a_in.receive(tr.loop.time() + 2.0)
    assert transfer
    assert transfer.transfer_id == 11
    await asyncio.sleep(1.0)  # Let messages propagate.

    # Validate the captures.
    assert captures == captures_other
    assert len(captures) == 2  # One sent, one received.
    assert captures[0].own
    assert b"first" in captures[0].frame.data
    assert not captures[1].own
    assert b"second" in captures[1].frame.data

    # Check the loopback stats.
    assert tr.sample_statistics().in_frames == 1
    assert tr.sample_statistics().in_frames_loopback == 1
    assert tr2.sample_statistics().in_frames == 1
    assert tr2.sample_statistics().in_frames_loopback == 0

    # Perform basic tracer test (the full test is implemented separately).
    tracer = tr.make_tracer()
    trc = tracer.update(captures[0])
    assert isinstance(trc, TransferTrace)
    assert b"first" in trc.transfer.fragmented_payload[0].tobytes()
    trc = tracer.update(captures[1])
    assert isinstance(trc, TransferTrace)
    assert b"second" in trc.transfer.fragmented_payload[0].tobytes()