mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-06-27 16:30:35 +08:00
Merge branch 'dynamic_node_id'
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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