Modified CAN driver API: Passing the next pending TX frames into the select() call to facilitate proper prioritization in the driver

This commit is contained in:
Pavel Kirienko
2015-07-05 18:46:21 +03:00
parent b4ba088e08
commit 8d923fee4c
10 changed files with 127 additions and 24 deletions
+3 -1
View File
@@ -75,7 +75,9 @@ struct PairableCanDriver : public uavcan::ICanDriver, public uavcan::ICanIface
virtual uavcan::uint8_t getNumIfaces() const { return 1; }
virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks, uavcan::MonotonicTime blocking_deadline)
virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks,
const uavcan::CanFrame* (&)[uavcan::MaxCanIfaces],
uavcan::MonotonicTime blocking_deadline)
{
if (inout_masks.read == 1)
{
+20 -1
View File
@@ -54,6 +54,7 @@ public:
uint64_t num_errors;
uavcan::ISystemClock& iclock;
bool enable_utc_timestamping;
uavcan::CanFrame pending_tx;
CanIfaceMock(uavcan::ISystemClock& iclock)
: writeable(true)
@@ -88,6 +89,17 @@ public:
return (frame_time.frame == frame) && (frame_time.time == tx_deadline);
}
bool matchPendingTx(const uavcan::CanFrame& frame) const
{
if (pending_tx != frame)
{
std::cout << "Pending TX mismatch: \n"
<< " Expected: " << frame.toString(uavcan::CanFrame::StrAligned) << "\n"
<< " Actual: " << pending_tx.toString(uavcan::CanFrame::StrAligned) << std::endl;
}
return pending_tx == frame;
}
bool matchAndPopTx(const uavcan::CanFrame& frame, uint64_t tx_deadline_usec)
{
return matchAndPopTx(frame, uavcan::MonotonicTime::fromUSec(tx_deadline_usec));
@@ -196,11 +208,18 @@ public:
}
}
virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks, uavcan::MonotonicTime deadline)
virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks,
const uavcan::CanFrame* (& pending_tx)[uavcan::MaxCanIfaces],
uavcan::MonotonicTime deadline)
{
assert(this);
//std::cout << "Write/read masks: " << inout_write_iface_mask << "/" << inout_read_iface_mask << std::endl;
for (unsigned i = 0; i < ifaces.size(); i++)
{
ifaces.at(i).pending_tx = (pending_tx[i] == NULL) ? uavcan::CanFrame() : *pending_tx[i];
}
if (select_failure)
{
return -1;
+10 -6
View File
@@ -13,13 +13,15 @@ TEST(CanDriverMock, Basic)
SystemClockMock clockmock;
CanDriverMock driver(3, clockmock);
const uavcan::CanFrame* pending_tx[uavcan::MaxCanIfaces] = { };
ASSERT_EQ(3, driver.getNumIfaces());
// All WR, no RD
CanSelectMasks masks;
masks.write = 7;
masks.read = 7;
EXPECT_LT(0, driver.select(masks, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_LT(0, driver.select(masks, pending_tx, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(7, masks.write);
EXPECT_EQ(0, masks.read);
@@ -31,7 +33,7 @@ TEST(CanDriverMock, Basic)
// No WR, no RD
masks.write = 7;
masks.read = 7;
EXPECT_EQ(0, driver.select(masks, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(0, driver.select(masks, pending_tx, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(0, masks.write);
EXPECT_EQ(0, masks.read);
EXPECT_EQ(100, clockmock.monotonic);
@@ -42,7 +44,7 @@ TEST(CanDriverMock, Basic)
driver.ifaces.at(1).pushRx(fr1);
masks.write = 7;
masks.read = 6;
EXPECT_LT(0, driver.select(masks, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_LT(0, driver.select(masks, pending_tx, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(0, masks.write);
EXPECT_EQ(2, masks.read);
CanFrame fr2;
@@ -60,7 +62,7 @@ TEST(CanDriverMock, Basic)
driver.select_failure = true;
masks.write = 1;
masks.read = 7;
EXPECT_EQ(-1, driver.select(masks, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(-1, driver.select(masks, pending_tx, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(1, masks.write); // Leaving masks unchanged - the library must ignore them
EXPECT_EQ(7, masks.read);
}
@@ -73,10 +75,12 @@ TEST(CanDriverMock, Loopback)
SystemClockMock clockmock;
CanDriverMock driver(1, clockmock);
const uavcan::CanFrame* pending_tx[uavcan::MaxCanIfaces] = { };
CanSelectMasks masks;
masks.write = 1;
masks.read = 1;
EXPECT_LT(0, driver.select(masks, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_LT(0, driver.select(masks, pending_tx, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(1, masks.write);
EXPECT_EQ(0, masks.read);
@@ -88,7 +92,7 @@ TEST(CanDriverMock, Loopback)
masks.write = 0;
masks.read = 1;
EXPECT_LT(0, driver.select(masks, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_LT(0, driver.select(masks, pending_tx, uavcan::MonotonicTime::fromUSec(100)));
EXPECT_EQ(0, masks.write);
EXPECT_EQ(1, masks.read);
+27
View File
@@ -141,9 +141,13 @@ TEST(CanIOManager, Transmission)
EXPECT_EQ(2, iomgr.send(frames[0], tsMono(100), tsMono(0), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[0], 100));
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 100));
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
EXPECT_EQ(1, iomgr.send(frames[1], tsMono(200), tsMono(100), 2, CanTxQueue::Persistent, flags)); // To #1 only
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[1], 200));
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(uavcan::CanFrame()));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1]));
EXPECT_EQ(0, clockmock.monotonic);
EXPECT_EQ(0, clockmock.utc);
@@ -166,11 +170,15 @@ TEST(CanIOManager, Transmission)
EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
EXPECT_EQ(1, pool.getNumUsedBlocks()); // One frame went into TX queue, and will expire soon
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0])); // This one will persist
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(uavcan::CanFrame())); // This will drop off on the second select()
// Sending to both, both blocked
driver.ifaces.at(1).writeable = false;
EXPECT_EQ(0, iomgr.send(frames[1], tsMono(777), tsMono(300), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
EXPECT_EQ(3, pool.getNumUsedBlocks()); // Total 3 frames in TX queue now
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0])); // Still 0
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1])); // 1!!
// Sending to #0, both blocked
EXPECT_EQ(0, iomgr.send(frames[2], tsMono(888), tsMono(400), 1, CanTxQueue::Persistent, flags));
@@ -179,6 +187,8 @@ TEST(CanIOManager, Transmission)
EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
EXPECT_EQ(4, pool.getNumUsedBlocks());
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1]));
// At this time TX queues are containing the following data:
// iface 0: frames[0] (EXPIRED), frames[1], frames[2]
@@ -193,6 +203,8 @@ TEST(CanIOManager, Transmission)
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 999)); // In different order due to prioritization
EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0])); // Expired but still will be reported
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
// Calling receive() to flush the rest two frames
uavcan::CanRxFrame dummy_rx_frame;
@@ -200,6 +212,8 @@ TEST(CanIOManager, Transmission)
EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[2], 888));
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[1], 777));
ASSERT_EQ(0, flags);
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[2]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1]));
// Final checks
EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
@@ -220,6 +234,9 @@ TEST(CanIOManager, Transmission)
// One frame kicked here:
EXPECT_EQ(0, iomgr.send(frames[1], tsMono(4444), tsMono(1200), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[1]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
// State checks
EXPECT_EQ(4, pool.getNumUsedBlocks()); // TX queue is full
EXPECT_EQ(1200, clockmock.monotonic);
@@ -241,12 +258,16 @@ TEST(CanIOManager, Transmission)
EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[1], 4444));
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 3333));
ASSERT_EQ(0, flags);
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[1]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
EXPECT_EQ(1, iomgr.receive(rx_frame, tsMono(0), flags));
EXPECT_TRUE(rxFrameEquals(rx_frame, rx_frames[1], 1200, 1));
EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[2], 2222));
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[2], 2222)); // Iface #1, frame[1] was rejected (VOLATILE)
ASSERT_EQ(0, flags);
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[2]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[2]));
// State checks
EXPECT_EQ(0, pool.getNumUsedBlocks()); // TX queue is empty
@@ -268,6 +289,8 @@ TEST(CanIOManager, Transmission)
EXPECT_EQ(1200, clockmock.monotonic);
EXPECT_EQ(1200, clockmock.utc);
ASSERT_EQ(0, flags);
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
// Transmission failure
driver.select_failure = false;
@@ -277,6 +300,8 @@ TEST(CanIOManager, Transmission)
driver.ifaces.at(1).tx_failure = true;
// Non-blocking - return < 0
EXPECT_GE(0, iomgr.send(frames[0], tsMono(2200), tsMono(0), ALL_IFACES_MASK, CanTxQueue::Persistent, flags));
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
ASSERT_EQ(2, pool.getNumUsedBlocks()); // Untransmitted frames will be buffered
@@ -288,6 +313,8 @@ TEST(CanIOManager, Transmission)
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 2200));
EXPECT_EQ(0, pool.getNumUsedBlocks()); // All transmitted
ASSERT_EQ(0, flags);
EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(uavcan::CanFrame())); // Last call will be receive-only,
EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(uavcan::CanFrame())); // hence empty TX
/*
* Perf counters