async def _unittest_slow_diagnostic(generated_packages: typing.List[ pyuavcan.dsdl.GeneratedPackageInfo], caplog: typing.Any) -> None: from pyuavcan.application import diagnostic from uavcan.time import SynchronizedTimestamp_1_0 assert generated_packages pres = Presentation(LoopbackTransport(2222)) pub = pres.make_publisher_with_fixed_subject_id(diagnostic.Record) diag = diagnostic.DiagnosticSubscriber(pres) diag.start() caplog.clear() await pub.publish( diagnostic.Record( timestamp=SynchronizedTimestamp_1_0(123456789), severity=diagnostic.Severity(diagnostic.Severity.INFO), text="Hello world!", )) await asyncio.sleep(1.0) print("Captured log records:") for lr in caplog.records: print(" ", lr) assert isinstance(lr, logging.LogRecord) pat = r"uavcan\.diagnostic\.Record: node=2222 severity=2 ts_sync=123\.456789 ts_local=\S+:\nHello world!" if lr.levelno == logging.INFO and re.match(pat, lr.message): break else: assert False, "Expected log message not captured" diag.close() pub.close() pres.close() await asyncio.sleep(1.0) # Let the background tasks terminate.
async def _unittest_slow_plug_and_play_allocatee( compiled: typing.List[pyuavcan.dsdl.GeneratedPackageInfo], caplog: typing.Any ) -> None: from pyuavcan.presentation import Presentation from pyuavcan.application.plug_and_play import Allocatee, NodeIDAllocationData_2, ID assert compiled asyncio.get_running_loop().slow_callback_duration = 5.0 peers: typing.Set[MockMedia] = set() pres_client = Presentation(CANTransport(MockMedia(peers, 64, 1), None)) pres_server = Presentation(CANTransport(MockMedia(peers, 64, 1), 123)) allocatee = Allocatee(pres_client, _uid("00112233445566778899aabbccddeeff"), 42) pub = pres_server.make_publisher_with_fixed_subject_id(NodeIDAllocationData_2) await pub.publish(NodeIDAllocationData_2(ID(10), unique_id=_uid("aabbccddeeff00112233445566778899"))) # Mismatch. await asyncio.sleep(1.0) assert allocatee.get_result() is None with caplog.at_level(logging.CRITICAL, logger=pyuavcan.application.plug_and_play.__name__): # Bad NID. await pub.publish(NodeIDAllocationData_2(ID(999), unique_id=_uid("00112233445566778899aabbccddeeff"))) await asyncio.sleep(1.0) assert allocatee.get_result() is None await pub.publish(NodeIDAllocationData_2(ID(0), unique_id=_uid("00112233445566778899aabbccddeeff"))) # Correct. await asyncio.sleep(1.0) assert allocatee.get_result() == 0 allocatee.close() pub.close() pres_client.close() pres_server.close() await asyncio.sleep(1.0) # Let the tasks finalize properly.
def _make_subscriber(subjects: typing.Sequence[str], presentation: Presentation) -> Subscriber[_M]: group = [construct_port_id_and_type(ds) for ds in subjects] assert len(group) > 0 if len(group) == 1: ((subject_id, dtype), ) = group return presentation.make_subscriber(dtype, subject_id) raise NotImplementedError( "Multi-subject subscription is not yet implemented. See https://github.com/UAVCAN/pyuavcan/issues/65" )
async def _unittest_slow_node_tracker(generated_packages: typing.List[pyuavcan.dsdl.GeneratedPackageInfo], caplog: typing.Any) -> None: from . import get_transport from pyuavcan.presentation import Presentation from pyuavcan.application.node_tracker import NodeTracker, Entry, GetInfo, Heartbeat assert generated_packages p_a = Presentation(get_transport(0xA)) p_b = Presentation(get_transport(0xB)) p_c = Presentation(get_transport(0xC)) p_trk = Presentation(get_transport(None)) try: last_update_args: typing.List[typing.Tuple[int, typing.Optional[Entry], typing.Optional[Entry]]] = [] def simple_handler(node_id: int, old: typing.Optional[Entry], new: typing.Optional[Entry]) -> None: last_update_args.append((node_id, old, new)) def faulty_handler(_node_id: int, _old: typing.Optional[Entry], _new: typing.Optional[Entry]) -> None: raise Exception('INTENDED EXCEPTION') trk = NodeTracker(p_trk) assert not trk.registry assert pytest.approx(trk.get_info_timeout) == trk.DEFAULT_GET_INFO_TIMEOUT assert trk.get_info_attempts == trk.DEFAULT_GET_INFO_ATTEMPTS # Override the defaults to simplify and speed-up testing. trk.get_info_timeout = 1.0 trk.get_info_attempts = 2 assert pytest.approx(trk.get_info_timeout) == 1.0 assert trk.get_info_attempts == 2 with caplog.at_level(logging.CRITICAL, logger=pyuavcan.application.node_tracker.__name__): trk.add_update_handler(faulty_handler) trk.add_update_handler(simple_handler) trk.start() trk.start() # Idempotency await asyncio.sleep(1) assert not last_update_args assert not trk.registry # Bring the first node online and make sure it is detected and reported. hb_a = asyncio.create_task(_publish_heartbeat(p_a, 0xde)) await asyncio.sleep(2.5) assert len(last_update_args) == 1 assert last_update_args[0][0] == 0xA assert last_update_args[0][1] is None assert last_update_args[0][2] is not None assert last_update_args[0][2].heartbeat.uptime == 0 assert last_update_args[0][2].heartbeat.vendor_specific_status_code == 0xde last_update_args.clear() assert list(trk.registry.keys()) == [0xA] assert 3 >= trk.registry[0xA].heartbeat.uptime >= 2 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xde assert trk.registry[0xA].info is None # Remove the faulty handler -- no point keeping the noise in the log. trk.remove_update_handler(faulty_handler) # Bring the second node online and make sure it is detected and reported. hb_b = asyncio.create_task(_publish_heartbeat(p_b, 0xbe)) await asyncio.sleep(2.5) assert len(last_update_args) == 1 assert last_update_args[0][0] == 0xB assert last_update_args[0][1] is None assert last_update_args[0][2] is not None assert last_update_args[0][2].heartbeat.uptime == 0 assert last_update_args[0][2].heartbeat.vendor_specific_status_code == 0xbe last_update_args.clear() assert list(trk.registry.keys()) == [0xA, 0xB] assert 6 >= trk.registry[0xA].heartbeat.uptime >= 4 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xde assert trk.registry[0xA].info is None assert 3 >= trk.registry[0xB].heartbeat.uptime >= 2 assert trk.registry[0xB].heartbeat.vendor_specific_status_code == 0xbe assert trk.registry[0xB].info is None # Enable get info servers. They will not be queried yet because the tracker node is anonymous. _serve_get_info(p_a, 'node-A') _serve_get_info(p_b, 'node-B') await asyncio.sleep(2.5) assert not last_update_args assert list(trk.registry.keys()) == [0xA, 0xB] assert 9 >= trk.registry[0xA].heartbeat.uptime >= 6 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xde assert trk.registry[0xA].info is None assert 6 >= trk.registry[0xB].heartbeat.uptime >= 4 assert trk.registry[0xB].heartbeat.vendor_specific_status_code == 0xbe assert trk.registry[0xB].info is None # Create a new tracker, this time with a valid node-ID, and make sure node info is requested. # We are going to need a new handler for this. num_events_a = 0 num_events_b = 0 num_events_c = 0 def validating_handler(node_id: int, old: typing.Optional[Entry], new: typing.Optional[Entry]) -> None: nonlocal num_events_a, num_events_b, num_events_c _logger.info('VALIDATING HANDLER %s %s %s', node_id, old, new) if node_id == 0xA: if num_events_a == 0: # First detection assert old is None assert new is not None assert new.heartbeat.vendor_specific_status_code == 0xde assert new.info is None elif num_events_a == 1: # Get info received assert old is not None assert new is not None assert old.heartbeat.vendor_specific_status_code == 0xde assert new.heartbeat.vendor_specific_status_code == 0xde assert old.info is None assert new.info is not None assert new.info.name.tobytes().decode() == 'node-A' elif num_events_a == 2: # Restart detected assert old is not None assert new is not None assert old.heartbeat.vendor_specific_status_code == 0xde assert new.heartbeat.vendor_specific_status_code == 0xfe assert old.info is not None assert new.info is None elif num_events_a == 3: # Get info after restart received assert old is not None assert new is not None assert old.heartbeat.vendor_specific_status_code == 0xfe assert new.heartbeat.vendor_specific_status_code == 0xfe assert old.info is None assert new.info is not None assert new.info.name.tobytes().decode() == 'node-A' elif num_events_a == 4: # Offline assert old is not None assert new is None assert old.heartbeat.vendor_specific_status_code == 0xfe assert old.info is not None else: assert False num_events_a += 1 elif node_id == 0xB: if num_events_b == 0: assert old is None assert new is not None assert new.heartbeat.vendor_specific_status_code == 0xbe assert new.info is None elif num_events_b == 1: assert old is not None assert new is not None assert old.heartbeat.vendor_specific_status_code == 0xbe assert new.heartbeat.vendor_specific_status_code == 0xbe assert old.info is None assert new.info is not None assert new.info.name.tobytes().decode() == 'node-B' elif num_events_b == 2: assert old is not None assert new is None assert old.heartbeat.vendor_specific_status_code == 0xbe assert old.info is not None else: assert False num_events_b += 1 elif node_id == 0xC: if num_events_c == 0: assert old is None assert new is not None assert new.heartbeat.vendor_specific_status_code == 0xf0 assert new.info is None elif num_events_c == 1: assert old is not None assert new is None assert old.heartbeat.vendor_specific_status_code == 0xf0 assert old.info is None else: assert False num_events_c += 1 else: assert False trk.close() trk.close() # Idempotency p_trk = Presentation(get_transport(0xDD)) trk = NodeTracker(p_trk) trk.add_update_handler(validating_handler) trk.start() trk.get_info_timeout = 1.0 trk.get_info_attempts = 2 assert pytest.approx(trk.get_info_timeout) == 1.0 assert trk.get_info_attempts == 2 await asyncio.sleep(2.5) assert num_events_a == 2 assert num_events_b == 2 assert num_events_c == 0 assert list(trk.registry.keys()) == [0xA, 0xB] assert 12 >= trk.registry[0xA].heartbeat.uptime >= 8 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xde assert trk.registry[0xA].info is not None assert trk.registry[0xA].info.name.tobytes().decode() == 'node-A' assert 9 >= trk.registry[0xB].heartbeat.uptime >= 6 assert trk.registry[0xB].heartbeat.vendor_specific_status_code == 0xbe assert trk.registry[0xB].info is not None assert trk.registry[0xB].info.name.tobytes().decode() == 'node-B' # Node B goes offline. hb_b.cancel() await asyncio.sleep(6) assert num_events_a == 2 assert num_events_b == 3 assert num_events_c == 0 assert list(trk.registry.keys()) == [0xA] assert 20 >= trk.registry[0xA].heartbeat.uptime >= 12 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xde assert trk.registry[0xA].info is not None assert trk.registry[0xA].info.name.tobytes().decode() == 'node-A' # Node C appears online. It does not respond to GetInfo. hb_c = asyncio.create_task(_publish_heartbeat(p_c, 0xf0)) await asyncio.sleep(6) assert num_events_a == 2 assert num_events_b == 3 assert num_events_c == 1 assert list(trk.registry.keys()) == [0xA, 0xC] assert 28 >= trk.registry[0xA].heartbeat.uptime >= 17 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xde assert trk.registry[0xA].info is not None assert trk.registry[0xA].info.name.tobytes().decode() == 'node-A' assert 7 >= trk.registry[0xC].heartbeat.uptime >= 5 assert trk.registry[0xC].heartbeat.vendor_specific_status_code == 0xf0 assert trk.registry[0xC].info is None # Node A is restarted. Node C goes offline. hb_c.cancel() hb_a.cancel() hb_a = asyncio.create_task(_publish_heartbeat(p_a, 0xfe)) await asyncio.sleep(6) assert num_events_a == 4 # Two extra events: node restart detection, then get info reception. assert num_events_b == 3 assert num_events_c == 2 assert list(trk.registry.keys()) == [0xA] assert 7 >= trk.registry[0xA].heartbeat.uptime >= 5 assert trk.registry[0xA].heartbeat.vendor_specific_status_code == 0xfe assert trk.registry[0xA].info is not None assert trk.registry[0xA].info.name.tobytes().decode() == 'node-A' # Node A goes offline. No online nodes are left standing. hb_a.cancel() await asyncio.sleep(6) assert num_events_a == 5 assert num_events_b == 3 assert num_events_c == 2 assert not trk.registry # Finalization. trk.close() trk.close() # Idempotency for c in [hb_a, hb_b, hb_c]: c.cancel() finally: for p in [p_a, p_b, p_c, p_trk]: p.close() await asyncio.sleep(1) # Let all pending tasks finalize properly to avoid stack traces in the output.
async def _unittest_spoofer(caplog: pytest.LogCaptureFixture) -> None: dcs_pres = Presentation(LoopbackTransport(1234)) dcs_pub_spoof = dcs_pres.make_publisher(Spoof, 1) spoofer = Spoofer(dcs_pres.make_subscriber(Spoof, 1)) # No target transports configured -- spoofing will do nothing except incrementing the transfer-ID counter. assert await dcs_pub_spoof.publish( Spoof( timeout=uavcan.si.unit.duration.Scalar_1_0(1.0), priority=org_uavcan_yukon.io.transfer.Priority_1_0(3), session=org_uavcan_yukon.io.transfer.Session_0_1( subject=org_uavcan_yukon.io.transfer.SubjectSession_0_1( subject_id=uavcan.node.port.SubjectID_1_0(6666), source=[uavcan.node.ID_1_0(1234)])), transfer_id=[], iface_id=[], payload=org_uavcan_yukon.io.transfer.Payload_1_0(b"Hello world!"), )) await asyncio.sleep(0.5) # Validate the transfer-ID map. assert len(spoofer._transfer_id_map) == 1 assert list(spoofer._transfer_id_map.keys())[0].source_node_id == 1234 assert list(spoofer._transfer_id_map.values())[0]._value == 1 # Configure transports. cap_a: typing.List[pyuavcan.transport.Capture] = [] cap_b: typing.List[pyuavcan.transport.Capture] = [] target_tr_a = LoopbackTransport(None) target_tr_b = LoopbackTransport(None) target_tr_a.begin_capture(cap_a.append) target_tr_b.begin_capture(cap_b.append) spoofer.add_iface(111, target_tr_a) spoofer.add_iface(222, target_tr_b) # Spoof on both, successfully. spoof = Spoof( timeout=uavcan.si.unit.duration.Scalar_1_0(1.0), priority=org_uavcan_yukon.io.transfer.Priority_1_0(3), session=org_uavcan_yukon.io.transfer.Session_0_1( subject=org_uavcan_yukon.io.transfer.SubjectSession_0_1( subject_id=uavcan.node.port.SubjectID_1_0(6666), source=[uavcan.node.ID_1_0(1234)])), transfer_id=[9876543210], # This transfer will not touch the TID map. iface_id=[], # All ifaces. payload=org_uavcan_yukon.io.transfer.Payload_1_0(b"abcd"), ) assert await dcs_pub_spoof.publish(spoof) await asyncio.sleep(0.5) assert len(cap_a) == len(cap_b) (cap, ) = cap_a cap_a.clear() cap_b.clear() assert isinstance(cap, LoopbackCapture) assert cap.transfer.metadata.transfer_id == 9876543210 assert len(spoofer._transfer_id_map) == 1 # New entry was not created. assert spoofer.status == { 111: IfaceStatus(num_bytes=4, num_errors=0, num_timeouts=0, num_transfers=1, backlog=0, backlog_peak=0), 222: IfaceStatus(num_bytes=4, num_errors=0, num_timeouts=0, num_transfers=1, backlog=0, backlog_peak=0), } # Make one time out, the other raise an error, third one is closed. target_tr_a.spoof_result = False target_tr_b.spoof_result = RuntimeError("Intended exception") target_tr_c = LoopbackTransport(None) target_tr_c.close() spoofer.add_iface(0, target_tr_c) with caplog.at_level(logging.CRITICAL): assert await dcs_pub_spoof.publish(spoof) await asyncio.sleep(2.0) assert not cap_a assert not cap_b old_status = spoofer.status assert old_status == { 0: IfaceStatus(num_bytes=0, num_errors=1, num_timeouts=0, num_transfers=0, backlog=0, backlog_peak=0), 111: IfaceStatus(num_bytes=4, num_errors=0, num_timeouts=1, num_transfers=1, backlog=0, backlog_peak=0), 222: IfaceStatus(num_bytes=4, num_errors=1, num_timeouts=0, num_transfers=1, backlog=0, backlog_peak=0), } # Force only one iface out of three. Check that the backlog counter goes up. spoof.iface_id = [0] assert await dcs_pub_spoof.publish(spoof) assert await dcs_pub_spoof.publish(spoof) assert await dcs_pub_spoof.publish(spoof) await asyncio.sleep(2.0) assert spoofer.status[0].backlog > 0 assert spoofer.status[0].backlog_peak > 0 assert spoofer.status[111] == old_status[111] assert spoofer.status[222] == old_status[222] # Finalize. spoofer.close() target_tr_a.close() target_tr_b.close() target_tr_c.close() dcs_pres.close() await asyncio.sleep(1.0)
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 _uid(as_hex: str) -> bytes: out = bytes.fromhex(as_hex) assert len(out) == 16 return out _TABLE = pathlib.Path('allocation_table.sqlite.tmp') if __name__ == '__main__': media = pyuavcan.transport.can.media.socketcan.SocketCANMedia('can0', mtu=64) #transport_client = pyuavcan.transport.can.CANTransport(media, local_node_id=None) transport_server = pyuavcan.transport.can.CANTransport(media, local_node_id=123) #pres_client = Presentation(transport_client) pres_server = Presentation(transport_server) allocator = CentralizedAllocator(pres_server, _uid('deadbeefdeadbeefdeadbeefdeadbeef'), _TABLE) allocator.start() app_tasks = asyncio.Task.all_tasks() async def list_tasks_periodically() -> None: """Print active tasks periodically for demo purposes.""" import re def repr_task(t: asyncio.Task) -> str: try: out, = re.findall(r'^<([^<]+<[^>]+>)', str(t)) except ValueError: out = str(t)
async def _unittest_slow_plug_and_play_centralized( generated_packages: typing.List[pyuavcan.dsdl.GeneratedPackageInfo], mtu: int) -> None: from pyuavcan.application.plug_and_play import CentralizedAllocator, Allocatee assert generated_packages asyncio.get_running_loop().slow_callback_duration = 5.0 peers: typing.Set[MockMedia] = set() pres_client = Presentation(CANTransport(MockMedia(peers, mtu, 1), None)) pres_server = Presentation(CANTransport(MockMedia(peers, mtu, 1), 123)) cln_a = Allocatee(pres_client, _uid("00112233445566778899aabbccddeeff"), 42) assert cln_a.get_result() is None cln_a.start() await asyncio.sleep(2.0) assert cln_a.get_result() is None # Nope, no response. try: _TABLE.unlink() except FileNotFoundError: pass with pytest.raises(ValueError, match=".*anonymous.*"): CentralizedAllocator(pres_client, _uid("deadbeefdeadbeefdeadbeefdeadbeef"), _TABLE) with pytest.raises(ValueError): CentralizedAllocator(pres_client, b"123", _TABLE) allocator = CentralizedAllocator(pres_server, _uid("deadbeefdeadbeefdeadbeefdeadbeef"), _TABLE) allocator.start() allocator.register_node(41, None) allocator.register_node( 41, _uid("00000000000000000000000000000001")) # Overwrites allocator.register_node(42, _uid("00000000000000000000000000000002")) allocator.register_node(42, None) # Does not overwrite allocator.register_node(43, _uid("0000000000000000000000000000000F")) allocator.register_node( 43, _uid("00000000000000000000000000000003")) # Overwrites allocator.register_node(43, None) # Does not overwrite use_v2 = mtu > cln_a._MTU_THRESHOLD # pylint: disable=protected-access await asyncio.sleep(2.0) assert cln_a.get_result() == (44 if use_v2 else 125) # Another request. cln_b = Allocatee(pres_client, _uid("aabbccddeeff00112233445566778899")) assert cln_b.get_result() is None cln_b.start() await asyncio.sleep(2.0) assert cln_b.get_result() == (125 if use_v2 else 124) # Re-request A and make sure we get the same response. cln_a = Allocatee(pres_client, _uid("00112233445566778899aabbccddeeff"), 42) assert cln_a.get_result() is None cln_a.start() await asyncio.sleep(2.0) assert cln_a.get_result() == (44 if use_v2 else 125) # C should be served from the manually added entries above. cln_c = Allocatee(pres_client, _uid("00000000000000000000000000000003")) assert cln_c.get_result() is None cln_c.start() await asyncio.sleep(2.0) assert cln_c.get_result() == 43 # This one requires no allocation because the transport is not anonymous. cln_d = Allocatee(pres_server, _uid("00000000000000000000000000000009"), 100) assert cln_d.get_result() == 123 cln_d.start() await asyncio.sleep(2.0) assert cln_d.get_result() == 123 # No change. # More test coverage needed. # Finalization. cln_a.close() cln_b.close() cln_c.close() cln_d.close() allocator.close() pres_client.close() pres_server.close() await asyncio.sleep(1.0) # Let the tasks finalize properly.
def subscribe( purser: yakut.Purser, subject: typing.Tuple[str, ...], with_metadata: bool, count: typing.Optional[int], ) -> None: """ Subscribe to specified subjects and print messages into stdout. This command does not instantiate a local node and does not disturb the network in any way, so many instances can be cheaply executed concurrently. It is recommended to use anonymous transport (i.e., without a node-ID). The arguments are a list of message data type names prepended with the subject-ID; the subject-ID may be omitted if the data type defines a fixed one: \b [SUBJECT_ID.]TYPE_NAME.MAJOR.MINOR If multiple subjects are specified, a synchronous subscription will be used. It is useful for subscribing to a group of coupled subjects like lockstep sensor feeds, but it will not work for subjects that are temporally unrelated or published at different rates. Each object emitted into stdout is a key-value mapping where the number of elements equals the number of subjects the command is asked to subscribe to; the keys are subject-IDs and values are the received message objects. In data type names forward or backward slashes can be used instead of "."; version numbers can be also separated using underscores. This is done to allow the user to rely on filesystem autocompletion when typing the command. Examples: \b yakut sub 33.uavcan.si.unit.angle.Scalar.1.0 --no-metadata """ _logger.debug("subject=%r, with_metadata=%r, count=%r", subject, with_metadata, count) if not subject: _logger.info("Nothing to do because no subjects are specified") return if count is not None and count <= 0: _logger.info("Nothing to do because count=%s", count) return count = count if count is not None else sys.maxsize formatter = purser.make_formatter() transport = purser.get_transport() if transport.local_node_id is not None: _logger.info( "It is recommended to use an anonymous transport with this command." ) with contextlib.closing(Presentation(transport)) as presentation: subscriber = _make_subscriber(subject, presentation) try: _run(subscriber, formatter, with_metadata=with_metadata, count=count) finally: if _logger.isEnabledFor(logging.INFO): _logger.info("%s", presentation.transport.sample_statistics()) _logger.info("%s", subscriber.sample_statistics())
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.