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()
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)
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))
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()
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.
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.
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()