def get_transport( node_id: typing.Optional[int]) -> pyuavcan.transport.Transport: from pyuavcan.transport.udp import UDPTransport return UDPTransport( f"127.0.0.{node_id}") if node_id is not None else UDPTransport( "127.0.0.1", anonymous=True)
def one(nid: typing.Optional[int]) -> RedundantTransport: red = RedundantTransport() red.attach_inferior( UDPTransport(f'127.0.0.{nid}/8') if nid is not None else UDPTransport('127.255.255.255/8')) red.attach_inferior(SerialTransport(VIRTUAL_BUS_URI, nid)) print('REDUNDANT TRANSPORT UDP+SERIAL:', red) return red
def make_udp_serial(nid: typing.Optional[int]) -> pyuavcan.transport.Transport: tr = RedundantTransport() if nid is not None: tr.attach_inferior(UDPTransport(f"127.0.0.{nid}")) else: tr.attach_inferior(UDPTransport(f"127.0.0.1", anonymous=True)) tr.attach_inferior(SerialTransport("socket://localhost:50905", local_node_id=nid)) return tr
def one(nid: typing.Optional[int]) -> RedundantTransport: red = RedundantTransport() if nid is not None: red.attach_inferior(UDPTransport(f"127.0.0.{nid}")) else: red.attach_inferior(UDPTransport("127.0.0.1", anonymous=True)) red.attach_inferior(SerialTransport(VIRTUAL_BUS_URI, nid)) print("UDP+SERIAL:", red) return red
def _make_udp( registers: MutableMapping[str, ValueProxy], node_id: Optional[int]) -> Iterator[pyuavcan.transport.Transport]: def init(name: str, default: RelaxedValue) -> ValueProxy: return registers.setdefault("uavcan.udp." + name, ValueProxy(default)) ip_list = str(init("iface", "")).split() mtu = int(init("mtu", Natural16([1200]))) srv_mult = int(init("duplicate_service_transfers", False)) + 1 if ip_list: from pyuavcan.transport.udp import UDPTransport for ip in ip_list: yield UDPTransport(ip, node_id, mtu=mtu, service_transfer_multiplier=srv_mult)
def get_transport( node_id: typing.Optional[int]) -> pyuavcan.transport.Transport: from pyuavcan.transport.udp import UDPTransport return UDPTransport("127.0.0.1", local_node_id=node_id)
def one(nid: typing.Optional[int]) -> UDPTransport: return UDPTransport( f"127.0.0.{nid}") if nid is not None else UDPTransport( "127.0.0.1", anonymous=True)
async def _unittest_udp_transport() -> None: from pyuavcan.transport import MessageDataSpecifier, ServiceDataSpecifier, PayloadMetadata, Transfer, TransferFrom from pyuavcan.transport import Priority, Timestamp, InputSessionSpecifier, OutputSessionSpecifier from pyuavcan.transport import ProtocolParameters get_monotonic = asyncio.get_event_loop().time with pytest.raises(ValueError): _ = UDPTransport(ip_address='127.0.0.111/8', mtu=10) with pytest.raises(ValueError): _ = UDPTransport(ip_address='127.0.0.111/8', service_transfer_multiplier=100) tr = UDPTransport('127.0.0.111/8', mtu=9000) tr2 = UDPTransport('127.0.0.222/8', service_transfer_multiplier=2) assert tr.local_ip_address_with_netmask == '127.0.0.111/8' assert tr2.local_ip_address_with_netmask == '127.0.0.222/8' assert tr.loop is asyncio.get_event_loop() assert tr.local_node_id == 111 assert tr2.local_node_id == 222 assert tr.input_sessions == [] assert tr.output_sessions == [] assert list(xml.etree.ElementTree.fromstring( tr.descriptor).itertext()) == ['127.0.0.111/8'] assert tr.protocol_parameters == ProtocolParameters( transfer_id_modulo=2**56, max_nodes=2**UDPTransport.NODE_ID_BIT_LENGTH, mtu=9000, ) assert list(xml.etree.ElementTree.fromstring( tr2.descriptor).itertext()) == ['127.0.0.222/8'] assert tr2.protocol_parameters == ProtocolParameters( transfer_id_modulo=2**56, max_nodes=2**UDPTransport.NODE_ID_BIT_LENGTH, mtu=UDPTransport.DEFAULT_MTU, ) assert tr.sample_statistics() == tr2.sample_statistics( ) == UDPTransportStatistics() payload_single = [_mem('qwertyui'), _mem('01234567') ] * (UDPTransport.DEFAULT_MTU // 16) assert sum(map(len, payload_single)) == UDPTransport.DEFAULT_MTU payload_x3 = (payload_single * 3)[:-1] payload_x3_size_bytes = UDPTransport.DEFAULT_MTU * 3 - 8 assert sum(map(len, payload_x3)) == payload_x3_size_bytes # # Instantiate session objects. # meta = PayloadMetadata(0x_bad_c0ffee_0dd_f00d, 10000) broadcaster = tr2.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(12345), None), meta) assert broadcaster is tr2.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(12345), None), meta) subscriber_promiscuous = tr.get_input_session( InputSessionSpecifier(MessageDataSpecifier(12345), None), meta) assert subscriber_promiscuous is tr.get_input_session( InputSessionSpecifier(MessageDataSpecifier(12345), None), meta) subscriber_selective = tr.get_input_session( InputSessionSpecifier(MessageDataSpecifier(12345), 123), meta) assert subscriber_selective is tr.get_input_session( InputSessionSpecifier(MessageDataSpecifier(12345), 123), meta) server_listener = tr.get_input_session( InputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), None), meta) assert server_listener is tr.get_input_session( InputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), None), meta) server_responder = tr.get_output_session( OutputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 222), meta) assert server_responder is tr.get_output_session( OutputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 222), meta) client_requester = tr2.get_output_session( OutputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), 111), meta) assert client_requester is tr2.get_output_session( OutputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), 111), meta) client_listener = tr2.get_input_session( InputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 111), meta) assert client_listener is tr2.get_input_session( InputSessionSpecifier( ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 111), meta) print('tr :', tr.input_sessions, tr.output_sessions) assert set(tr.input_sessions) == { subscriber_promiscuous, subscriber_selective, server_listener } assert set(tr.output_sessions) == {server_responder} print('tr2:', tr2.input_sessions, tr2.output_sessions) assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {broadcaster, client_requester} assert tr.sample_statistics().demultiplexer[MessageDataSpecifier( 12345)].accepted_datagrams == {} assert tr.sample_statistics().demultiplexer[ServiceDataSpecifier( 444, ServiceDataSpecifier.Role.REQUEST)].accepted_datagrams == {} assert tr2.sample_statistics().demultiplexer[ServiceDataSpecifier( 444, ServiceDataSpecifier.Role.RESPONSE)].accepted_datagrams == {} # # Message exchange test. # assert await broadcaster.send_until( Transfer(timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=77777, fragmented_payload=payload_single), monotonic_deadline=get_monotonic() + 5.0) rx_transfer = await subscriber_promiscuous.receive_until(get_monotonic() + 5.0) print('PROMISCUOUS SUBSCRIBER TRANSFER:', rx_transfer) assert isinstance(rx_transfer, TransferFrom) assert rx_transfer.priority == Priority.LOW assert rx_transfer.transfer_id == 77777 assert rx_transfer.fragmented_payload == [b''.join(payload_single) ] # type: ignore print('tr :', tr.sample_statistics()) assert tr.sample_statistics().demultiplexer[MessageDataSpecifier( 12345)].accepted_datagrams == { 222: 1 } assert tr.sample_statistics().demultiplexer[ServiceDataSpecifier( 444, ServiceDataSpecifier.Role.REQUEST)].accepted_datagrams == {} print('tr2:', tr2.sample_statistics()) assert tr2.sample_statistics().demultiplexer[ServiceDataSpecifier( 444, ServiceDataSpecifier.Role.RESPONSE)].accepted_datagrams == {} assert None is await subscriber_selective.receive_until(get_monotonic() + 0.1) assert None is await subscriber_promiscuous.receive_until(get_monotonic() + 0.1) assert None is await server_listener.receive_until(get_monotonic() + 0.1) assert None is await client_listener.receive_until(get_monotonic() + 0.1) # # Service exchange test. # assert await client_requester.send_until( Transfer(timestamp=Timestamp.now(), priority=Priority.HIGH, transfer_id=88888, fragmented_payload=payload_x3), monotonic_deadline=get_monotonic() + 5.0) rx_transfer = await server_listener.receive_until(get_monotonic() + 5.0) print('SERVER LISTENER TRANSFER:', rx_transfer) assert isinstance(rx_transfer, TransferFrom) assert rx_transfer.priority == Priority.HIGH assert rx_transfer.transfer_id == 88888 assert len(rx_transfer.fragmented_payload) == 3 assert b''.join(rx_transfer.fragmented_payload) == b''.join(payload_x3) assert None is await subscriber_selective.receive_until(get_monotonic() + 0.1) assert None is await subscriber_promiscuous.receive_until(get_monotonic() + 0.1) assert None is await server_listener.receive_until(get_monotonic() + 0.1) assert None is await client_listener.receive_until(get_monotonic() + 0.1) print('tr :', tr.sample_statistics()) assert tr.sample_statistics().demultiplexer[MessageDataSpecifier( 12345)].accepted_datagrams == { 222: 1 } assert tr.sample_statistics().demultiplexer[ServiceDataSpecifier( 444, ServiceDataSpecifier.Role.REQUEST )].accepted_datagrams == { 222: 3 * 2 } # Deterministic data loss mitigation is enabled, multiplication factor 2 print('tr2:', tr2.sample_statistics()) assert tr2.sample_statistics().demultiplexer[ServiceDataSpecifier( 444, ServiceDataSpecifier.Role.RESPONSE)].accepted_datagrams == {} # # Termination. # assert set(tr.input_sessions) == { subscriber_promiscuous, subscriber_selective, server_listener } assert set(tr.output_sessions) == {server_responder} assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {broadcaster, client_requester} subscriber_promiscuous.close() subscriber_promiscuous.close() # Idempotency. assert set(tr.input_sessions) == {subscriber_selective, server_listener} assert set(tr.output_sessions) == {server_responder} assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {broadcaster, client_requester} broadcaster.close() broadcaster.close() # Idempotency. assert set(tr.input_sessions) == {subscriber_selective, server_listener} assert set(tr.output_sessions) == {server_responder} assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {client_requester} tr.close() tr.close() # Idempotency. tr2.close() tr2.close() # Idempotency. assert not set(tr.input_sessions) assert not set(tr.output_sessions) assert not set(tr2.input_sessions) assert not set(tr2.output_sessions) with pytest.raises(pyuavcan.transport.ResourceClosedError): _ = tr.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(12345), None), meta) with pytest.raises(pyuavcan.transport.ResourceClosedError): _ = tr2.get_input_session( InputSessionSpecifier(MessageDataSpecifier(12345), None), meta)
async def _unittest_redundant_transport(caplog: typing.Any) -> None: from pyuavcan.transport import MessageDataSpecifier, PayloadMetadata, Transfer from pyuavcan.transport import Priority, Timestamp, InputSessionSpecifier, OutputSessionSpecifier from pyuavcan.transport import ProtocolParameters loop = asyncio.get_event_loop() loop.slow_callback_duration = 1.0 tr_a = RedundantTransport() tr_b = RedundantTransport(loop) assert tr_a.sample_statistics() == RedundantTransportStatistics([]) assert tr_a.inferiors == [] assert tr_a.local_node_id is None assert tr_a.loop is asyncio.get_event_loop() assert tr_a.local_node_id is None assert tr_a.protocol_parameters == ProtocolParameters( transfer_id_modulo=0, max_nodes=0, mtu=0, ) assert tr_a.descriptor == '<redundant></redundant>' # Empty, no inferiors. assert tr_a.input_sessions == [] assert tr_a.output_sessions == [] assert tr_a.loop == tr_b.loop # # Instantiate session objects. # meta = PayloadMetadata(10_240) pub_a = tr_a.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) sub_any_a = tr_a.get_input_session( InputSessionSpecifier(MessageDataSpecifier(2345), None), meta) assert pub_a is tr_a.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) assert set(tr_a.input_sessions) == {sub_any_a} assert set(tr_a.output_sessions) == {pub_a} assert tr_a.sample_statistics() == RedundantTransportStatistics() pub_b = tr_b.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) sub_any_b = tr_b.get_input_session( InputSessionSpecifier(MessageDataSpecifier(2345), None), meta) sub_sel_b = tr_b.get_input_session( InputSessionSpecifier(MessageDataSpecifier(2345), 3210), meta) assert sub_sel_b is tr_b.get_input_session( InputSessionSpecifier(MessageDataSpecifier(2345), 3210), meta) assert set(tr_b.input_sessions) == {sub_any_b, sub_sel_b} assert set(tr_b.output_sessions) == {pub_b} assert tr_b.sample_statistics() == RedundantTransportStatistics() # # Exchange test with no inferiors, expected to fail. # assert len(pub_a.inferiors) == 0 assert len(sub_any_a.inferiors) == 0 assert not await pub_a.send_until(Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=1, fragmented_payload=[memoryview(b'abc')]), monotonic_deadline=loop.time() + 1.0) assert not await sub_any_a.receive_until(loop.time() + 0.1) assert not await sub_any_b.receive_until(loop.time() + 0.1) assert tr_a.sample_statistics() == RedundantTransportStatistics() assert tr_b.sample_statistics() == RedundantTransportStatistics() # # Adding inferiors - loopback, transport A only. # with pytest.raises(InconsistentInferiorConfigurationError, match='(?i).*loop.*'): tr_a.attach_inferior( LoopbackTransport( 111, loop=asyncio.new_event_loop())) # Wrong event loop. assert len(pub_a.inferiors) == 0 assert len(sub_any_a.inferiors) == 0 lo_mono_0 = LoopbackTransport(111) lo_mono_1 = LoopbackTransport(111) tr_a.attach_inferior(lo_mono_0) assert len(pub_a.inferiors) == 1 assert len(sub_any_a.inferiors) == 1 with pytest.raises(ValueError): tr_a.detach_inferior(lo_mono_1) # Not a registered inferior (yet). tr_a.attach_inferior(lo_mono_1) assert len(pub_a.inferiors) == 2 assert len(sub_any_a.inferiors) == 2 with pytest.raises(ValueError): tr_a.attach_inferior(lo_mono_0) # Double-add not allowed. with pytest.raises(InconsistentInferiorConfigurationError, match='(?i).*node-id.*'): tr_a.attach_inferior(LoopbackTransport(None)) # Wrong node-ID. with pytest.raises(InconsistentInferiorConfigurationError, match='(?i).*node-id.*'): tr_a.attach_inferior(LoopbackTransport(1230)) # Wrong node-ID. assert tr_a.inferiors == [lo_mono_0, lo_mono_1] assert len(pub_a.inferiors) == 2 assert len(sub_any_a.inferiors) == 2 assert tr_a.sample_statistics() == RedundantTransportStatistics(inferiors=[ lo_mono_0.sample_statistics(), lo_mono_1.sample_statistics(), ]) assert tr_a.local_node_id == 111 assert tr_a.descriptor == '<redundant><loopback/><loopback/></redundant>' assert await pub_a.send_until(Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=2, fragmented_payload=[memoryview(b'def')]), monotonic_deadline=loop.time() + 1.0) rx = await sub_any_a.receive_until(loop.time() + 1.0) assert rx is not None assert rx.fragmented_payload == [memoryview(b'def')] assert rx.transfer_id == 2 assert not await sub_any_b.receive_until(loop.time() + 0.1) # # Incapacitate one inferior, ensure things are still OK. # with caplog.at_level(logging.CRITICAL, logger=pyuavcan.transport.redundant.__name__): for s in lo_mono_0.output_sessions: s.exception = RuntimeError('INTENDED EXCEPTION') assert await pub_a.send_until(Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=3, fragmented_payload=[memoryview(b'qwe')]), monotonic_deadline=loop.time() + 1.0) rx = await sub_any_a.receive_until(loop.time() + 1.0) assert rx is not None assert rx.fragmented_payload == [memoryview(b'qwe')] assert rx.transfer_id == 3 # # Remove old loopback transports. Configure new ones with cyclic TID. # lo_cyc_0 = LoopbackTransport(111) lo_cyc_1 = LoopbackTransport(111) cyc_proto_params = ProtocolParameters( transfer_id_modulo=32, # Like CAN max_nodes=128, # Like CAN mtu=63, # Like CAN ) lo_cyc_0.protocol_parameters = cyc_proto_params lo_cyc_1.protocol_parameters = cyc_proto_params assert lo_cyc_0.protocol_parameters == lo_cyc_1.protocol_parameters == cyc_proto_params assert tr_a.protocol_parameters.transfer_id_modulo >= 2**56 with pytest.raises(InconsistentInferiorConfigurationError, match='(?i).*transfer-id.*'): tr_a.attach_inferior(lo_cyc_0) # Transfer-ID modulo mismatch tr_a.detach_inferior(lo_mono_0) tr_a.detach_inferior(lo_mono_1) del lo_mono_0 # Prevent accidental reuse. del lo_mono_1 assert tr_a.inferiors == [] # All removed, okay. assert pub_a.inferiors == [] assert sub_any_a.inferiors == [] assert tr_a.local_node_id is None # Back to the roots assert tr_a.descriptor == '<redundant></redundant>' # Yes yes # Now we can add our cyclic transports safely. tr_a.attach_inferior(lo_cyc_0) assert tr_a.protocol_parameters.transfer_id_modulo == 32 tr_a.attach_inferior(lo_cyc_1) assert tr_a.protocol_parameters == cyc_proto_params, 'Protocol parameter mismatch' assert tr_a.local_node_id == 111 assert tr_a.descriptor == '<redundant><loopback/><loopback/></redundant>' # Exchange test. assert await pub_a.send_until(Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=4, fragmented_payload=[memoryview(b'rty')]), monotonic_deadline=loop.time() + 1.0) rx = await sub_any_a.receive_until(loop.time() + 1.0) assert rx is not None assert rx.fragmented_payload == [memoryview(b'rty')] assert rx.transfer_id == 4 # # Real heterogeneous transport test. # tr_a.detach_inferior(lo_cyc_0) tr_a.detach_inferior(lo_cyc_1) del lo_cyc_0 # Prevent accidental reuse. del lo_cyc_1 udp_a = UDPTransport('127.0.0.111/8') udp_b = UDPTransport('127.0.0.222/8') serial_a = SerialTransport(SERIAL_URI, 111) serial_b = SerialTransport(SERIAL_URI, 222, mtu=2048) # Heterogeneous. tr_a.attach_inferior(udp_a) tr_a.attach_inferior(serial_a) tr_b.attach_inferior(udp_b) tr_b.attach_inferior(serial_b) print('tr_a.descriptor', tr_a.descriptor) print('tr_b.descriptor', tr_b.descriptor) assert tr_a.protocol_parameters == ProtocolParameters( transfer_id_modulo=2**64, max_nodes=4096, mtu=1024, ) assert tr_a.local_node_id == 111 assert tr_a.descriptor == f'<redundant>{udp_a.descriptor}{serial_a.descriptor}</redundant>' assert tr_b.protocol_parameters == ProtocolParameters( transfer_id_modulo=2**64, max_nodes=4096, mtu=1024, ) assert tr_b.local_node_id == 222 assert tr_b.descriptor == f'<redundant>{udp_b.descriptor}{serial_b.descriptor}</redundant>' assert await pub_a.send_until(Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=5, fragmented_payload=[memoryview(b'uio')]), monotonic_deadline=loop.time() + 1.0) rx = await sub_any_b.receive_until(loop.time() + 1.0) assert rx is not None assert rx.fragmented_payload == [memoryview(b'uio')] assert rx.transfer_id == 5 assert not await sub_any_a.receive_until(loop.time() + 0.1) assert not await sub_any_b.receive_until(loop.time() + 0.1) assert not await sub_sel_b.receive_until(loop.time() + 0.1) # # Construct new session with the transports configured. # pub_a_new = tr_a.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), 222), meta) assert pub_a_new is tr_a.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), 222), meta) assert set(tr_a.output_sessions) == {pub_a, pub_a_new} assert await pub_a_new.send_until(Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=6, fragmented_payload=[memoryview(b'asd')]), monotonic_deadline=loop.time() + 1.0) rx = await sub_any_b.receive_until(loop.time() + 1.0) assert rx is not None assert rx.fragmented_payload == [memoryview(b'asd')] assert rx.transfer_id == 6 # # Termination. # tr_a.close() tr_a.close() # Idempotency tr_b.close() tr_b.close() # Idempotency with pytest.raises(pyuavcan.transport.ResourceClosedError ): # Make sure the inferiors are closed. udp_a.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) with pytest.raises(pyuavcan.transport.ResourceClosedError ): # Make sure the inferiors are closed. serial_b.get_output_session( OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) with pytest.raises(pyuavcan.transport.ResourceClosedError ): # Make sure the sessions are closed. await pub_a.send_until(Transfer(timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=100, fragmented_payload=[]), monotonic_deadline=loop.time() + 1.0) await asyncio.sleep( 1 ) # Let all pending tasks finalize properly to avoid stack traces in the output.
async def _unittest_udp_transport_ipv4() -> None: from pyuavcan.transport import MessageDataSpecifier, ServiceDataSpecifier, PayloadMetadata, Transfer, TransferFrom from pyuavcan.transport import Priority, Timestamp, InputSessionSpecifier, OutputSessionSpecifier from pyuavcan.transport import ProtocolParameters asyncio.get_running_loop().slow_callback_duration = 5.0 get_monotonic = asyncio.get_event_loop().time with pytest.raises(ValueError): _ = UDPTransport("127.0.0.111", mtu=10) with pytest.raises(ValueError): _ = UDPTransport("127.0.0.111", service_transfer_multiplier=100) tr = UDPTransport("127.0.0.111", mtu=9000) tr2 = UDPTransport("127.0.0.222", service_transfer_multiplier=2) assert tr.local_ip_address == ipaddress.ip_address("127.0.0.111") assert tr2.local_ip_address == ipaddress.ip_address("127.0.0.222") assert tr.loop is asyncio.get_event_loop() assert tr.local_node_id == 111 assert tr2.local_node_id == 222 assert tr.input_sessions == [] assert tr.output_sessions == [] assert "127.0.0.111" in repr(tr) assert tr.protocol_parameters == ProtocolParameters( transfer_id_modulo=2 ** 64, max_nodes=65535, mtu=9000, ) default_mtu = min(UDPTransport.VALID_MTU_RANGE) assert "127.0.0.222" in repr(tr2) assert tr2.protocol_parameters == ProtocolParameters( transfer_id_modulo=2 ** 64, max_nodes=65535, mtu=default_mtu, ) assert tr.sample_statistics() == tr2.sample_statistics() == UDPTransportStatistics() payload_single = [_mem("qwertyui"), _mem("01234567")] * (default_mtu // 16) assert sum(map(len, payload_single)) == default_mtu payload_x3 = (payload_single * 3)[:-1] payload_x3_size_bytes = default_mtu * 3 - 8 assert sum(map(len, payload_x3)) == payload_x3_size_bytes # # Instantiate session objects. # meta = PayloadMetadata(10000) broadcaster = tr2.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) assert broadcaster is tr2.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) subscriber_promiscuous = tr.get_input_session(InputSessionSpecifier(MessageDataSpecifier(2345), None), meta) assert subscriber_promiscuous is tr.get_input_session(InputSessionSpecifier(MessageDataSpecifier(2345), None), meta) subscriber_selective = tr.get_input_session(InputSessionSpecifier(MessageDataSpecifier(2345), 123), meta) assert subscriber_selective is tr.get_input_session(InputSessionSpecifier(MessageDataSpecifier(2345), 123), meta) server_listener = tr.get_input_session( InputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), None), meta ) assert server_listener is tr.get_input_session( InputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), None), meta ) server_responder = tr.get_output_session( OutputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 222), meta ) assert server_responder is tr.get_output_session( OutputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 222), meta ) client_requester = tr2.get_output_session( OutputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), 111), meta ) assert client_requester is tr2.get_output_session( OutputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST), 111), meta ) client_listener = tr2.get_input_session( InputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 111), meta ) assert client_listener is tr2.get_input_session( InputSessionSpecifier(ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE), 111), meta ) print("tr :", tr.input_sessions, tr.output_sessions) assert set(tr.input_sessions) == {subscriber_promiscuous, subscriber_selective, server_listener} assert set(tr.output_sessions) == {server_responder} print("tr2:", tr2.input_sessions, tr2.output_sessions) assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {broadcaster, client_requester} assert tr.sample_statistics().received_datagrams[MessageDataSpecifier(2345)].accepted_datagrams == {} assert ( tr.sample_statistics() .received_datagrams[ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST)] .accepted_datagrams == {} ) assert ( tr2.sample_statistics() .received_datagrams[ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE)] .accepted_datagrams == {} ) # # Message exchange test. # assert await broadcaster.send( Transfer( timestamp=Timestamp.now(), priority=Priority.LOW, transfer_id=77777, fragmented_payload=payload_single ), monotonic_deadline=get_monotonic() + 5.0, ) rx_transfer = await subscriber_promiscuous.receive(get_monotonic() + 5.0) print("PROMISCUOUS SUBSCRIBER TRANSFER:", rx_transfer) assert isinstance(rx_transfer, TransferFrom) assert rx_transfer.priority == Priority.LOW assert rx_transfer.transfer_id == 77777 assert rx_transfer.fragmented_payload == [b"".join(payload_single)] print("tr :", tr.sample_statistics()) assert tr.sample_statistics().received_datagrams[MessageDataSpecifier(2345)].accepted_datagrams == {222: 1} assert ( tr.sample_statistics() .received_datagrams[ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST)] .accepted_datagrams == {} ) print("tr2:", tr2.sample_statistics()) assert ( tr2.sample_statistics() .received_datagrams[ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE)] .accepted_datagrams == {} ) assert None is await subscriber_selective.receive(get_monotonic() + 0.1) assert None is await subscriber_promiscuous.receive(get_monotonic() + 0.1) assert None is await server_listener.receive(get_monotonic() + 0.1) assert None is await client_listener.receive(get_monotonic() + 0.1) # # Service exchange test. # assert await client_requester.send( Transfer(timestamp=Timestamp.now(), priority=Priority.HIGH, transfer_id=88888, fragmented_payload=payload_x3), monotonic_deadline=get_monotonic() + 5.0, ) rx_transfer = await server_listener.receive(get_monotonic() + 5.0) print("SERVER LISTENER TRANSFER:", rx_transfer) assert isinstance(rx_transfer, TransferFrom) assert rx_transfer.priority == Priority.HIGH assert rx_transfer.transfer_id == 88888 assert len(rx_transfer.fragmented_payload) == 3 assert b"".join(rx_transfer.fragmented_payload) == b"".join(payload_x3) assert None is await subscriber_selective.receive(get_monotonic() + 0.1) assert None is await subscriber_promiscuous.receive(get_monotonic() + 0.1) assert None is await server_listener.receive(get_monotonic() + 0.1) assert None is await client_listener.receive(get_monotonic() + 0.1) print("tr :", tr.sample_statistics()) assert tr.sample_statistics().received_datagrams[MessageDataSpecifier(2345)].accepted_datagrams == {222: 1} assert tr.sample_statistics().received_datagrams[ ServiceDataSpecifier(444, ServiceDataSpecifier.Role.REQUEST) ].accepted_datagrams == { 222: 3 * 2 } # Deterministic data loss mitigation is enabled, multiplication factor 2 print("tr2:", tr2.sample_statistics()) assert ( tr2.sample_statistics() .received_datagrams[ServiceDataSpecifier(444, ServiceDataSpecifier.Role.RESPONSE)] .accepted_datagrams == {} ) # # Termination. # assert set(tr.input_sessions) == {subscriber_promiscuous, subscriber_selective, server_listener} assert set(tr.output_sessions) == {server_responder} assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {broadcaster, client_requester} subscriber_promiscuous.close() subscriber_promiscuous.close() # Idempotency. assert set(tr.input_sessions) == {subscriber_selective, server_listener} assert set(tr.output_sessions) == {server_responder} assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {broadcaster, client_requester} broadcaster.close() broadcaster.close() # Idempotency. assert set(tr.input_sessions) == {subscriber_selective, server_listener} assert set(tr.output_sessions) == {server_responder} assert set(tr2.input_sessions) == {client_listener} assert set(tr2.output_sessions) == {client_requester} tr.close() tr.close() # Idempotency. tr2.close() tr2.close() # Idempotency. assert not set(tr.input_sessions) assert not set(tr.output_sessions) assert not set(tr2.input_sessions) assert not set(tr2.output_sessions) with pytest.raises(pyuavcan.transport.ResourceClosedError): _ = tr.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(2345), None), meta) with pytest.raises(pyuavcan.transport.ResourceClosedError): _ = tr2.get_input_session(InputSessionSpecifier(MessageDataSpecifier(2345), None), meta) await asyncio.sleep(1) # Let all pending tasks finalize properly to avoid stack traces in the output.
async def _unittest_slow_node( compiled: typing.List[pyuavcan.dsdl.GeneratedPackageInfo]) -> None: from pyuavcan.application import make_node, make_registry import uavcan.primitive from uavcan.node import Version_1_0, Heartbeat_1_0, GetInfo_1_0, Mode_1_0, Health_1_0 asyncio.get_running_loop().slow_callback_duration = 3.0 assert compiled remote_pres = Presentation(UDPTransport("127.1.1.1")) remote_hb_sub = remote_pres.make_subscriber_with_fixed_subject_id( Heartbeat_1_0) remote_info_cln = remote_pres.make_client_with_fixed_service_id( GetInfo_1_0, 258) trans = RedundantTransport() try: info = GetInfo_1_0.Response( protocol_version=Version_1_0( *pyuavcan.UAVCAN_SPECIFICATION_VERSION), software_version=Version_1_0(*pyuavcan.__version_info__[:2]), name="org.uavcan.pyuavcan.test.node", ) node = make_node(info, make_registry(None, typing.cast(Dict[str, bytes], {})), transport=trans) print("node:", node) assert node.presentation.transport is trans node.start() node.start() # Idempotency # Check port instantiation API for non-fixed-port-ID types. assert "uavcan.pub.optional.id" not in node.registry # Nothing yet. with pytest.raises(KeyError, match=r".*uavcan\.pub\.optional\.id.*"): node.make_publisher(uavcan.primitive.Empty_1_0, "optional") assert 0xFFFF == int( node.registry["uavcan.pub.optional.id"]) # Created automatically! with pytest.raises(TypeError): node.make_publisher(uavcan.primitive.Empty_1_0) # Same but for fixed port-ID types. assert "uavcan.pub.atypical_heartbeat.id" not in node.registry # Nothing yet. port = node.make_publisher(uavcan.node.Heartbeat_1_0, "atypical_heartbeat") assert port.port_id == pyuavcan.dsdl.get_model( uavcan.node.Heartbeat_1_0).fixed_port_id port.close() assert 0xFFFF == int(node.registry["uavcan.pub.atypical_heartbeat.id"] ) # Created automatically! node.registry[ "uavcan.pub.atypical_heartbeat.id"] = 111 # Override the default. port = node.make_publisher(uavcan.node.Heartbeat_1_0, "atypical_heartbeat") assert port.port_id == 111 port.close() node.heartbeat_publisher.priority = pyuavcan.transport.Priority.FAST node.heartbeat_publisher.period = 0.5 node.heartbeat_publisher.mode = Mode_1_0.MAINTENANCE # type: ignore node.heartbeat_publisher.health = Health_1_0.ADVISORY # type: ignore node.heartbeat_publisher.vendor_specific_status_code = 93 with pytest.raises(ValueError): node.heartbeat_publisher.period = 99.0 with pytest.raises(ValueError): node.heartbeat_publisher.vendor_specific_status_code = -299 assert node.heartbeat_publisher.priority == pyuavcan.transport.Priority.FAST assert node.heartbeat_publisher.period == pytest.approx(0.5) assert node.heartbeat_publisher.mode == Mode_1_0.MAINTENANCE assert node.heartbeat_publisher.health == Health_1_0.ADVISORY assert node.heartbeat_publisher.vendor_specific_status_code == 93 assert None is await remote_hb_sub.receive_for(2.0) assert trans.local_node_id is None trans.attach_inferior(UDPTransport("127.1.1.2")) assert trans.local_node_id == 258 for _ in range(2): hb_transfer = await remote_hb_sub.receive_for(2.0) assert hb_transfer is not None hb, transfer = hb_transfer assert transfer.source_node_id == 258 assert transfer.priority == pyuavcan.transport.Priority.FAST assert 1 <= hb.uptime <= 9 assert hb.mode.value == Mode_1_0.MAINTENANCE assert hb.health.value == Health_1_0.ADVISORY assert hb.vendor_specific_status_code == 93 info_transfer = await remote_info_cln.call(GetInfo_1_0.Request()) assert info_transfer is not None resp, transfer = info_transfer assert transfer.source_node_id == 258 assert isinstance(resp, GetInfo_1_0.Response) assert resp.name.tobytes().decode() == "org.uavcan.pyuavcan.test.node" assert resp.protocol_version.major == pyuavcan.UAVCAN_SPECIFICATION_VERSION[ 0] assert resp.software_version.major == pyuavcan.__version_info__[0] trans.detach_inferior(trans.inferiors[0]) assert trans.local_node_id is None assert None is await remote_hb_sub.receive_for(2.0) node.close() node.close() # Idempotency finally: trans.close() remote_pres.close() await asyncio.sleep(1.0) # Let the background tasks terminate.
def one(nid: typing.Optional[int]) -> RedundantTransport: red = RedundantTransport() red.attach_inferior(UDPTransport("127.0.0.1", local_node_id=nid)) red.attach_inferior(SerialTransport(VIRTUAL_BUS_URI, nid)) print("UDP+SERIAL:", red) return red
def one(nid: typing.Optional[int]) -> UDPTransport: return UDPTransport("127.0.0.1", local_node_id=nid)
async def _unittest_slow_node( generated_packages: typing.List[pyuavcan.dsdl.GeneratedPackageInfo] ) -> None: from pyuavcan.application import Node from uavcan.node import Version_1_0, Heartbeat_1_0, GetInfo_1_0, Mode_1_0, Health_1_0 asyncio.get_running_loop().slow_callback_duration = 3.0 assert generated_packages remote_pres = Presentation(UDPTransport("127.1.1.1")) remote_hb_sub = remote_pres.make_subscriber_with_fixed_subject_id( Heartbeat_1_0) remote_info_cln = remote_pres.make_client_with_fixed_service_id( GetInfo_1_0, 258) trans = RedundantTransport() pres = Presentation(trans) try: info = GetInfo_1_0.Response( protocol_version=Version_1_0( *pyuavcan.UAVCAN_SPECIFICATION_VERSION), software_version=Version_1_0(*pyuavcan.__version_info__[:2]), name="org.uavcan.pyuavcan.test.node", ) node = Node(pres, info, with_diagnostic_subscriber=True) print("node:", node) assert node.presentation is pres node.start() node.start() # Idempotency node.heartbeat_publisher.priority = pyuavcan.transport.Priority.FAST node.heartbeat_publisher.period = 0.5 node.heartbeat_publisher.mode = Mode_1_0.MAINTENANCE # type: ignore node.heartbeat_publisher.health = Health_1_0.ADVISORY # type: ignore node.heartbeat_publisher.vendor_specific_status_code = 93 with pytest.raises(ValueError): node.heartbeat_publisher.period = 99.0 with pytest.raises(ValueError): node.heartbeat_publisher.vendor_specific_status_code = -299 assert node.heartbeat_publisher.priority == pyuavcan.transport.Priority.FAST assert node.heartbeat_publisher.period == pytest.approx(0.5) assert node.heartbeat_publisher.mode == Mode_1_0.MAINTENANCE assert node.heartbeat_publisher.health == Health_1_0.ADVISORY assert node.heartbeat_publisher.vendor_specific_status_code == 93 assert None is await remote_hb_sub.receive_for(2.0) assert trans.local_node_id is None trans.attach_inferior(UDPTransport("127.1.1.2")) assert trans.local_node_id == 258 for _ in range(2): hb_transfer = await remote_hb_sub.receive_for(2.0) assert hb_transfer is not None hb, transfer = hb_transfer assert transfer.source_node_id == 258 assert transfer.priority == pyuavcan.transport.Priority.FAST assert 1 <= hb.uptime <= 9 assert hb.mode.value == Mode_1_0.MAINTENANCE assert hb.health.value == Health_1_0.ADVISORY assert hb.vendor_specific_status_code == 93 info_transfer = await remote_info_cln.call(GetInfo_1_0.Request()) assert info_transfer is not None resp, transfer = info_transfer assert transfer.source_node_id == 258 assert isinstance(resp, GetInfo_1_0.Response) assert resp.name.tobytes().decode() == "org.uavcan.pyuavcan.test.node" assert resp.protocol_version.major == pyuavcan.UAVCAN_SPECIFICATION_VERSION[ 0] assert resp.software_version.major == pyuavcan.__version_info__[0] trans.detach_inferior(trans.inferiors[0]) assert trans.local_node_id is None assert None is await remote_hb_sub.receive_for(2.0) node.close() node.close() # Idempotency finally: pres.close() remote_pres.close() await asyncio.sleep(1.0) # Let the background tasks terminate.
def _get_run_configs() -> typing.Iterable[RunConfig]: """ Provides interface options to test the demo against. When adding new transports, add them to the demo and update this factory accordingly. Don't forget about redundant configurations, too. """ from pyuavcan.transport.redundant import RedundantTransport from pyuavcan.transport.serial import SerialTransport from pyuavcan.transport.udp import UDPTransport # UDP yield RunConfig( demo_env_vars={"DEMO_INTERFACE_KIND": "udp"}, local_transport_factory=lambda nid: UDPTransport(f"127.0.0.{1 if nid is None else nid}", anonymous=nid is None), ) # Serial yield RunConfig( demo_env_vars={"DEMO_INTERFACE_KIND": "serial"}, local_transport_factory=lambda nid: SerialTransport("socket://localhost:50905", local_node_id=nid), ) # DMR UDP+Serial def make_udp_serial(nid: typing.Optional[int]) -> pyuavcan.transport.Transport: tr = RedundantTransport() if nid is not None: tr.attach_inferior(UDPTransport(f"127.0.0.{nid}")) else: tr.attach_inferior(UDPTransport(f"127.0.0.1", anonymous=True)) tr.attach_inferior(SerialTransport("socket://localhost:50905", local_node_id=nid)) return tr yield RunConfig( demo_env_vars={"DEMO_INTERFACE_KIND": "udp_serial"}, local_transport_factory=make_udp_serial, ) if sys.platform.startswith("linux"): from pyuavcan.transport.can.media.socketcan import SocketCANMedia from pyuavcan.transport.can import CANTransport # CAN yield RunConfig( demo_env_vars={"DEMO_INTERFACE_KIND": "can"}, # The demo uses Classic CAN! SocketCAN does not support nonuniform MTU well. local_transport_factory=lambda nid: CANTransport(SocketCANMedia("vcan0", 8), local_node_id=nid), ) # TMR CAN def make_tmr_can(nid: typing.Optional[int]) -> pyuavcan.transport.Transport: from pyuavcan.transport.redundant import RedundantTransport tr = RedundantTransport() tr.attach_inferior(CANTransport(SocketCANMedia("vcan0", 8), local_node_id=nid)) tr.attach_inferior(CANTransport(SocketCANMedia("vcan1", 32), local_node_id=nid)) tr.attach_inferior(CANTransport(SocketCANMedia("vcan2", 64), local_node_id=nid)) return tr yield RunConfig( demo_env_vars={"DEMO_INTERFACE_KIND": "can_can_can"}, local_transport_factory=make_tmr_can, )
async def _unittest_udp_transport_ipv4_capture() -> None: import socket from pyuavcan.transport.udp import UDPCapture, IPPacket from pyuavcan.transport import MessageDataSpecifier, PayloadMetadata, Transfer from pyuavcan.transport import Priority, Timestamp, OutputSessionSpecifier from pyuavcan.transport import Capture, AlienSessionSpecifier asyncio.get_running_loop().slow_callback_duration = 5.0 tr_capture = UDPTransport("127.50.0.2", local_node_id=None) captures: typing.List[UDPCapture] = [] def inhale(s: Capture) -> None: print("CAPTURED:", s) assert isinstance(s, UDPCapture) captures.append(s) tr_capture.begin_capture(inhale) await asyncio.sleep(1.0) tr = UDPTransport("127.50.0.111") meta = PayloadMetadata(10000) broadcaster = tr.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(190), None), meta) assert broadcaster is tr.get_output_session(OutputSessionSpecifier(MessageDataSpecifier(190), None), meta) # For reasons of Windows compatibility, we have to set up a dummy listener on the target multicast group. # Otherwise, we will not see any packets at all. This is Windows-specific. sink = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sink.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sink.bind(("", 11111)) sink.setsockopt( socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton("239.50.0.190") + socket.inet_aton("127.0.0.1") ) ts = Timestamp.now() assert len(captures) == 0 # Assuming here that there are no other entities that might create noise. await broadcaster.send( Transfer( timestamp=ts, priority=Priority.NOMINAL, transfer_id=9876543210, fragmented_payload=[_mem(bytes(range(256)))] * 4, ), monotonic_deadline=tr.loop.time() + 2.0, ) await asyncio.sleep(1.0) # Let the packet propagate. assert len(captures) == 1 # Ensure the packet is captured. tr_capture.close() # Ensure the capture is stopped after the capturing transport is closed. await broadcaster.send( # This one shall be ignored. Transfer(timestamp=Timestamp.now(), priority=Priority.HIGH, transfer_id=54321, fragmented_payload=[_mem(b"")]), monotonic_deadline=tr.loop.time() + 2.0, ) await asyncio.sleep(1.0) assert len(captures) == 1 # Ignored? tr.close() sink.close() (pkt,) = captures assert isinstance(pkt, UDPCapture) assert (ts.monotonic - 1) <= pkt.timestamp.monotonic <= Timestamp.now().monotonic assert (ts.system - 1) <= pkt.timestamp.system <= Timestamp.now().system ip_pkt = IPPacket.parse(pkt.link_layer_packet) assert ip_pkt is not None assert [str(x) for x in ip_pkt.source_destination] == ["127.50.0.111", "239.50.0.190"] parsed = pkt.parse() assert parsed ses, frame = parsed assert isinstance(ses, AlienSessionSpecifier) assert ses.source_node_id == 111 assert ses.destination_node_id is None assert ses.data_specifier == broadcaster.specifier.data_specifier assert frame.end_of_transfer assert frame.index == 0 assert frame.transfer_id == 9876543210 assert len(frame.payload) == 1024 assert frame.priority == Priority.NOMINAL
async def _unittest_file( compiled: typing.List[pyuavcan.dsdl.GeneratedPackageInfo]) -> None: from pyuavcan.application import make_node, NodeInfo from pyuavcan.transport.udp import UDPTransport from pyuavcan.application.file import FileClient, FileServer, Error assert compiled asyncio.get_running_loop().slow_callback_duration = 3.0 root_a = mkdtemp(".file", "a.") root_b = mkdtemp(".file", "b.") srv_node = make_node( NodeInfo(name="org.uavcan.pyuavcan.test.file.server"), transport=UDPTransport("127.63.0.0", 222, service_transfer_multiplier=2), ) cln_node = make_node( NodeInfo(name="org.uavcan.pyuavcan.test.file.client"), transport=UDPTransport("127.63.0.0", 223, service_transfer_multiplier=2), ) try: srv_node.start() file_server = FileServer(srv_node, [root_a, root_b]) assert (Path(root_a), Path("abc")) == file_server.locate(Path("abc")) assert [] == list(file_server.glob("*")) cln_node.start() cln = FileClient(cln_node, 222) async def ls(path: str) -> typing.List[str]: out: typing.List[str] = [] async for e in cln.list(path): out.append(e) return out assert [] == await ls("") assert [] == await ls("nonexistent/directory") assert (await cln.get_info("none")).error.value == Error.NOT_FOUND assert 0 == await cln.touch("a/foo/x") assert 0 == await cln.touch("a/foo/y") assert 0 == await cln.touch("b") assert ["foo"] == await ls("a") # Make sure files are created. assert [ (file_server.roots[0], Path("a/foo/x")), (file_server.roots[0], Path("a/foo/y")), ] == list(sorted(file_server.glob("a/foo/*"))) assert await cln.read("a/foo/x") == b"" assert await cln.read( "/a/foo/x") == b"" # Slash or no slash makes no difference. assert await cln.read("a/foo/z") == Error.NOT_FOUND assert (await cln.get_info("a/foo/z")).error.value == Error.NOT_FOUND # Write non-existent file assert await cln.write("a/foo/z", bytes(range(200)) * 3) == Error.NOT_FOUND # Write into empty file assert await cln.write("a/foo/x", bytes(range(200)) * 3) == 0 assert await cln.read("a/foo/x") == bytes(range(200)) * 3 assert (await cln.get_info("a/foo/x")).size == 600 # Truncation -- this write is shorter hundred = bytes(x ^ 0xFF for x in range(100)) assert await cln.write("a/foo/x", hundred * 4) == 0 assert (await cln.get_info("a/foo/x")).size == 400 assert await cln.read("a/foo/x") == (hundred * 4) assert (await cln.get_info("a/foo/x")).size == 400 # Fill in the middle without truncation ref = bytearray(hundred * 4) for i in range(100): ref[i + 100] = 0x55 assert len(ref) == 400 assert (await cln.get_info("a/foo/x")).size == 400 assert await cln.write("a/foo/x", b"\x55" * 100, offset=100, truncate=False) == 0 assert (await cln.get_info("a/foo/x")).size == 400 assert await cln.read("a/foo/x") == ref # Fill in the middle with truncation assert await cln.write("a/foo/x", b"\xAA" * 50, offset=50) == 0 assert (await cln.get_info("a/foo/x")).size == 100 assert await cln.read("a/foo/x") == hundred[:50] + b"\xAA" * 50 # Directories info = await cln.get_info("a/foo") print("a/foo:", info) assert info.error.value == 0 assert info.is_writeable assert info.is_readable assert not info.is_file_not_directory assert not info.is_link assert (await cln.get_info("a/foo/nothing")).error.value == Error.NOT_FOUND assert await cln.write("a/foo", b"123") in (Error.IS_DIRECTORY, Error.ACCESS_DENIED ) # Windows compatibility # Removal assert (await cln.remove("a/foo/z")) == Error.NOT_FOUND assert (await cln.remove("a/foo/x")) == 0 assert (await cln.touch("a/foo/x")) == 0 # Put it back assert (await cln.remove("a/foo/")) == 0 # Removed assert (await cln.remove("a/foo/")) == Error.NOT_FOUND # Not found # Copy assert (await cln.touch("r/a")) == 0 assert (await cln.touch("r/b/0")) == 0 assert (await cln.touch("r/b/1")) == 0 assert not (await cln.get_info("r/b")).is_file_not_directory assert ["a", "b"] == await ls("r") assert (await cln.copy("r/b", "r/c")) == 0 assert ["a", "b", "c"] == await ls("r") assert (await cln.copy("r/a", "r/c")) != 0 # Overwrite not enabled assert ["a", "b", "c"] == await ls("r") assert not (await cln.get_info("r/c")).is_file_not_directory assert (await cln.copy("/r/a", "r/c", overwrite=True)) == 0 assert (await cln.get_info("r/c")).is_file_not_directory # Move assert ["a", "b", "c"] == await ls("r") assert (await cln.move("/r/a", "r/c")) != 0 # Overwrite not enabled assert (await cln.move("/r/a", "r/c", overwrite=True)) == 0 assert ["b", "c"] == await ls("r") assert (await cln.move("/r/a", "r/c", overwrite=True)) == Error.NOT_FOUND assert ["b", "c"] == await ls("r") # Access protected files if sys.platform.startswith("linux"): # pragma: no branch file_server.roots.append(Path("/")) info = await cln.get_info("dev/null") print("/dev/null:", info) assert info.error.value == 0 assert not info.is_link assert info.is_writeable assert info.is_file_not_directory info = await cln.get_info("/bin/sh") print("/bin/sh:", info) assert info.error.value == 0 assert not info.is_writeable assert info.is_file_not_directory assert await cln.read("/dev/null", size=100) == b"" # Read less than requested assert await cln.read( "/dev/zero", size=100) == b"\x00" * 256 # Read more than requested assert await cln.write("bin/sh", b"123") == Error.ACCESS_DENIED file_server.roots.pop(-1) finally: srv_node.close() cln_node.close() await asyncio.sleep(1.0) shutil.rmtree(root_a, ignore_errors=True) shutil.rmtree(root_b, ignore_errors=True)
def get_transport( node_id: typing.Optional[int]) -> pyuavcan.transport.Transport: from pyuavcan.transport.udp import UDPTransport return UDPTransport( f'127.0.0.{node_id}/8') if node_id is not None else UDPTransport( '127.255.255.255/8')
def one(nid: typing.Optional[int]) -> UDPTransport: return UDPTransport( f'127.0.0.{nid}/8') if nid is not None else UDPTransport( '127.255.255.255/8')