diff --git a/libuavcan/include/uavcan/internal/transport/transfer.hpp b/libuavcan/include/uavcan/internal/transport/transfer.hpp index 49f7ff8e6d..517f8732f0 100644 --- a/libuavcan/include/uavcan/internal/transport/transfer.hpp +++ b/libuavcan/include/uavcan/internal/transport/transfer.hpp @@ -14,6 +14,8 @@ namespace uavcan enum { MaxTransferPayloadLen = 439 }; ///< According to the standard +enum { MaxSingleFrameTransferPayloadLen = 7 }; + enum TransferType { TransferTypeServiceResponse = 0, diff --git a/libuavcan/include/uavcan/subscriber.hpp b/libuavcan/include/uavcan/subscriber.hpp index 4a18b37e0e..22a56b36af 100644 --- a/libuavcan/include/uavcan/subscriber.hpp +++ b/libuavcan/include/uavcan/subscriber.hpp @@ -20,18 +20,22 @@ namespace uavcan template &), - unsigned int NumStaticBufs = 1, - unsigned int NumStaticReceivers = NumStaticBufs + 1> + unsigned int NumStaticReceivers = 2, + unsigned int NumStaticBufs_ = 1> class Subscriber : Noncopyable { - typedef Subscriber SelfType; + typedef Subscriber SelfType; public: typedef DataType_ DataType; private: - typedef TransferListener::Result, - NumStaticBufs ? NumStaticBufs : 1, // TODO: add support for zero buffers + enum { DataTypeMaxByteLen = BitLenToByteLen::Result }; + enum { NeedsBuffer = int(DataTypeMaxByteLen) > int(MaxSingleFrameTransferPayloadLen) }; + enum { BufferSize = NeedsBuffer ? DataTypeMaxByteLen : 0 }; + enum { NumStaticBufs = NeedsBuffer ? (NumStaticBufs_ ? NumStaticBufs_ : 1) : 0 }; + + typedef TransferListener TransferListenerType; // We need to break the inheritance chain here to implement lazy initialization diff --git a/libuavcan/test/subscriber.cpp b/libuavcan/test/subscriber.cpp index 0bdaa9043e..94df084930 100644 --- a/libuavcan/test/subscriber.cpp +++ b/libuavcan/test/subscriber.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "common.hpp" #include "transport/can/iface_mock.hpp" @@ -80,6 +81,10 @@ TEST(Subscriber, Basic) uavcan::Subscriber sub_simple(sch, poolmgr); uavcan::Subscriber sub_simple2(sch, poolmgr); // Not used + std::cout << + "sizeof(uavcan::Subscriber): " << + sizeof(uavcan::Subscriber) << std::endl; + // Null binder - will fail ASSERT_EQ(-1, sub_extended.start(Listener::ExtendedBinder(NULL, NULL))); @@ -233,3 +238,57 @@ TEST(Subscriber, FailureCount) } ASSERT_EQ(0, sch.getDispatcher().getNumMessageListeners()); // Removed } + + +TEST(Subscriber, SingleFrameTransfer) +{ + uavcan::PoolAllocator pool; + uavcan::PoolManager<1> poolmgr; + poolmgr.addPool(&pool); + + // Manual type registration - we can't rely on the GDTR state + uavcan::GlobalDataTypeRegistry::instance().reset(); + uavcan::DefaultDataTypeRegistrator _registrator; + + SystemClockDriver clock_driver; + CanDriverMock can_driver(2, clock_driver); + + uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr); + + uavcan::Scheduler sch(can_driver, poolmgr, clock_driver, out_trans_reg, uavcan::NodeID(1)); + + typedef SubscriptionListener Listener; + + uavcan::Subscriber sub(sch, poolmgr); + + std::cout << + "sizeof(uavcan::Subscriber): " << + sizeof(uavcan::Subscriber) << std::endl; + + Listener listener; + + sub.start(listener.bindSimple()); + + for (int i = 0; i < 4; i++) + { + // uint_fast16_t data_type_id, TransferType transfer_type, NodeID src_node_id, NodeID dst_node_id, + // uint_fast8_t frame_index, TransferID transfer_id, bool last_frame + uavcan::Frame frame(root_ns_a::EmptyMessage::DefaultDataTypeID, uavcan::TransferTypeMessageBroadcast, + uavcan::NodeID(i + 100), uavcan::NodeID::Broadcast, 0, i, true); + // No payload - message is empty + uavcan::RxFrame rx_frame(frame, clock_driver.getMonotonicMicroseconds(), + clock_driver.getUtcMicroseconds(), 0); + can_driver.ifaces[0].pushRx(rx_frame); + can_driver.ifaces[1].pushRx(rx_frame); + } + + ASSERT_LE(0, sch.spin(clock_driver.getMonotonicMicroseconds() + 10000)); + + ASSERT_EQ(0, sub.getFailureCount()); + + ASSERT_EQ(4, listener.simple.size()); + for (unsigned int i = 0; i < 4; i++) + { + ASSERT_TRUE(listener.simple.at(i) == root_ns_a::EmptyMessage()); + } +}