diff --git a/libuavcan/include/uavcan/transport/transfer_receiver.hpp b/libuavcan/include/uavcan/transport/transfer_receiver.hpp index 737c9bc2db..b2fcb7ace2 100644 --- a/libuavcan/include/uavcan/transport/transfer_receiver.hpp +++ b/libuavcan/include/uavcan/transport/transfer_receiver.hpp @@ -19,31 +19,41 @@ class UAVCAN_EXPORT TransferReceiver public: enum ResultCode { ResultNotComplete, ResultComplete, ResultSingleFrame }; - static const uint32_t MinTransferIntervalUSec = 1 * 1000UL; - static const uint32_t MaxTransferIntervalUSec = 10 * 1000 * 1000UL; - static const uint32_t DefaultTransferIntervalUSec = 1 * 1000 * 1000UL; + static const uint16_t MinTransferIntervalMSec = 1; + static const uint16_t MaxTransferIntervalMSec = 0xFFFF; + static const uint16_t DefaultTransferIntervalMSec = 1000; static MonotonicDuration getDefaultTransferInterval() { - return MonotonicDuration::fromUSec(DefaultTransferIntervalUSec); + return MonotonicDuration::fromMSec(DefaultTransferIntervalMSec); } - static MonotonicDuration getMinTransferInterval() { return MonotonicDuration::fromUSec(MinTransferIntervalUSec); } - static MonotonicDuration getMaxTransferInterval() { return MonotonicDuration::fromUSec(MaxTransferIntervalUSec); } + static MonotonicDuration getMinTransferInterval() { return MonotonicDuration::fromMSec(MinTransferIntervalMSec); } + static MonotonicDuration getMaxTransferInterval() { return MonotonicDuration::fromMSec(MaxTransferIntervalMSec); } private: enum TidRelation { TidSame, TidRepeat, TidFuture }; - static const uint8_t IfaceIndexNotSet = 0xFF; + + enum { IfaceIndexNotSet = MaxCanIfaces }; + + enum { BufferWritePosMask = 4095 }; + enum { ErrorCntMask = 15 }; + enum { IfaceIndexMask = MaxCanIfaces }; MonotonicTime prev_transfer_ts_; MonotonicTime this_transfer_ts_; UtcTime first_frame_ts_; - uint32_t transfer_interval_usec_; + uint16_t transfer_interval_msec_; uint16_t this_transfer_crc_; - uint16_t buffer_write_pos_; - TransferID tid_; - uint8_t iface_index_; - uint8_t next_frame_index_; - mutable uint8_t error_cnt_; + + // 2 byte aligned bitfields: + uint16_t buffer_write_pos_ : 12; + mutable uint16_t error_cnt_ : 4; + + TransferID tid_; // 1 byte field + + // 1 byte aligned bitfields: + uint8_t next_frame_index_ : 6; + uint8_t iface_index_ : 2; bool isInitialized() const { return iface_index_ != IfaceIndexNotSet; } @@ -59,14 +69,18 @@ private: ResultCode receive(const RxFrame& frame, TransferBufferAccessor& tba); public: - TransferReceiver() - : transfer_interval_usec_(DefaultTransferIntervalUSec) - , this_transfer_crc_(0) - , buffer_write_pos_(0) - , iface_index_(IfaceIndexNotSet) - , next_frame_index_(0) - , error_cnt_(0) - { } + TransferReceiver() : + transfer_interval_msec_(DefaultTransferIntervalMSec), + this_transfer_crc_(0), + buffer_write_pos_(0), + error_cnt_(0), + next_frame_index_(0), + iface_index_(IfaceIndexNotSet) + { +#if UAVCAN_DEBUG + StaticAssert::check(); +#endif + } bool isTimedOut(MonotonicTime current_ts) const; @@ -79,7 +93,7 @@ public: uint16_t getLastTransferCrc() const { return this_transfer_crc_; } - MonotonicDuration getInterval() const { return MonotonicDuration::fromUSec(transfer_interval_usec_); } + MonotonicDuration getInterval() const { return MonotonicDuration::fromMSec(transfer_interval_msec_); } }; UAVCAN_PACKED_END diff --git a/libuavcan/src/transport/uc_transfer_receiver.cpp b/libuavcan/src/transport/uc_transfer_receiver.cpp index 7d85a0b749..9b20ace8e5 100644 --- a/libuavcan/src/transport/uc_transfer_receiver.cpp +++ b/libuavcan/src/transport/uc_transfer_receiver.cpp @@ -11,21 +11,13 @@ namespace uavcan { -const uint32_t TransferReceiver::MinTransferIntervalUSec; -const uint32_t TransferReceiver::MaxTransferIntervalUSec; -const uint32_t TransferReceiver::DefaultTransferIntervalUSec; -const uint8_t TransferReceiver::IfaceIndexNotSet; +const uint16_t TransferReceiver::MinTransferIntervalMSec; +const uint16_t TransferReceiver::MaxTransferIntervalMSec; +const uint16_t TransferReceiver::DefaultTransferIntervalMSec; void TransferReceiver::registerError() const { - if (error_cnt_ < 0xFF) - { - error_cnt_ = static_cast(error_cnt_ + 1); - } - else - { - UAVCAN_ASSERT(0); - } + error_cnt_ = static_cast(error_cnt_ + 1) & ErrorCntMask; } TransferReceiver::TidRelation TransferReceiver::getTidRelation(const RxFrame& frame) const @@ -51,10 +43,10 @@ void TransferReceiver::updateTransferTimings() if ((!prev_prev_ts.isZero()) && (!prev_transfer_ts_.isZero()) && (prev_transfer_ts_ >= prev_prev_ts)) { - uint64_t interval_usec = uint64_t((prev_transfer_ts_ - prev_prev_ts).toUSec()); - interval_usec = min(interval_usec, uint64_t(MaxTransferIntervalUSec)); - interval_usec = max(interval_usec, uint64_t(MinTransferIntervalUSec)); - transfer_interval_usec_ = static_cast((uint64_t(transfer_interval_usec_) * 7 + interval_usec) / 8); + uint64_t interval_msec = uint64_t((prev_transfer_ts_ - prev_prev_ts).toMSec()); + interval_msec = min(interval_msec, uint64_t(MaxTransferIntervalMSec)); + interval_msec = max(interval_msec, uint64_t(MinTransferIntervalMSec)); + transfer_interval_msec_ = static_cast((uint64_t(transfer_interval_msec_) * 7U + interval_msec) / 8U); } } @@ -118,7 +110,7 @@ bool TransferReceiver::writePayload(const RxFrame& frame, ITransferBuffer& buf) const bool success = res == static_cast(effective_payload_len); if (success) { - buffer_write_pos_ = static_cast(buffer_write_pos_ + effective_payload_len); + buffer_write_pos_ = static_cast(buffer_write_pos_ + effective_payload_len) & BufferWritePosMask; } return success; } @@ -128,7 +120,7 @@ bool TransferReceiver::writePayload(const RxFrame& frame, ITransferBuffer& buf) const bool success = res == static_cast(payload_len); if (success) { - buffer_write_pos_ = static_cast(buffer_write_pos_ + payload_len); + buffer_write_pos_ = static_cast(buffer_write_pos_ + payload_len) & BufferWritePosMask; } return success; } @@ -186,12 +178,12 @@ TransferReceiver::ResultCode TransferReceiver::receive(const RxFrame& frame, Tra bool TransferReceiver::isTimedOut(MonotonicTime current_ts) const { - static const int64_t INTERVAL_MULT = (1 << TransferID::BitLen) / 2 + 1; + static const int64_t IntervalMult = (1 << TransferID::BitLen) / 2 + 1; if (current_ts <= this_transfer_ts_) { return false; } - return (current_ts - this_transfer_ts_).toUSec() > (int64_t(transfer_interval_usec_) * INTERVAL_MULT); + return (current_ts - this_transfer_ts_).toUSec() > (int64_t(transfer_interval_msec_) * 1000 * IntervalMult); } TransferReceiver::ResultCode TransferReceiver::addFrame(const RxFrame& frame, TransferBufferAccessor& tba) @@ -209,7 +201,7 @@ TransferReceiver::ResultCode TransferReceiver::addFrame(const RxFrame& frame, Tr const bool first_fame = frame.isFirst(); const TidRelation tid_rel = getTidRelation(frame); const bool iface_timed_out = - (frame.getMonotonicTimestamp() - this_transfer_ts_).toUSec() > (int64_t(transfer_interval_usec_) * 2); + (frame.getMonotonicTimestamp() - this_transfer_ts_).toUSec() > (int64_t(transfer_interval_msec_) * 1000 * 2); // FSM, the hard way const bool need_restart = @@ -230,7 +222,7 @@ TransferReceiver::ResultCode TransferReceiver::addFrame(const RxFrame& frame, Tr int(not_initialized), int(iface_timed_out), int(receiver_timed_out), int(same_iface), int(first_fame), int(tid_rel), frame.toString().c_str()); tba.remove(); - iface_index_ = frame.getIfaceIndex(); + iface_index_ = frame.getIfaceIndex() & IfaceIndexMask; tid_ = frame.getTransferID(); next_frame_index_ = 0; buffer_write_pos_ = 0; diff --git a/libuavcan/test/transport/transfer_receiver.cpp b/libuavcan/test/transport/transfer_receiver.cpp index 12ae485d75..68aebf2204 100644 --- a/libuavcan/test/transport/transfer_receiver.cpp +++ b/libuavcan/test/transport/transfer_receiver.cpp @@ -144,8 +144,8 @@ TEST(TransferReceiver, Basic) ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678abcdefgh")); ASSERT_EQ(0x789A, rcv.getLastTransferCrc()); - ASSERT_GT(TransferReceiver::getDefaultTransferInterval(), rcv.getInterval()); - ASSERT_LT(TransferReceiver::getMinTransferInterval(), rcv.getInterval()); + ASSERT_GE(TransferReceiver::getDefaultTransferInterval(), rcv.getInterval()); + ASSERT_LE(TransferReceiver::getMinTransferInterval(), rcv.getInterval()); ASSERT_EQ(1000, rcv.getLastTransferTimestampMonotonic().toUSec()); ASSERT_FALSE(rcv.isTimedOut(tsMono(1000))); ASSERT_FALSE(rcv.isTimedOut(tsMono(5000))); @@ -174,33 +174,47 @@ TEST(TransferReceiver, Basic) CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "", 0, true, 4, 3600), bk)); ASSERT_EQ(3600, rcv.getLastTransferTimestampMonotonic().toUSec()); + std::cout << "Interval: " << rcv.getInterval().toString() << std::endl; + /* * Timeouts */ - CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwe", 0, true, 1, 100000), bk)); // Wrong iface - ignored + CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwe", 0, true, 1, 5000), bk)); // Wrong iface - ignored CHECK_SINGLE_FRAME(rcv.addFrame(gen(1, "qwe", 0, true, 6, 1500000), bk)); // Accepted due to iface timeout ASSERT_EQ(1500000, rcv.getLastTransferTimestampMonotonic().toUSec()); + std::cout << "Interval: " << rcv.getInterval().toString() << std::endl; + CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwe", 0, true, 7, 1500100), bk)); // Ignored - old iface 0 CHECK_SINGLE_FRAME(rcv.addFrame(gen(1, "qwe", 0, true, 7, 1500100), bk)); ASSERT_EQ(1500100, rcv.getLastTransferTimestampMonotonic().toUSec()); + std::cout << "Interval: " << rcv.getInterval().toString() << std::endl; + CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwe", 0, true, 7, 1500100), bk)); // Old TID CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "qwe", 0, true, 7, 100000000), bk)); // Accepted - global timeout ASSERT_EQ(100000000, rcv.getLastTransferTimestampMonotonic().toUSec()); + std::cout << "Interval: " << rcv.getInterval().toString() << std::endl; + CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "qwe", 0, true, 0, 100000100), bk)); ASSERT_EQ(100000100, rcv.getLastTransferTimestampMonotonic().toUSec()); + std::cout << "Interval: " << rcv.getInterval().toString() << std::endl; + ASSERT_TRUE(rcv.isTimedOut(tsMono(900000000))); CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "\x78\x56" "345678", 0, false, 0, 900000000), bk)); // Global timeout CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "12345678", 0, false, 0, 900000100), bk)); // Wrong iface CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwe", 1, true, 0, 900000200), bk)); // Wrong iface CHECK_COMPLETE( rcv.addFrame(gen(1, "qwe", 1, true, 0, 900000200), bk)); + ASSERT_EQ(900000000, rcv.getLastTransferTimestampMonotonic().toUSec()); + std::cout << "Interval: " << rcv.getInterval().toString() << std::endl; + ASSERT_FALSE(rcv.isTimedOut(tsMono(1000))); - ASSERT_FALSE(rcv.isTimedOut(tsMono(900000200))); - ASSERT_TRUE(rcv.isTimedOut(tsMono(1000 * 1000000))); + ASSERT_FALSE(rcv.isTimedOut(tsMono(900000300))); + ASSERT_TRUE(rcv.isTimedOut(tsMono(990000000))); + ASSERT_LT(TransferReceiver::getDefaultTransferInterval(), rcv.getInterval()); ASSERT_LE(TransferReceiver::getMinTransferInterval(), rcv.getInterval()); ASSERT_GE(TransferReceiver::getMaxTransferInterval(), rcv.getInterval());