mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-06-27 17:30:35 +08:00
Merge branch 'dynamic_node_id'
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/protocol/dynamic_node_id_allocation_client.hpp>
|
||||
#include "helpers.hpp"
|
||||
|
||||
|
||||
TEST(DynamicNodeIDAllocationClient, Basic)
|
||||
{
|
||||
// Node A is Allocator, Node B is Allocatee
|
||||
InterlinkedTestNodesWithSysClock nodes(uavcan::NodeID(10), uavcan::NodeID::Broadcast);
|
||||
|
||||
uavcan::DynamicNodeIDAllocationClient dnidac(nodes.b);
|
||||
|
||||
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::Allocation> _reg1;
|
||||
(void)_reg1;
|
||||
|
||||
/*
|
||||
* Client initialization
|
||||
*/
|
||||
uavcan::protocol::HardwareVersion hwver;
|
||||
|
||||
ASSERT_LE(-uavcan::ErrInvalidParam, dnidac.start(hwver)); // Empty hardware version is not allowed
|
||||
|
||||
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
|
||||
{
|
||||
hwver.unique_id[i] = i;
|
||||
}
|
||||
|
||||
ASSERT_LE(-uavcan::ErrInvalidParam, dnidac.start(hwver, uavcan::NodeID()));
|
||||
|
||||
const uavcan::NodeID PreferredNodeID = 42;
|
||||
ASSERT_LE(0, dnidac.start(hwver, PreferredNodeID));
|
||||
|
||||
ASSERT_FALSE(dnidac.getAllocatedNodeID().isValid());
|
||||
ASSERT_FALSE(dnidac.getAllocatorNodeID().isValid());
|
||||
ASSERT_FALSE(dnidac.isAllocationComplete());
|
||||
|
||||
/*
|
||||
* Subscriber (server emulation)
|
||||
*/
|
||||
SubscriberWithCollector<uavcan::protocol::dynamic_node_id::Allocation> dynid_sub(nodes.a);
|
||||
ASSERT_LE(0, dynid_sub.start());
|
||||
dynid_sub.subscriber.allowAnonymousTransfers();
|
||||
|
||||
/*
|
||||
* Monitoring requests at 1Hz
|
||||
*/
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1100));
|
||||
ASSERT_TRUE(dynid_sub.collector.msg.get());
|
||||
std::cout << "First-stage request:\n" << *dynid_sub.collector.msg << std::endl;
|
||||
ASSERT_EQ(PreferredNodeID.get(), dynid_sub.collector.msg->node_id);
|
||||
ASSERT_TRUE(dynid_sub.collector.msg->first_part_of_unique_id);
|
||||
ASSERT_TRUE(uavcan::equal(dynid_sub.collector.msg->unique_id.begin(),
|
||||
dynid_sub.collector.msg->unique_id.end(),
|
||||
hwver.unique_id.begin()));
|
||||
dynid_sub.collector.msg.reset();
|
||||
|
||||
// Rate validation
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(500));
|
||||
ASSERT_FALSE(dynid_sub.collector.msg.get());
|
||||
|
||||
// Second - rate is 1 Hz
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(500));
|
||||
ASSERT_TRUE(dynid_sub.collector.msg.get());
|
||||
dynid_sub.collector.msg.reset();
|
||||
|
||||
ASSERT_FALSE(dnidac.getAllocatedNodeID().isValid());
|
||||
ASSERT_FALSE(dnidac.getAllocatorNodeID().isValid());
|
||||
ASSERT_FALSE(dnidac.isAllocationComplete());
|
||||
|
||||
/*
|
||||
* Publisher (server emulation)
|
||||
*/
|
||||
uavcan::Publisher<uavcan::protocol::dynamic_node_id::Allocation> dynid_pub(nodes.a);
|
||||
ASSERT_LE(0, dynid_pub.init());
|
||||
|
||||
/*
|
||||
* Sending some some Allocation messages - the timer will keep restarting
|
||||
*/
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
uavcan::protocol::dynamic_node_id::Allocation msg; // Contents of the message doesn't matter
|
||||
ASSERT_LE(0, dynid_pub.broadcast(msg));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(210));
|
||||
ASSERT_FALSE(dynid_sub.collector.msg.get());
|
||||
}
|
||||
|
||||
/*
|
||||
* Responding with partially matching unique ID - the client will respond with second-stage request immediately
|
||||
*/
|
||||
{
|
||||
uavcan::protocol::dynamic_node_id::Allocation msg;
|
||||
msg.unique_id.resize(7);
|
||||
uavcan::copy(hwver.unique_id.begin(), hwver.unique_id.begin() + 7, msg.unique_id.begin());
|
||||
|
||||
std::cout << "First-stage offer:\n" << msg << std::endl;
|
||||
|
||||
ASSERT_FALSE(dynid_sub.collector.msg.get());
|
||||
ASSERT_LE(0, dynid_pub.broadcast(msg));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(100));
|
||||
|
||||
ASSERT_TRUE(dynid_sub.collector.msg.get());
|
||||
std::cout << "Second-stage request:\n" << *dynid_sub.collector.msg << std::endl;
|
||||
ASSERT_EQ(PreferredNodeID.get(), dynid_sub.collector.msg->node_id);
|
||||
ASSERT_FALSE(dynid_sub.collector.msg->first_part_of_unique_id);
|
||||
ASSERT_TRUE(uavcan::equal(dynid_sub.collector.msg->unique_id.begin(),
|
||||
dynid_sub.collector.msg->unique_id.end(),
|
||||
hwver.unique_id.begin() + 7));
|
||||
dynid_sub.collector.msg.reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Responding with second-stage offer, expecting the last request back
|
||||
*/
|
||||
{
|
||||
uavcan::protocol::dynamic_node_id::Allocation msg;
|
||||
msg.unique_id.resize(14);
|
||||
uavcan::copy(hwver.unique_id.begin(), hwver.unique_id.begin() + 14, msg.unique_id.begin());
|
||||
|
||||
std::cout << "Second-stage offer:\n" << msg << std::endl;
|
||||
|
||||
ASSERT_FALSE(dynid_sub.collector.msg.get());
|
||||
ASSERT_LE(0, dynid_pub.broadcast(msg));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(100));
|
||||
|
||||
ASSERT_TRUE(dynid_sub.collector.msg.get());
|
||||
std::cout << "Last request:\n" << *dynid_sub.collector.msg << std::endl;
|
||||
ASSERT_EQ(PreferredNodeID.get(), dynid_sub.collector.msg->node_id);
|
||||
ASSERT_FALSE(dynid_sub.collector.msg->first_part_of_unique_id);
|
||||
ASSERT_TRUE(uavcan::equal(dynid_sub.collector.msg->unique_id.begin(),
|
||||
dynid_sub.collector.msg->unique_id.end(),
|
||||
hwver.unique_id.begin() + 14));
|
||||
dynid_sub.collector.msg.reset();
|
||||
}
|
||||
|
||||
ASSERT_FALSE(dnidac.getAllocatedNodeID().isValid());
|
||||
ASSERT_FALSE(dnidac.getAllocatorNodeID().isValid());
|
||||
ASSERT_FALSE(dnidac.isAllocationComplete());
|
||||
|
||||
/*
|
||||
* Now we have full unique ID for this client received, and it is possible to grant allocation
|
||||
*/
|
||||
{
|
||||
uavcan::protocol::dynamic_node_id::Allocation msg;
|
||||
msg.unique_id.resize(16);
|
||||
msg.node_id = 72;
|
||||
uavcan::copy(hwver.unique_id.begin(), hwver.unique_id.end(), msg.unique_id.begin());
|
||||
|
||||
ASSERT_FALSE(dynid_sub.collector.msg.get());
|
||||
ASSERT_LE(0, dynid_pub.broadcast(msg));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1100));
|
||||
ASSERT_FALSE(dynid_sub.collector.msg.get());
|
||||
}
|
||||
|
||||
ASSERT_EQ(uavcan::NodeID(72), dnidac.getAllocatedNodeID());
|
||||
ASSERT_EQ(uavcan::NodeID(10), dnidac.getAllocatorNodeID());
|
||||
ASSERT_TRUE(dnidac.isAllocationComplete());
|
||||
}
|
||||
|
||||
|
||||
TEST(DynamicNodeIDAllocationClient, NonPassiveMode)
|
||||
{
|
||||
InterlinkedTestNodesWithSysClock nodes;
|
||||
|
||||
uavcan::DynamicNodeIDAllocationClient dnidac(nodes.b);
|
||||
|
||||
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::Allocation> _reg1;
|
||||
(void)_reg1;
|
||||
|
||||
uavcan::protocol::HardwareVersion hwver;
|
||||
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
|
||||
{
|
||||
hwver.unique_id[i] = i;
|
||||
}
|
||||
|
||||
ASSERT_LE(-uavcan::ErrLogic, dnidac.start(hwver));
|
||||
}
|
||||
@@ -162,7 +162,18 @@ TEST(Frame, FrameParsing)
|
||||
|
||||
can.id = CanFrame::FlagEFF | // cppcheck-suppress duplicateExpression
|
||||
(2 << 0) | (1 << 3) | (0 << 4) | (0 << 10) | (uavcan::TransferTypeMessageUnicast << 17) | (456 << 19);
|
||||
ASSERT_FALSE(frame.parse(can)); // Broadcast Src Node ID
|
||||
ASSERT_FALSE(frame.parse(can)); // Broadcast Src Node ID with unicast transfer
|
||||
|
||||
can.id = CanFrame::FlagEFF | // cppcheck-suppress duplicateExpression
|
||||
(2 << 0) | (0 << 3) | (0 << 4) | (0 << 10) | (uavcan::TransferTypeMessageBroadcast << 17) | (456 << 19);
|
||||
ASSERT_FALSE(frame.parse(can)); // Broadcast Src Node ID with multiframe broadcast transfer
|
||||
|
||||
/*
|
||||
* Broadcast SNID exceptions
|
||||
*/
|
||||
can.id = CanFrame::FlagEFF | // cppcheck-suppress duplicateExpression
|
||||
(2 << 0) | (1 << 3) | (0 << 4) | (0 << 10) | (uavcan::TransferTypeMessageBroadcast << 17) | (456 << 19);
|
||||
ASSERT_TRUE(frame.parse(can)); // Broadcast Src Node ID with single frame broadcast transfer
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -243,3 +243,38 @@ TEST(TransferListener, MaximumTransferLength)
|
||||
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferListener, AnonymousTransfers)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
uavcan::TransferPerfCounter perf;
|
||||
TestListener<0, 0, 0> subscriber(perf, type, poolmgr);
|
||||
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer transfers[] =
|
||||
{
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 0, "12345678"), // Invalid - not broadcast
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 0, "12345678"), // Valid
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 0, "123456789"), // Invalid - not SFT
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 0, "") // Valid
|
||||
};
|
||||
|
||||
emulator.send(transfers);
|
||||
|
||||
// Nothing will be received, because anonymous transfers are disabled by default
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
|
||||
subscriber.allowAnonymousTransfers();
|
||||
|
||||
// Re-send everything again
|
||||
emulator.send(transfers);
|
||||
|
||||
// Now the anonymous transfers are enabled
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[1])); // Only SFT broadcast will be accepted
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[3]));
|
||||
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
|
||||
@@ -235,11 +235,24 @@ TEST(TransferSender, PassiveMode)
|
||||
|
||||
static const uint8_t Payload[] = {1, 2, 3, 4, 5};
|
||||
|
||||
// By default, sending in passive mode is not enabled
|
||||
ASSERT_EQ(-uavcan::ErrPassiveMode,
|
||||
sender.send(Payload, sizeof(Payload), tsMono(1000), uavcan::MonotonicTime(),
|
||||
uavcan::TransferTypeMessageBroadcast, uavcan::NodeID::Broadcast));
|
||||
|
||||
// Overriding the default
|
||||
sender.allowAnonymousTransfers();
|
||||
|
||||
// OK, now we can broadcast in any mode
|
||||
ASSERT_LE(0, sender.send(Payload, sizeof(Payload), tsMono(1000), uavcan::MonotonicTime(),
|
||||
uavcan::TransferTypeMessageBroadcast, uavcan::NodeID::Broadcast));
|
||||
|
||||
// ...but not unicast or anything else
|
||||
ASSERT_EQ(-uavcan::ErrPassiveMode,
|
||||
sender.send(Payload, sizeof(Payload), tsMono(1000), uavcan::MonotonicTime(),
|
||||
uavcan::TransferTypeMessageUnicast, uavcan::NodeID(42)));
|
||||
|
||||
EXPECT_EQ(0, dispatcher.getTransferPerfCounter().getErrorCount());
|
||||
EXPECT_EQ(0, dispatcher.getTransferPerfCounter().getTxTransferCount());
|
||||
EXPECT_EQ(1, dispatcher.getTransferPerfCounter().getTxTransferCount());
|
||||
EXPECT_EQ(0, dispatcher.getTransferPerfCounter().getRxTransferCount());
|
||||
}
|
||||
|
||||
@@ -129,6 +129,14 @@ public:
|
||||
const Transfer rx(transfer, Base::getDataTypeDescriptor());
|
||||
transfers_.push(rx);
|
||||
std::cout << "Received transfer: " << rx.toString() << std::endl;
|
||||
|
||||
const bool single_frame = dynamic_cast<uavcan::SingleFrameIncomingTransfer*>(&transfer) != NULL;
|
||||
|
||||
const bool anonymous = single_frame &&
|
||||
transfer.getSrcNodeID().isBroadcast() &&
|
||||
(transfer.getTransferType() == uavcan::TransferTypeMessageBroadcast);
|
||||
|
||||
ASSERT_EQ(anonymous, transfer.isAnonymousTransfer());
|
||||
}
|
||||
|
||||
bool matchAndPop(const Transfer& reference)
|
||||
|
||||
Reference in New Issue
Block a user