Merge branch 'dynamic_node_id'

This commit is contained in:
Pavel Kirienko
2015-04-26 07:51:05 +03:00
27 changed files with 707 additions and 24 deletions
@@ -60,6 +60,15 @@ public:
MonotonicDuration getTxTimeout() const { return tx_timeout_; }
void setTxTimeout(MonotonicDuration tx_timeout);
/**
* By default, attempt to send a transfer from passive mode will result in an error @ref ErrPassive.
* This option allows to enable sending anonymous transfers from passive mode.
*/
void allowAnonymousTransfers()
{
sender_->allowAnonymousTransfers();
}
INode& getNode() const { return node_; }
};
@@ -67,6 +67,7 @@ public:
TransferID getTransferID() const { return safeget<TransferID, &IncomingTransfer::getTransferID>(); }
NodeID getSrcNodeID() const { return safeget<NodeID, &IncomingTransfer::getSrcNodeID>(); }
uint8_t getIfaceIndex() const { return safeget<uint8_t, &IncomingTransfer::getIfaceIndex>(); }
bool isAnonymousTransfer() const { return safeget<bool, &IncomingTransfer::isAnonymousTransfer>(); }
};
/**
@@ -200,6 +201,15 @@ protected:
return genericStart(&Dispatcher::registerServiceResponseListener);
}
/**
* By default, anonymous transfers will be ignored.
* This option allows to enable reception of anonymous transfers.
*/
void allowAnonymousTransfers()
{
forwarder_->allowAnonymousTransfers();
}
/**
* Terminate the subscription.
* Dispatcher core will remove this instance from the subscribers list.
@@ -87,6 +87,7 @@ public:
*/
using BaseType::init;
using BaseType::allowAnonymousTransfers;
using BaseType::getTransferSender;
using BaseType::getMinTxTimeout;
using BaseType::getMaxTxTimeout;
@@ -111,6 +111,7 @@ public:
return BaseType::startAsMessageListener();
}
using BaseType::allowAnonymousTransfers;
using BaseType::stop;
using BaseType::getFailureCount;
};
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#ifndef UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_CLIENT_HPP_INCLUDED
#define UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_CLIENT_HPP_INCLUDED
#include <uavcan/node/subscriber.hpp>
#include <uavcan/node/publisher.hpp>
#include <uavcan/node/timer.hpp>
#include <uavcan/util/method_binder.hpp>
#include <uavcan/build_config.hpp>
#include <uavcan/protocol/dynamic_node_id/Allocation.hpp>
#include <uavcan/protocol/HardwareVersion.hpp>
namespace uavcan
{
/**
* This class implements client-side logic of dynamic node ID allocation procedure.
*
* Once started, the object will be publishing dynamic node ID allocation requests at the default frequency defined
* by the specification, until a Node ID is granted by the allocator.
*
* If the local node is equipped with redundant CAN interfaces, all of them will be used for publishing requests
* and listening for responses.
*
* Once dynamic allocation is complete (or not needed anymore), the object can be deleted.
*/
class DynamicNodeIDAllocationClient : private TimerBase
{
typedef MethodBinder<DynamicNodeIDAllocationClient*,
void (DynamicNodeIDAllocationClient::*)
(const ReceivedDataStructure<protocol::dynamic_node_id::Allocation>&)>
AllocationCallback;
Publisher<protocol::dynamic_node_id::Allocation> dnida_pub_;
Subscriber<protocol::dynamic_node_id::Allocation, AllocationCallback> dnida_sub_;
uint8_t unique_id_[protocol::HardwareVersion::FieldTypes::unique_id::MaxSize];
NodeID preferred_node_id_;
NodeID allocated_node_id_;
NodeID allocator_node_id_;
void terminate();
virtual void handleTimerEvent(const TimerEvent&);
void handleAllocation(const ReceivedDataStructure<protocol::dynamic_node_id::Allocation>& msg);
public:
DynamicNodeIDAllocationClient(INode& node)
: TimerBase(node)
, dnida_pub_(node)
, dnida_sub_(node)
{ }
/**
* @param hardware_version Hardware version information, where unique_id must be set correctly.
* @param preferred_node_id Node ID that the application would like to take; set to broadcast (zero) if
* the application doesn't have any preference (this is default).
* @return Zero on success
* Negative error code on failure
* -ErrLogic if 1. the node is not in passive mode or 2. the client is already started
*/
int start(const protocol::HardwareVersion& hardware_version, const NodeID preferred_node_id = NodeID::Broadcast);
/**
* Use this method to determine when allocation is complete.
*/
bool isAllocationComplete() const { return getAllocatedNodeID().isUnicast(); }
/**
* This method allows to retrieve the node ID that was allocated to the local node.
* If no node ID was allocated yet, the returned node ID will be invalid (non-unicast).
* @return If allocation is complete, a valid unicast node ID will be returned.
* If allocation is not complete yet, a non-unicast node ID will be returned.
*/
NodeID getAllocatedNodeID() const { return allocated_node_id_; }
/**
* This method allows to retrieve node ID of the allocator that granted our Node ID.
* If no node ID was allocated yet, the returned node ID will be invalid (non-unicast).
* @return If allocation is complete, a valid unicast node ID will be returned.
* If allocation is not complete yet, an non-unicast node ID will be returned.
*/
NodeID getAllocatorNodeID() const { return allocator_node_id_; }
};
}
#endif // UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_CLIENT_HPP_INCLUDED
+1
View File
@@ -7,6 +7,7 @@
#include <uavcan/build_config.hpp>
#include <cstdarg>
#include <cstddef>
#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
# error UAVCAN_CPP_VERSION
@@ -115,6 +115,7 @@ public:
: canio_(driver, allocator, sysclock)
, sysclock_(sysclock)
, outgoing_transfer_reg_(otr)
, self_node_id_(NodeID::Broadcast) // Default
, self_node_id_is_set_(false)
{ }
+1 -1
View File
@@ -50,7 +50,7 @@ public:
{
UAVCAN_ASSERT((transfer_type == TransferTypeMessageBroadcast) == dst_node_id.isBroadcast());
UAVCAN_ASSERT(data_type_id.isValid());
UAVCAN_ASSERT(src_node_id != dst_node_id);
UAVCAN_ASSERT(src_node_id.isUnicast() ? (src_node_id != dst_node_id) : true);
UAVCAN_ASSERT(frame_index <= MaxIndex);
}
@@ -50,6 +50,11 @@ public:
*/
virtual void release() { }
/**
* Whether this is a anonymous transfer
*/
virtual bool isAnonymousTransfer() const { return false; }
MonotonicTime getMonotonicTimestamp() const { return ts_mono_; }
UtcTime getUtcTimestamp() const { return ts_utc_; }
TransferType getTransferType() const { return transfer_type_; }
@@ -68,6 +73,7 @@ class UAVCAN_EXPORT SingleFrameIncomingTransfer : public IncomingTransfer
public:
explicit SingleFrameIncomingTransfer(const RxFrame& frm);
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
virtual bool isAnonymousTransfer() const;
};
/**
@@ -93,6 +99,7 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListene
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers_;
ITransferBufferManager& bufmgr_;
TransferPerfCounter& perf_;
bool allow_anonymous_transfers_;
class TimedOutReceiverPredicate
{
@@ -119,17 +126,25 @@ protected:
, receivers_(receivers)
, bufmgr_(bufmgr)
, perf_(perf)
, allow_anonymous_transfers_(false)
{ }
virtual ~TransferListenerBase() { }
void handleReception(TransferReceiver& receiver, const RxFrame& frame, TransferBufferAccessor& tba);
void handleAnonymousTransferReception(const RxFrame& frame);
virtual void handleIncomingTransfer(IncomingTransfer& transfer) = 0;
public:
const DataTypeDescriptor& getDataTypeDescriptor() const { return data_type_; }
/**
* By default, anonymous transfers will be ignored.
* This option allows to enable reception of anonymous transfers.
*/
void allowAnonymousTransfers() { allow_anonymous_transfers_ = true; }
void cleanup(MonotonicTime ts);
virtual void handleFrame(const RxFrame& frame);
@@ -25,6 +25,7 @@ class UAVCAN_EXPORT TransferSender
const TransferCRC crc_base_;
CanIOFlags flags_;
uint8_t iface_mask_;
bool allow_anonymous_transfers_;
Dispatcher& dispatcher_;
@@ -46,6 +47,7 @@ public:
, crc_base_(data_type.getSignature().toTransferCRC())
, flags_(CanIOFlags(0))
, iface_mask_(AllIfacesMask)
, allow_anonymous_transfers_(false)
, dispatcher_(dispatcher)
{ }
@@ -59,6 +61,15 @@ public:
iface_mask_ = iface_mask;
}
/**
* Anonymous transfers (i.e. transfers that don't carry a valid Source Node ID) can be sent if
* the local node is configured in passive mode (i.e. the node doesn't have a valid Node ID).
* By default, this class will return an error if it is asked to send a transfer while the
* node is configured in passive mode. However, if this option is enabled, it will be possible
* to send anonymous transfers from passive mode.
*/
void allowAnonymousTransfers() { allow_anonymous_transfers_ = true; }
/**
* Send with explicit Transfer ID.
* Should be used only for service responses, where response TID should match request TID.
@@ -0,0 +1,177 @@
/*
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <uavcan/protocol/dynamic_node_id_allocation_client.hpp>
namespace uavcan
{
void DynamicNodeIDAllocationClient::terminate()
{
UAVCAN_TRACE("DynamicNodeIDAllocationClient", "Client terminated");
stop();
dnida_sub_.stop();
}
void DynamicNodeIDAllocationClient::handleTimerEvent(const TimerEvent&)
{
// This method implements Rule B
UAVCAN_ASSERT(preferred_node_id_.isValid());
if (isAllocationComplete())
{
UAVCAN_ASSERT(0);
terminate();
return;
}
// Filling the message
protocol::dynamic_node_id::Allocation msg;
msg.node_id = preferred_node_id_.get();
msg.first_part_of_unique_id = true;
msg.unique_id.resize(protocol::dynamic_node_id::Allocation::MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST);
copy(unique_id_, unique_id_ + msg.unique_id.size(), msg.unique_id.begin());
UAVCAN_ASSERT(equal(msg.unique_id.begin(), msg.unique_id.end(), unique_id_));
// Broadcasting
UAVCAN_TRACE("DynamicNodeIDAllocationClient", "Broadcasting 1st stage: preferred ID: %d",
static_cast<int>(preferred_node_id_.get()));
const int res = dnida_pub_.broadcast(msg);
if (res < 0)
{
dnida_pub_.getNode().registerInternalFailure("DynamicNodeIDAllocationClient request failed");
}
}
void DynamicNodeIDAllocationClient::handleAllocation(
const ReceivedDataStructure<protocol::dynamic_node_id::Allocation>& msg)
{
/*
* TODO This method can blow the stack easily
*/
UAVCAN_ASSERT(preferred_node_id_.isValid());
if (isAllocationComplete())
{
UAVCAN_ASSERT(0);
terminate();
return;
}
startPeriodic(getPeriod()); // Restarting the timer - Rule C
UAVCAN_TRACE("DynamicNodeIDAllocationClient", "Request timer reset because of Allocation message from %d",
static_cast<int>(msg.getSrcNodeID().get()));
// Rule D
if (!msg.isAnonymousTransfer() &&
msg.unique_id.size() > 0 &&
msg.unique_id.size() < msg.unique_id.capacity() &&
equal(msg.unique_id.begin(), msg.unique_id.end(), unique_id_))
{
// Filling the response message
const uint8_t size_of_unique_id_in_response =
min(protocol::dynamic_node_id::Allocation::MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST,
static_cast<uint8_t>(msg.unique_id.capacity() - msg.unique_id.size()));
protocol::dynamic_node_id::Allocation second_stage;
second_stage.node_id = preferred_node_id_.get();
second_stage.first_part_of_unique_id = false;
second_stage.unique_id.resize(size_of_unique_id_in_response);
copy(unique_id_ + msg.unique_id.size(),
unique_id_ + msg.unique_id.size() + size_of_unique_id_in_response,
second_stage.unique_id.begin());
UAVCAN_ASSERT(equal(second_stage.unique_id.begin(),
second_stage.unique_id.end(),
unique_id_ + msg.unique_id.size()));
// Broadcasting the response
UAVCAN_TRACE("DynamicNodeIDAllocationClient",
"Broadcasting 2nd stage: preferred ID: %d, size of unique ID: %d",
static_cast<int>(preferred_node_id_.get()), static_cast<int>(second_stage.unique_id.size()));
const int res = dnida_pub_.broadcast(second_stage);
if (res < 0)
{
dnida_pub_.getNode().registerInternalFailure("DynamicNodeIDAllocationClient request failed");
}
}
// Rule E
if (!msg.isAnonymousTransfer() &&
msg.unique_id.size() == msg.unique_id.capacity() &&
equal(msg.unique_id.begin(), msg.unique_id.end(), unique_id_) &&
msg.node_id > 0)
{
allocated_node_id_ = msg.node_id;
allocator_node_id_ = msg.getSrcNodeID();
UAVCAN_TRACE("DynamicNodeIDAllocationClient", "Allocation complete, node ID %d provided by %d",
static_cast<int>(allocated_node_id_.get()), static_cast<int>(allocator_node_id_.get()));
terminate();
UAVCAN_ASSERT(isAllocationComplete());
}
}
int DynamicNodeIDAllocationClient::start(const protocol::HardwareVersion& hardware_version,
const NodeID preferred_node_id)
{
terminate();
// Allocation is not possible if node ID is already set
if (dnida_pub_.getNode().getNodeID().isUnicast())
{
return -ErrLogic;
}
// Unique ID initialization & validation
copy(hardware_version.unique_id.begin(), hardware_version.unique_id.end(), unique_id_);
bool unique_id_is_zero = true;
for (uint8_t i = 0; i < sizeof(unique_id_); i++)
{
if (unique_id_[i] != 0)
{
unique_id_is_zero = false;
break;
}
}
if (unique_id_is_zero)
{
return -ErrInvalidParam;
}
if (!preferred_node_id.isValid()) // Only broadcast and unicast are allowed
{
return -ErrInvalidParam;
}
// Initializing the fields
preferred_node_id_ = preferred_node_id;
allocated_node_id_ = NodeID();
allocator_node_id_ = NodeID();
UAVCAN_ASSERT(preferred_node_id_.isValid());
UAVCAN_ASSERT(!allocated_node_id_.isValid());
UAVCAN_ASSERT(!allocator_node_id_.isValid());
// Initializing node objects - Rule A
int res = dnida_pub_.init();
if (res < 0)
{
return res;
}
dnida_pub_.allowAnonymousTransfers();
res = dnida_sub_.start(AllocationCallback(this, &DynamicNodeIDAllocationClient::handleAllocation));
if (res < 0)
{
return res;
}
dnida_sub_.allowAnonymousTransfers();
startPeriodic(MonotonicDuration::fromMSec(protocol::dynamic_node_id::Allocation::DEFAULT_REQUEST_PERIOD_MS));
return 0;
}
}
+5 -2
View File
@@ -170,9 +170,12 @@ bool Frame::isValid() const
const bool invalid =
(frame_index_ > MaxIndex) ||
((frame_index_ == MaxIndex) && !last_frame_) ||
(!src_node_id_.isUnicast()) ||
(!src_node_id_.isValid()) ||
(!dst_node_id_.isValid()) ||
(src_node_id_ == dst_node_id_) ||
(src_node_id_.isUnicast() ? (src_node_id_ == dst_node_id_) : false) ||
(src_node_id_.isBroadcast()
? (!last_frame_ || (frame_index_ > 0) || (transfer_type_ != TransferTypeMessageBroadcast))
: false) ||
((transfer_type_ == TransferTypeMessageBroadcast) != dst_node_id_.isBroadcast()) ||
(transfer_type_ >= NumTransferTypes) ||
(static_cast<int>(payload_len_) > getMaxPayloadLen()) ||
@@ -50,6 +50,11 @@ int SingleFrameIncomingTransfer::read(unsigned offset, uint8_t* data, unsigned l
return int(len);
}
bool SingleFrameIncomingTransfer::isAnonymousTransfer() const
{
return (getTransferType() == TransferTypeMessageBroadcast) && getSrcNodeID().isBroadcast();
}
/*
* MultiFrameIncomingTransfer
*/
@@ -172,6 +177,16 @@ void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxF
}
}
void TransferListenerBase::handleAnonymousTransferReception(const RxFrame& frame)
{
if (allow_anonymous_transfers_)
{
perf_.addRxTransfer();
SingleFrameIncomingTransfer it(frame);
handleIncomingTransfer(it);
}
}
void TransferListenerBase::cleanup(MonotonicTime ts)
{
receivers_.removeWhere(TimedOutReceiverPredicate(ts, bufmgr_));
@@ -180,26 +195,40 @@ void TransferListenerBase::cleanup(MonotonicTime ts)
void TransferListenerBase::handleFrame(const RxFrame& frame)
{
const TransferBufferManagerKey key(frame.getSrcNodeID(), frame.getTransferType());
TransferReceiver* recv = receivers_.access(key);
if (recv == NULL)
if (frame.getSrcNodeID().isUnicast()) // Normal transfer
{
if (!frame.isFirst())
{
return;
}
const TransferBufferManagerKey key(frame.getSrcNodeID(), frame.getTransferType());
TransferReceiver new_recv;
recv = receivers_.insert(key, new_recv);
TransferReceiver* recv = receivers_.access(key);
if (recv == NULL)
{
UAVCAN_TRACE("TransferListener", "Receiver registration failed; frame %s", frame.toString().c_str());
return;
if (!frame.isFirst())
{
return;
}
TransferReceiver new_recv;
recv = receivers_.insert(key, new_recv);
if (recv == NULL)
{
UAVCAN_TRACE("TransferListener", "Receiver registration failed; frame %s", frame.toString().c_str());
return;
}
}
TransferBufferAccessor tba(bufmgr_, key);
handleReception(*recv, frame, tba);
}
else if (frame.getSrcNodeID().isBroadcast() &&
frame.isFirst() &&
frame.isLast() &&
frame.getDstNodeID().isBroadcast()) // Anonymous transfer
{
handleAnonymousTransferReception(frame);
}
else
{
UAVCAN_TRACE("TransferListenerBase", "Invalid frame: %s", frame.toString().c_str()); // Invalid frame
}
TransferBufferAccessor tba(bufmgr_, key);
handleReception(*recv, frame, tba);
}
}
+23 -3
View File
@@ -19,15 +19,35 @@ int TransferSender::send(const uint8_t* payload, unsigned payload_len, Monotonic
MonotonicTime blocking_deadline, TransferType transfer_type, NodeID dst_node_id,
TransferID tid)
{
if (payload_len > MaxTransferPayloadLen)
{
UAVCAN_ASSERT(0);
return -ErrInvalidParam;
}
Frame frame(data_type_.getID(), transfer_type, dispatcher_.getNodeID(), dst_node_id, 0, tid);
UAVCAN_TRACE("TransferSender", "%s", frame.toString().c_str());
/*
* Checking if we're allowed to send.
* In passive mode we can send only anonymous transfers, if they are enabled.
*/
if (dispatcher_.isPassiveMode())
{
return -ErrPassiveMode;
const bool allow = allow_anonymous_transfers_ &&
(transfer_type == TransferTypeMessageBroadcast) &&
(int(payload_len) <= frame.getMaxPayloadLen());
if (!allow)
{
return -ErrPassiveMode;
}
}
dispatcher_.getTransferPerfCounter().addTxTransfer();
Frame frame(data_type_.getID(), transfer_type, dispatcher_.getNodeID(), dst_node_id, 0, tid);
/*
* Sending frames
*/
if (frame.getMaxPayloadLen() >= int(payload_len)) // Single Frame Transfer
{
const int res = frame.setPayload(payload, payload_len);
@@ -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));
}
+12 -1
View File
@@ -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());
}
+14 -1
View File
@@ -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)