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.
Example #2
0
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.
Example #3
0
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)
Example #4
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.
Example #5
0
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.
Example #6
0
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.