Exemple #1
0
async def _unittest_can_pythoncan_socketcan() -> None:
    asyncio.get_running_loop().slow_callback_duration = 5.0

    media_a = PythonCANMedia("socketcan:vcan2", 0, 8)
    media_b = PythonCANMedia("socketcan:vcan2", 0, 64)

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

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

    def on_rx_b(frames: typing.Iterable[typing.Tuple[Timestamp, Envelope]]) -> None:
        nonlocal rx_b
        rx_b += list(frames)

    media_a.start(on_rx_a, no_automatic_retransmission=False)
    media_b.start(on_rx_b, no_automatic_retransmission=False)

    ts_begin = Timestamp.now()
    await media_a.send(
        [
            Envelope(DataFrame(FrameFormat.EXTENDED, 0xBADC0FE, bytearray(b"123")), loopback=True),
            Envelope(DataFrame(FrameFormat.EXTENDED, 0x12345678, bytearray(b"456")), loopback=False),
        ],
        asyncio.get_event_loop().time() + 1.0,
    )
    await asyncio.sleep(1.0)
    ts_end = Timestamp.now()

    assert len(rx_b) == 2
    assert ts_begin.monotonic_ns <= rx_b[0][0].monotonic_ns <= ts_end.monotonic_ns
    assert ts_begin.monotonic_ns <= rx_b[1][0].monotonic_ns <= ts_end.monotonic_ns
    assert ts_begin.system_ns <= rx_b[0][0].system_ns <= ts_end.system_ns
    assert ts_begin.system_ns <= rx_b[1][0].system_ns <= ts_end.system_ns
    assert not rx_b[0][1].loopback
    assert not rx_b[1][1].loopback
    assert rx_b[0][1].frame.identifier == 0xBADC0FE
    assert rx_b[1][1].frame.identifier == 0x12345678
    assert rx_b[0][1].frame.data == b"123"
    assert rx_b[1][1].frame.data == b"456"

    assert len(rx_a) == 1
    assert ts_begin.monotonic_ns <= rx_a[0][0].monotonic_ns <= ts_end.monotonic_ns
    assert ts_begin.system_ns <= rx_a[0][0].system_ns <= ts_end.system_ns
    assert rx_a[0][1].loopback
    assert rx_a[0][1].frame.identifier == 0xBADC0FE
    assert rx_a[0][1].frame.data == b"123"

    media_a.close()
    media_b.close()
    media_a.close()  # Ensure idempotency.
    media_b.close()
Exemple #2
0
 def _parse_native_frame(msg: can.Message) -> typing.Optional[DataFrame]:
     if msg.is_error_frame:  # error frame, ignore silently
         _logger.debug("Error frame dropped: id_raw=%08x",
                       msg.arbitration_id)
         return None
     frame_format = FrameFormat.EXTENDED if msg.is_extended_id else FrameFormat.BASE
     data = msg.data
     return DataFrame(frame_format, msg.arbitration_id, data)
Exemple #3
0
 def _parse_native_frame(source: bytes) -> typing.Optional[DataFrame]:
     header_size = _FRAME_HEADER_STRUCT.size
     ident_raw, data_length, _flags = _FRAME_HEADER_STRUCT.unpack(source[:header_size])
     if (ident_raw & _CAN_RTR_FLAG) or (ident_raw & _CAN_ERR_FLAG):  # Unsupported format, ignore silently
         _logger.debug("Unsupported CAN frame dropped; raw SocketCAN ID is %08x", ident_raw)
         return None
     frame_format = FrameFormat.EXTENDED if ident_raw & _CAN_EFF_FLAG else FrameFormat.BASE
     data = source[header_size : header_size + data_length]
     assert len(data) == data_length
     ident = ident_raw & _CAN_EFF_MASK
     return DataFrame(frame_format, ident, bytearray(data))
Exemple #4
0
async def _unittest_can_pythoncan() -> None:
    asyncio.get_running_loop().slow_callback_duration = 5.0

    media_a = PythonCANMedia("virtual:0", 500000)
    media_b = PythonCANMedia("virtual:0", 500000)

    assert media_a.mtu == 8
    assert media_b.mtu == 8
    assert media_a.interface_name == "virtual:0"
    assert media_b.interface_name == "virtual:0"
    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

    rx_a: typing.List[typing.Tuple[Timestamp, Envelope]] = []
    rx_b: 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:
        nonlocal rx_b
        frames = list(frames)
        print("RX B:", frames)
        rx_b += frames

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

    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 read timeout properly

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

    print("rx_a:", rx_a)
    # Three received from another part
    assert len(rx_a) == 3
    for ts, _f in rx_a:
        assert ts_begin.monotonic_ns <= ts.monotonic_ns <= ts_end.monotonic_ns
        assert ts_begin.system_ns <= ts.system_ns <= ts_end.system_ns

    rx_external = list(filter(lambda x: True, rx_a))

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

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

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

    print("rx_b:", rx_b)
    # Two messages are loopback and were copied
    assert len(rx_b) == 2

    rx_loopback = list(filter(lambda x: True, rx_b))

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

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

    media_a.close()
    media_b.close()
Exemple #5
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.
Exemple #6
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.
Exemple #7
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()