Пример #1
0
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.
Пример #2
0
 def one(nid: typing.Optional[int]) -> RedundantTransport:
     red = RedundantTransport()
     red.attach_inferior(
         CANTransport(SocketCANMedia("vcan0", 64), nid))
     red.attach_inferior(
         CANTransport(SocketCANMedia("vcan1", 32), nid))
     return red
Пример #3
0
        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
Пример #4
0
 def fact(nid_a: typing.Optional[int],
          nid_b: typing.Optional[int]) -> TransportPack:
     bus: typing.Set[MockMedia] = set()
     media_a = MockMedia(bus, 8, 1)
     media_b = MockMedia(bus, 64, 2)  # Heterogeneous setup
     assert bus == {media_a, media_b}
     return CANTransport(media_a, nid_a), CANTransport(media_b,
                                                       nid_b), True
Пример #5
0
        def can_socketcan_vcan0() -> typing.Iterator[TransportFactory]:
            from pyuavcan.transport.can import CANTransport
            from pyuavcan.transport.can.media.socketcan import SocketCANMedia

            yield lambda nid_a, nid_b: (
                CANTransport(SocketCANMedia("vcan0", 16), nid_a),
                CANTransport(SocketCANMedia("vcan0", 64), nid_b),
                True,
            )
Пример #6
0
 def one(nid: typing.Optional[int]) -> RedundantTransport:
     red = RedundantTransport()
     red.attach_inferior(CANTransport(MockMedia(
         bus_0, 8, 1), nid))  # Heterogeneous setup (CAN classic)
     red.attach_inferior(CANTransport(MockMedia(
         bus_1, 32, 2), nid))  # Heterogeneous setup (CAN FD)
     red.attach_inferior(CANTransport(MockMedia(
         bus_2, 64, 3), nid))  # Heterogeneous setup (CAN FD)
     return red
Пример #7
0
def _make_transport_can(node_id_a: typing.Optional[int],
                        node_id_b: typing.Optional[int]) -> TransportPack:
    from pyuavcan.transport.can import CANTransport
    from tests.transport.can.media.mock import MockMedia
    bus: typing.Set[MockMedia] = set()
    media_a = MockMedia(bus, 8, 1)
    media_b = MockMedia(bus, 64, 2)  # Heterogeneous setup
    assert bus == {media_a, media_b}
    return CANTransport(media_a, node_id_a), CANTransport(media_b,
                                                          node_id_b), True
Пример #8
0
 def one(nid: typing.Optional[int]) -> RedundantTransport:
     # Triply redundant CAN bus.
     red = RedundantTransport()
     red.attach_inferior(CANTransport(MockMedia(
         bus_0, 8, 1), nid))  # Heterogeneous setup (CAN classic)
     red.attach_inferior(CANTransport(MockMedia(bus_1, 32, 1),
                                      nid))  # Heterogeneous setup (CAN FD)
     red.attach_inferior(CANTransport(MockMedia(bus_2, 64, 1),
                                      nid))  # Heterogeneous setup (CAN FD)
     print('REDUNDANT TRANSPORT CANx3:', red)
     return red
Пример #9
0
def _make_can(
        registers: MutableMapping[str, ValueProxy],
        node_id: Optional[int]) -> Iterator[pyuavcan.transport.Transport]:
    def init(name: str, default: RelaxedValue) -> ValueProxy:
        return registers.setdefault("uavcan.can." + name, ValueProxy(default))

    iface_list = str(init("iface", "")).split()
    mtu = int(init("mtu", Natural16([64])))
    br_arb, br_data = init("bitrate", Natural32([1_000_000, 4_000_000])).ints

    if iface_list:
        from pyuavcan.transport.can import CANTransport

        for iface in iface_list:
            media: pyuavcan.transport.can.media.Media
            if iface.lower().startswith("socketcan:"):
                from pyuavcan.transport.can.media.socketcan import SocketCANMedia

                media = SocketCANMedia(iface.split(":")[-1], mtu=mtu)
            else:
                from pyuavcan.transport.can.media.pythoncan import PythonCANMedia

                media = PythonCANMedia(
                    iface, br_arb if br_arb == br_data else (br_arb, br_data),
                    mtu)
            yield CANTransport(media, node_id)
Пример #10
0
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,
        )
Пример #11
0
async def _unittest_redundant_transport_capture() -> None:
    from threading import Lock
    from pyuavcan.transport import Capture, Trace, TransferTrace, Priority, ServiceDataSpecifier
    from pyuavcan.transport import AlienTransfer, AlienTransferMetadata, AlienSessionSpecifier
    from pyuavcan.transport.redundant import RedundantDuplicateTransferTrace, RedundantCapture
    from tests.transport.can.media.mock import MockMedia as CANMockMedia

    asyncio.get_event_loop().slow_callback_duration = 5.0

    tracer = RedundantTransport.make_tracer()
    traces: typing.List[typing.Optional[Trace]] = []
    lock = Lock()

    def handle_capture(cap: Capture) -> None:
        with lock:
            # Drop TX frames, they are not interesting for this test.
            assert isinstance(cap, RedundantCapture)
            if isinstance(cap.inferior, pyuavcan.transport.serial.SerialCapture
                          ) and cap.inferior.own:
                return
            if isinstance(
                    cap.inferior,
                    pyuavcan.transport.can.CANCapture) and cap.inferior.own:
                return
            print("CAPTURE:", cap)
            traces.append(tracer.update(cap))

    async def wait(how_many: int) -> None:
        for _ in range(10):
            await asyncio.sleep(0.1)
            with lock:
                if len(traces) >= how_many:
                    return
        assert False, "No traces received"

    # Setup capture -- one is added before capture started, the other is added later.
    # Make sure they are treated identically.
    tr = RedundantTransport()
    inf_a: pyuavcan.transport.Transport = SerialTransport(SERIAL_URI, 1234)
    inf_b: pyuavcan.transport.Transport = SerialTransport(SERIAL_URI, 1234)
    tr.attach_inferior(inf_a)
    assert not tr.capture_active
    assert not inf_a.capture_active
    assert not inf_b.capture_active
    tr.begin_capture(handle_capture)
    assert tr.capture_active
    assert inf_a.capture_active
    assert not inf_b.capture_active
    tr.attach_inferior(inf_b)
    assert tr.capture_active
    assert inf_a.capture_active
    assert inf_b.capture_active

    # Send a transfer and make sure it is handled and deduplicated correctly.
    transfer = AlienTransfer(
        AlienTransferMetadata(
            priority=Priority.IMMEDIATE,
            transfer_id=1234,
            session_specifier=AlienSessionSpecifier(
                source_node_id=321,
                destination_node_id=222,
                data_specifier=ServiceDataSpecifier(
                    77, ServiceDataSpecifier.Role.REQUEST),
            ),
        ),
        [memoryview(b"hello")],
    )
    assert await tr.spoof(transfer,
                          monotonic_deadline=asyncio.get_event_loop().time() +
                          1.0)
    await wait(2)
    with lock:
        # Check the status of the deduplication process. We should get two: one transfer, one duplicate.
        assert len(traces) == 2
        trace = traces.pop(0)
        assert isinstance(trace, TransferTrace)
        assert trace.transfer == transfer
        # This is the duplicate.
        assert isinstance(traces.pop(0), RedundantDuplicateTransferTrace)
        assert not traces

    # Spoof the same thing again, get nothing out: transfers discarded by the inferior's own reassemblers.
    # WARNING: this will fail if too much time has passed since the previous transfer due to TID timeout.
    assert await tr.spoof(transfer,
                          monotonic_deadline=asyncio.get_event_loop().time() +
                          1.0)
    await wait(2)
    with lock:
        assert None is traces.pop(0)
        assert None is traces.pop(0)
        assert not traces

    # But if we change ONLY destination, deduplication will not take place.
    transfer = AlienTransfer(
        AlienTransferMetadata(
            priority=Priority.IMMEDIATE,
            transfer_id=1234,
            session_specifier=AlienSessionSpecifier(
                source_node_id=321,
                destination_node_id=333,
                data_specifier=ServiceDataSpecifier(
                    77, ServiceDataSpecifier.Role.REQUEST),
            ),
        ),
        [memoryview(b"hello")],
    )
    assert await tr.spoof(transfer,
                          monotonic_deadline=asyncio.get_event_loop().time() +
                          1.0)
    await wait(2)
    with lock:
        # Check the status of the deduplication process. We should get two: one transfer, one duplicate.
        assert len(traces) == 2
        trace = traces.pop(0)
        assert isinstance(trace, TransferTrace)
        assert trace.transfer == transfer
        # This is the duplicate.
        assert isinstance(traces.pop(0), RedundantDuplicateTransferTrace)
        assert not traces

    # Change the inferior configuration and make sure it is handled properly.
    tr.detach_inferior(inf_a)
    tr.detach_inferior(inf_b)
    inf_a.close()
    inf_b.close()
    # The new inferiors use cyclic transfer-ID; the tracer should reconfigure itself automatically!
    can_peers: typing.Set[CANMockMedia] = set()
    inf_a = CANTransport(CANMockMedia(can_peers, 64, 2), 111)
    inf_b = CANTransport(CANMockMedia(can_peers, 64, 2), 111)
    tr.attach_inferior(inf_a)
    tr.attach_inferior(inf_b)
    # Capture should have been launched automatically.
    assert inf_a.capture_active
    assert inf_b.capture_active

    # Send transfer over CAN and observe that it is handled well.
    transfer = AlienTransfer(
        AlienTransferMetadata(
            priority=Priority.IMMEDIATE,
            transfer_id=19,
            session_specifier=AlienSessionSpecifier(
                source_node_id=111,
                destination_node_id=22,
                data_specifier=ServiceDataSpecifier(
                    77, ServiceDataSpecifier.Role.REQUEST),
            ),
        ),
        [memoryview(b"hello")],
    )
    assert await tr.spoof(transfer,
                          monotonic_deadline=asyncio.get_event_loop().time() +
                          1.0)
    await wait(2)
    with lock:
        # Check the status of the deduplication process. We should get two: one transfer, one duplicate.
        assert len(traces) == 2
        trace = traces.pop(0)
        assert isinstance(trace, TransferTrace)
        assert trace.transfer == transfer
        # This is the duplicate.
        assert isinstance(traces.pop(0), RedundantDuplicateTransferTrace)
        assert not traces

    # Dispose of everything.
    tr.close()
    await asyncio.sleep(1.0)
Пример #12
0
async def _unittest_slow_plug_and_play_centralized(
    compiled: typing.List[pyuavcan.dsdl.GeneratedPackageInfo], mtu: int
) -> None:
    from pyuavcan.application import make_node, NodeInfo
    from pyuavcan.application.plug_and_play import CentralizedAllocator, Allocatee

    assert compiled
    asyncio.get_running_loop().slow_callback_duration = 5.0

    peers: typing.Set[MockMedia] = set()
    trans_client = CANTransport(MockMedia(peers, mtu, 1), None)
    node_server = make_node(
        NodeInfo(unique_id=_uid("deadbeefdeadbeefdeadbeefdeadbeef")),
        transport=CANTransport(MockMedia(peers, mtu, 1), 123),
    )
    node_server.start()

    cln_a = Allocatee(trans_client, _uid("00112233445566778899aabbccddeeff"), 42)
    assert cln_a.get_result() is None
    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(make_node(NodeInfo(), transport=trans_client), _TABLE)
    allocator = CentralizedAllocator(node_server, _TABLE)

    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(trans_client, _uid("aabbccddeeff00112233445566778899"))
    assert cln_b.get_result() is None
    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(trans_client, _uid("00112233445566778899aabbccddeeff"), 42)
    assert cln_a.get_result() is None
    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(trans_client, _uid("00000000000000000000000000000003"))
    assert cln_c.get_result() is None
    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(node_server.presentation, _uid("00000000000000000000000000000009"), 100)
    assert cln_d.get_result() == 123
    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()
    trans_client.close()
    node_server.close()
    await asyncio.sleep(1.0)  # Let the tasks finalize properly.
Пример #13
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 = 1.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
    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.