Reorganized directory structure - directory 'internal' pulled up one level. No logical changes.

This commit is contained in:
Pavel Kirienko
2014-03-14 15:01:20 +04:00
parent ab34c94ba2
commit 15cbf96378
90 changed files with 165 additions and 166 deletions
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <uavcan/node/scheduler.hpp>
#include <uavcan/data_type.hpp>
#include <uavcan/node/marshal_buffer.hpp>
#include <uavcan/node/global_data_type_registry.hpp>
#include <uavcan/util/lazy_constructor.hpp>
#include <uavcan/debug.hpp>
#include <uavcan/transport/transfer_sender.hpp>
#include <uavcan/marshal/scalar_codec.hpp>
#include <uavcan/marshal/types.hpp>
namespace uavcan
{
/**
* Generic publisher, suitable for messages and services.
* DataSpec - data type specification class
* DataStruct - instantiable class
*/
template <typename DataSpec, typename DataStruct>
class GenericPublisher
{
enum { Qos = (DataTypeKind(DataSpec::DataTypeKind) == DataTypeKindMessage) ?
CanTxQueue::Volatile : CanTxQueue::Persistent };
const MonotonicDuration max_transfer_interval_; // TODO: memory usage can be reduced
MonotonicDuration tx_timeout_;
Scheduler& scheduler_;
IMarshalBufferProvider& buffer_provider_;
LazyConstructor<TransferSender> sender_;
bool checkInit()
{
if (sender_)
return true;
GlobalDataTypeRegistry::instance().freeze();
const DataTypeDescriptor* const descr =
GlobalDataTypeRegistry::instance().find(DataTypeKind(DataSpec::DataTypeKind),
DataSpec::getDataTypeFullName());
if (!descr)
{
UAVCAN_TRACE("GenericPublisher", "Type [%s] is not registered", DataSpec::getDataTypeFullName());
return false;
}
sender_.template construct<Dispatcher&, const DataTypeDescriptor&, CanTxQueue::Qos, MonotonicDuration>
(scheduler_.getDispatcher(), *descr, CanTxQueue::Qos(Qos), max_transfer_interval_);
return true;
}
MonotonicTime getTxDeadline() const { return scheduler_.getMonotonicTimestamp() + tx_timeout_; }
IMarshalBuffer* getBuffer()
{
return buffer_provider_.getBuffer(BitLenToByteLen<DataStruct::MaxBitLen>::Result);
}
int genericPublish(const DataStruct& message, TransferType transfer_type, NodeID dst_node_id,
TransferID* tid, MonotonicTime blocking_deadline)
{
if (!checkInit())
return -1;
IMarshalBuffer* const buf = getBuffer();
if (!buf)
return -1;
{
BitStream bitstream(*buf);
ScalarCodec codec(bitstream);
const int encode_res = DataStruct::encode(message, codec);
if (encode_res <= 0)
{
assert(0); // Impossible, internal error
return -1;
}
}
if (tid)
{
return sender_->send(buf->getDataPtr(), buf->getDataLength(), getTxDeadline(),
blocking_deadline, transfer_type, dst_node_id, *tid);
}
else
{
return sender_->send(buf->getDataPtr(), buf->getDataLength(), getTxDeadline(),
blocking_deadline, transfer_type, dst_node_id);
}
}
public:
GenericPublisher(Scheduler& scheduler, IMarshalBufferProvider& buffer_provider, MonotonicDuration tx_timeout,
MonotonicDuration max_transfer_interval = TransferSender::getDefaultMaxTransferInterval())
: max_transfer_interval_(max_transfer_interval)
, tx_timeout_(tx_timeout)
, scheduler_(scheduler)
, buffer_provider_(buffer_provider)
{
setTxTimeout(tx_timeout);
#if UAVCAN_DEBUG
assert(getTxTimeout() == tx_timeout); // Making sure default values are OK
#endif
}
~GenericPublisher() { }
int publish(const DataStruct& message, TransferType transfer_type, NodeID dst_node_id,
MonotonicTime blocking_deadline = MonotonicTime())
{
return genericPublish(message, transfer_type, dst_node_id, NULL, blocking_deadline);
}
int publish(const DataStruct& message, TransferType transfer_type, NodeID dst_node_id, TransferID tid,
MonotonicTime blocking_deadline = MonotonicTime())
{
return genericPublish(message, transfer_type, dst_node_id, &tid, blocking_deadline);
}
static MonotonicDuration getMinTxTimeout() { return MonotonicDuration::fromUSec(200); }
static MonotonicDuration getMaxTxTimeout() { return MonotonicDuration::fromMSec(60000); }
MonotonicDuration getTxTimeout() const { return tx_timeout_; }
void setTxTimeout(MonotonicDuration tx_timeout)
{
tx_timeout = std::max(tx_timeout, getMinTxTimeout());
tx_timeout = std::min(tx_timeout, getMaxTxTimeout());
tx_timeout_ = tx_timeout;
}
Scheduler& getScheduler() const { return scheduler_; }
};
}
@@ -0,0 +1,228 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <uavcan/node/scheduler.hpp>
#include <uavcan/data_type.hpp>
#include <uavcan/node/global_data_type_registry.hpp>
#include <uavcan/util/compile_time.hpp>
#include <uavcan/util/lazy_constructor.hpp>
#include <uavcan/debug.hpp>
#include <uavcan/transport/transfer_listener.hpp>
#include <uavcan/marshal/scalar_codec.hpp>
#include <uavcan/marshal/types.hpp>
namespace uavcan
{
template <typename DataType_>
class ReceivedDataStructure : public DataType_
{
const IncomingTransfer* transfer_;
template <typename Ret, Ret (IncomingTransfer::*Fun)() const>
Ret safeget() const
{
if (!transfer_)
{
assert(0);
return Ret();
}
return (transfer_->*Fun)();
}
protected:
ReceivedDataStructure() : transfer_(NULL) { }
void setTransfer(const IncomingTransfer* transfer)
{
assert(transfer);
transfer_ = transfer;
}
public:
typedef DataType_ DataType;
MonotonicTime getMonotonicTimestamp() const
{
return safeget<MonotonicTime, &IncomingTransfer::getMonotonicTimestamp>();
}
UtcTime getUtcTimestamp() const { return safeget<UtcTime, &IncomingTransfer::getUtcTimestamp>(); }
TransferType getTransferType() const { return safeget<TransferType, &IncomingTransfer::getTransferType>(); }
TransferID getTransferID() const { return safeget<TransferID, &IncomingTransfer::getTransferID>(); }
NodeID getSrcNodeID() const { return safeget<NodeID, &IncomingTransfer::getSrcNodeID>(); }
};
template <typename Stream, typename DataType>
static Stream& operator<<(Stream& s, const ReceivedDataStructure<DataType>& rds)
{
s << "# Received struct ts_m=" << rds.getMonotonicTimestamp()
<< " ts_utc=" << rds.getUtcTimestamp()
<< " snid=" << int(rds.getSrcNodeID().get()) << "\n";
s << static_cast<const DataType&>(rds);
return s;
}
template <typename DataStruct_, unsigned int NumStaticReceivers_, unsigned int NumStaticBufs_>
class TransferListenerInstantiationHelper
{
enum { DataTypeMaxByteLen = BitLenToByteLen<DataStruct_::MaxBitLen>::Result };
enum { NeedsBuffer = int(DataTypeMaxByteLen) > int(MaxSingleFrameTransferPayloadLen) };
enum { BufferSize = NeedsBuffer ? DataTypeMaxByteLen : 0 };
enum { NumStaticBufs = NeedsBuffer ? (NumStaticBufs_ ? NumStaticBufs_ : 1) : 0 };
public:
// TODO: support for zero static bufs
typedef TransferListener<BufferSize, NumStaticBufs, NumStaticReceivers_ ? NumStaticReceivers_ : 1> Type;
};
template <typename DataSpec, typename DataStruct, typename TransferListenerType>
class GenericSubscriber : Noncopyable
{
typedef GenericSubscriber<DataSpec, DataStruct, TransferListenerType> SelfType;
// We need to break the inheritance chain here to implement lazy initialization
class TransferForwarder : public TransferListenerType
{
SelfType& obj_;
void handleIncomingTransfer(IncomingTransfer& transfer)
{
obj_.handleIncomingTransfer(transfer);
}
public:
TransferForwarder(SelfType& obj, const DataTypeDescriptor& data_type, IAllocator& allocator)
: TransferListenerType(data_type, allocator)
, obj_(obj)
{ }
};
struct ReceivedDataStructureSpec : public ReceivedDataStructure<DataStruct>
{
using ReceivedDataStructure<DataStruct>::setTransfer;
};
Scheduler& scheduler_;
IAllocator& allocator_;
LazyConstructor<TransferForwarder> forwarder_;
ReceivedDataStructureSpec message_;
uint32_t failure_count_;
bool checkInit()
{
if (forwarder_)
return true;
GlobalDataTypeRegistry::instance().freeze();
const DataTypeDescriptor* const descr =
GlobalDataTypeRegistry::instance().find(DataTypeKind(DataSpec::DataTypeKind),
DataSpec::getDataTypeFullName());
if (!descr)
{
UAVCAN_TRACE("GenericSubscriber", "Type [%s] is not registered", DataSpec::getDataTypeFullName());
return false;
}
forwarder_.template construct<SelfType&, const DataTypeDescriptor&, IAllocator&>(*this, *descr, allocator_);
return true;
}
bool decodeTransfer(IncomingTransfer& transfer)
{
BitStream bitstream(transfer);
ScalarCodec codec(bitstream);
message_.setTransfer(&transfer);
const int decode_res = DataStruct::decode(message_, codec);
// We don't need the data anymore, the memory can be reused from the callback:
transfer.release();
if (decode_res <= 0)
{
UAVCAN_TRACE("GenericSubscriber", "Unable to decode the message [%i] [%s]",
decode_res, DataSpec::getDataTypeFullName());
failure_count_++;
return false;
}
return true;
}
void handleIncomingTransfer(IncomingTransfer& transfer)
{
if (decodeTransfer(transfer))
{
handleReceivedDataStruct(message_);
}
}
int genericStart(bool(Dispatcher::*registration_method)(TransferListenerBase* listener))
{
stop();
if (!checkInit())
{
UAVCAN_TRACE("GenericSubscriber", "Initialization failure [%s]", DataSpec::getDataTypeFullName());
return -1;
}
if (!(scheduler_.getDispatcher().*registration_method)(forwarder_))
{
UAVCAN_TRACE("GenericSubscriber", "Failed to register transfer listener [%s]",
DataSpec::getDataTypeFullName());
return -1;
}
return 1;
}
protected:
GenericSubscriber(Scheduler& scheduler, IAllocator& allocator)
: scheduler_(scheduler)
, allocator_(allocator)
, failure_count_(0)
{ }
virtual ~GenericSubscriber() { stop(); }
virtual void handleReceivedDataStruct(ReceivedDataStructure<DataStruct>&) = 0;
int startAsMessageListener()
{
return genericStart(&Dispatcher::registerMessageListener);
}
int startAsServiceRequestListener()
{
return genericStart(&Dispatcher::registerServiceRequestListener);
}
int startAsServiceResponseListener()
{
return genericStart(&Dispatcher::registerServiceResponseListener);
}
void stop()
{
if (forwarder_)
{
scheduler_.getDispatcher().unregisterMessageListener(forwarder_);
scheduler_.getDispatcher().unregisterServiceRequestListener(forwarder_);
scheduler_.getDispatcher().unregisterServiceResponseListener(forwarder_);
}
}
uint32_t getFailureCount() const { return failure_count_; }
TransferListenerType* getTransferListener() { return forwarder_; }
ReceivedDataStructure<DataStruct>& getReceivedStructStorage() { return message_; }
public:
Scheduler& getScheduler() const { return scheduler_; }
};
}
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cassert>
#include <bitset>
#include <stdint.h>
#include <algorithm>
#include <uavcan/data_type.hpp>
#include <uavcan/util/compile_time.hpp>
#include <uavcan/fatal_error.hpp>
#include <uavcan/linked_list.hpp>
#if UAVCAN_DEBUG
#include <uavcan/debug.hpp>
#endif
namespace uavcan
{
typedef std::bitset<DataTypeID::Max + 1> DataTypeIDMask;
class GlobalDataTypeRegistry : Noncopyable
{
struct Entry : public LinkedListNode<Entry>
{
DataTypeDescriptor descriptor;
Entry() { }
Entry(DataTypeKind kind, DataTypeID id, const DataTypeSignature& signature, const char* name)
: descriptor(kind, id, signature, name)
{ }
};
struct EntryInsertionComparator
{
const DataTypeID id;
EntryInsertionComparator(Entry* dtd) : id(dtd->descriptor.getID()) { }
bool operator()(const Entry* entry) const
{
assert(entry);
return entry->descriptor.getID() > id;
}
};
public:
enum RegistResult
{
RegistResultOk,
RegistResultCollision,
RegistResultInvalidParams,
RegistResultFrozen
};
private:
typedef LinkedListRoot<Entry> List;
List msgs_;
List srvs_;
bool frozen_;
GlobalDataTypeRegistry() : frozen_(false) { }
const List* selectList(DataTypeKind kind) const;
List* selectList(DataTypeKind kind);
RegistResult remove(Entry* dtd);
RegistResult registImpl(Entry* dtd);
public:
static GlobalDataTypeRegistry& instance();
/// Last call wins
template <typename Type>
RegistResult regist(DataTypeID id)
{
if (isFrozen())
return RegistResultFrozen;
static Entry entry;
{
const RegistResult remove_res = remove(&entry);
if (remove_res != RegistResultOk)
return remove_res;
}
entry = Entry(DataTypeKind(Type::DataTypeKind), id, Type::getDataTypeSignature(), Type::getDataTypeFullName());
{
const RegistResult remove_res = remove(&entry);
if (remove_res != RegistResultOk)
return remove_res;
}
return registImpl(&entry);
}
/// Further calls to regist<>() will fail
void freeze();
bool isFrozen() const { return frozen_; }
const DataTypeDescriptor* find(DataTypeKind kind, const char* name) const;
DataTypeSignature computeAggregateSignature(DataTypeKind kind, DataTypeIDMask& inout_id_mask) const;
void getDataTypeIDMask(DataTypeKind kind, DataTypeIDMask& mask) const;
unsigned int getNumMessageTypes() const { return msgs_.getLength(); }
unsigned int getNumServiceTypes() const { return srvs_.getLength(); }
#if UAVCAN_DEBUG
/// Required for unit testing
void reset()
{
UAVCAN_TRACE("GlobalDataTypeRegistry", "Reset; was frozen: %i, num msgs: %u, num srvs: %u",
int(frozen_), getNumMessageTypes(), getNumServiceTypes());
frozen_ = false;
while (msgs_.get())
msgs_.remove(msgs_.get());
while (srvs_.get())
srvs_.remove(srvs_.get());
}
#endif
};
template <typename Type>
struct DefaultDataTypeRegistrator
{
DefaultDataTypeRegistrator()
{
const GlobalDataTypeRegistry::RegistResult res =
GlobalDataTypeRegistry::instance().regist<Type>(Type::DefaultDataTypeID);
if (res != GlobalDataTypeRegistry::RegistResultOk)
handleFatalError("Type registration failed");
}
};
}
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <uavcan/transport/transfer.hpp>
#include <uavcan/transport/transfer_buffer.hpp>
namespace uavcan
{
class IMarshalBuffer : public ITransferBuffer
{
public:
virtual const uint8_t* getDataPtr() const = 0;
virtual unsigned int getDataLength() const = 0;
};
class IMarshalBufferProvider
{
public:
virtual ~IMarshalBufferProvider() { }
virtual IMarshalBuffer* getBuffer(unsigned int size) = 0;
};
template <unsigned int MaxSize_ = MaxTransferPayloadLen>
class MarshalBufferProvider : public IMarshalBufferProvider
{
class Buffer : public IMarshalBuffer
{
StaticTransferBuffer<MaxSize_> buf_;
int read(unsigned int offset, uint8_t* data, unsigned int len) const
{
return buf_.read(offset, data, len);
}
int write(unsigned int offset, const uint8_t* data, unsigned int len)
{
return buf_.write(offset, data, len);
}
const uint8_t* getDataPtr() const { return buf_.getRawPtr(); }
unsigned int getDataLength() const { return buf_.getMaxWritePos(); }
public:
void reset() { buf_.reset(); }
};
Buffer buffer_;
public:
enum { MaxSize = MaxSize_ };
IMarshalBuffer* getBuffer(unsigned int size)
{
if (size > MaxSize)
return NULL;
buffer_.reset();
return &buffer_;
}
};
}
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <uavcan/node/generic_publisher.hpp>
namespace uavcan
{
template <typename DataType_>
class Publisher : protected GenericPublisher<DataType_, DataType_>
{
typedef GenericPublisher<DataType_, DataType_> BaseType;
public:
typedef DataType_ DataType;
Publisher(Scheduler& scheduler, IMarshalBufferProvider& buffer_provider,
MonotonicDuration tx_timeout = getDefaultTxTimeout(),
MonotonicDuration max_transfer_interval = TransferSender::getDefaultMaxTransferInterval())
: BaseType(scheduler, buffer_provider, tx_timeout, max_transfer_interval)
{
#if UAVCAN_DEBUG
assert(getTxTimeout() == tx_timeout); // Making sure default values are OK
#endif
StaticAssert<DataTypeKind(DataType::DataTypeKind) == DataTypeKindMessage>::check();
}
int broadcast(const DataType& message)
{
return publish(message, TransferTypeMessageBroadcast, NodeID::Broadcast);
}
int unicast(const DataType& message, NodeID dst_node_id)
{
if (!dst_node_id.isUnicast())
{
assert(0);
return -1;
}
return publish(message, TransferTypeMessageUnicast, dst_node_id);
}
static MonotonicDuration getDefaultTxTimeout() { return MonotonicDuration::fromMSec(5); } // 5 ms --> 200 Hz max
using BaseType::getMinTxTimeout;
using BaseType::getMaxTxTimeout;
using BaseType::getTxTimeout;
using BaseType::setTxTimeout;
using BaseType::getScheduler;
};
}
+112
View File
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <uavcan/linked_list.hpp>
#include <uavcan/transport/dispatcher.hpp>
namespace uavcan
{
class Scheduler;
class DeadlineHandler : public LinkedListNode<DeadlineHandler>, Noncopyable
{
MonotonicTime deadline_;
protected:
Scheduler& scheduler_;
explicit DeadlineHandler(Scheduler& scheduler)
: scheduler_(scheduler)
{ }
virtual ~DeadlineHandler() { stop(); }
public:
virtual void handleDeadline(MonotonicTime current_timestamp) = 0;
void startWithDeadline(MonotonicTime deadline);
void startWithDelay(MonotonicDuration delay);
void stop();
bool isRunning() const;
MonotonicTime getDeadline() const { return deadline_; }
Scheduler& getScheduler() const { return scheduler_; }
};
class DeadlineScheduler : Noncopyable
{
LinkedListRoot<DeadlineHandler> handlers_; // Ordered by deadline, lowest first
public:
void add(DeadlineHandler* mdh);
void remove(DeadlineHandler* mdh);
bool doesExist(const DeadlineHandler* mdh) const;
unsigned int getNumHandlers() const { return handlers_.getLength(); }
MonotonicTime pollAndGetMonotonicTimestamp(ISystemClock& sysclock);
MonotonicTime getEarliestDeadline() const;
};
class Scheduler : Noncopyable
{
enum { DefaultDeadlineResolutionMs = 5 };
enum { MinDeadlineResolutionMs = 1 };
enum { MaxDeadlineResolutionMs = 100 };
enum { DefaultCleanupPeriodMs = 1000 };
enum { MinCleanupPeriodMs = 10 };
enum { MaxCleanupPeriodMs = 10000 };
DeadlineScheduler deadline_scheduler_;
Dispatcher dispatcher_;
MonotonicTime prev_cleanup_ts_;
MonotonicDuration deadline_resolution_;
MonotonicDuration cleanup_period_;
MonotonicTime computeDispatcherSpinDeadline(MonotonicTime spin_deadline) const;
void pollCleanup(MonotonicTime mono_ts, uint32_t num_frames_processed_with_last_spin);
public:
Scheduler(ICanDriver& can_driver, IAllocator& allocator, ISystemClock& sysclock, IOutgoingTransferRegistry& otr,
NodeID self_node_id)
: dispatcher_(can_driver, allocator, sysclock, otr, self_node_id)
, prev_cleanup_ts_(sysclock.getMonotonic())
, deadline_resolution_(MonotonicDuration::fromMSec(DefaultDeadlineResolutionMs))
, cleanup_period_(MonotonicDuration::fromMSec(DefaultCleanupPeriodMs))
{ }
int spin(MonotonicTime deadline);
DeadlineScheduler& getDeadlineScheduler() { return deadline_scheduler_; }
Dispatcher& getDispatcher() { return dispatcher_; }
ISystemClock& getSystemClock() { return dispatcher_.getSystemClock(); }
MonotonicTime getMonotonicTimestamp() const { return dispatcher_.getSystemClock().getMonotonic(); }
UtcTime getUtcTimestamp() const { return dispatcher_.getSystemClock().getUtc(); }
MonotonicDuration getDeadlineResolution() const { return deadline_resolution_; }
void setDeadlineResolution(MonotonicDuration res)
{
res = std::min(res, MonotonicDuration::fromMSec(MaxDeadlineResolutionMs));
res = std::max(res, MonotonicDuration::fromMSec(MinDeadlineResolutionMs));
deadline_resolution_ = res;
}
MonotonicDuration getCleanupPeriod() const { return cleanup_period_; }
void setCleanupPeriod(MonotonicDuration period)
{
period = std::min(period, MonotonicDuration::fromMSec(MaxCleanupPeriodMs));
period = std::max(period, MonotonicDuration::fromMSec(MinCleanupPeriodMs));
cleanup_period_ = period;
}
};
}
@@ -0,0 +1,268 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <uavcan/node/generic_publisher.hpp>
#include <uavcan/node/generic_subscriber.hpp>
namespace uavcan
{
template <typename ServiceDataType>
class ServiceResponseTransferListenerInstantiationHelper
{
enum { DataTypeMaxByteLen = BitLenToByteLen<ServiceDataType::Response::MaxBitLen>::Result };
public:
typedef ServiceResponseTransferListener<DataTypeMaxByteLen> Type;
};
template <typename DataType>
struct ServiceCallResult
{
typedef ReceivedDataStructure<typename DataType::Response> ResponseFieldType;
enum Status { Success, ErrorTimeout };
const Status status;
NodeID server_node_id;
ResponseFieldType& response; ///< Either response contents or unspecified response structure
ServiceCallResult(Status status, NodeID server_node_id, ResponseFieldType& response)
: status(status)
, server_node_id(server_node_id)
, response(response)
{
assert(server_node_id.isUnicast());
assert(status == Success || status == ErrorTimeout);
}
bool isSuccessful() const { return status == Success; }
};
template <typename Stream, typename DataType>
static Stream& operator<<(Stream& s, const ServiceCallResult<DataType>& scr)
{
s << "# Service call result [" << DataType::getDataTypeFullName() << "] "
<< (scr.isSuccessful() ? "OK" : "FAILURE")
<< " server_node_id=" << int(scr.server_node_id.get()) << "\n";
if (scr.isSuccessful())
s << scr.response;
else
s << "# (no data)";
return s;
}
template <typename DataType_, typename Callback = void(*)(const ServiceCallResult<DataType_>&)>
class ServiceClient :
public GenericSubscriber<DataType_, typename DataType_::Response,
typename ServiceResponseTransferListenerInstantiationHelper<DataType_>::Type >,
public DeadlineHandler
{
public:
typedef DataType_ DataType;
typedef typename DataType::Request RequestType;
typedef typename DataType::Response ResponseType;
typedef ServiceCallResult<DataType> ServiceCallResultType;
private:
typedef ServiceClient<DataType, Callback> SelfType;
typedef GenericPublisher<DataType, RequestType> PublisherType;
typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType>::Type TransferListenerType;
typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;
PublisherType publisher_;
IAllocator& allocator_;
Callback callback_;
MonotonicDuration request_timeout_;
bool pending_;
bool isCallbackValid() const { return try_implicit_cast<bool>(callback_, true); }
void invokeCallback(ServiceCallResultType& result)
{
if (isCallbackValid())
callback_(result);
else
handleFatalError("Invalid caller callback");
}
void handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
{
assert(response.getTransferType() == TransferTypeServiceResponse);
const TransferListenerType* const listener = SubscriberType::getTransferListener();
if (listener)
{
const typename TransferListenerType::ExpectedResponseParams erp = listener->getExpectedResponseParams();
ServiceCallResultType result(ServiceCallResultType::Success, erp.src_node_id, response);
cancel();
invokeCallback(result);
}
else
{
assert(0);
cancel();
}
}
void handleDeadline(MonotonicTime)
{
const TransferListenerType* const listener = SubscriberType::getTransferListener();
if (listener)
{
const typename TransferListenerType::ExpectedResponseParams erp = listener->getExpectedResponseParams();
ReceivedDataStructure<ResponseType>& ref = SubscriberType::getReceivedStructStorage();
ServiceCallResultType result(ServiceCallResultType::ErrorTimeout, erp.src_node_id, ref);
UAVCAN_TRACE("ServiceClient", "Timeout from nid=%i, dtname=%s",
erp.src_node_id.get(), DataType::getDataTypeFullName());
cancel();
invokeCallback(result);
}
else
{
assert(0);
cancel();
}
}
public:
ServiceClient(Scheduler& scheduler, IAllocator& allocator, IMarshalBufferProvider& buffer_provider,
const Callback& callback = Callback())
: SubscriberType(scheduler, allocator)
, DeadlineHandler(scheduler)
, publisher_(scheduler, buffer_provider, getDefaultRequestTimeout())
, allocator_(allocator)
, callback_(callback)
, request_timeout_(getDefaultRequestTimeout())
, pending_(false)
{
setRequestTimeout(getDefaultRequestTimeout());
#if UAVCAN_DEBUG
assert(getRequestTimeout() == getDefaultRequestTimeout()); // Making sure default values are OK
#endif
}
virtual ~ServiceClient() { cancel(); }
int call(NodeID server_node_id, const RequestType& request) // TODO: Refactor, move into a non-template subclass!
{
/*
* Cancelling the pending request
*/
cancel();
if (!isCallbackValid())
{
UAVCAN_TRACE("ServiceClient", "Invalid callback");
return -1;
}
pending_ = true;
/*
* Determining the Data Type ID
*/
GlobalDataTypeRegistry::instance().freeze();
const DataTypeDescriptor* const descr =
GlobalDataTypeRegistry::instance().find(DataTypeKindService, DataType::getDataTypeFullName());
if (!descr)
{
UAVCAN_TRACE("ServiceClient", "Type [%s] is not registered", DataType::getDataTypeFullName());
cancel();
return -1;
}
/*
* Determining the Transfer ID
*/
const OutgoingTransferRegistryKey otr_key(descr->getID(), TransferTypeServiceRequest, server_node_id);
const MonotonicTime otr_deadline =
SubscriberType::getScheduler().getMonotonicTimestamp() + TransferSender::getDefaultMaxTransferInterval();
TransferID* const otr_tid = SubscriberType::getScheduler().getDispatcher().getOutgoingTransferRegistry()
.accessOrCreate(otr_key, otr_deadline);
if (!otr_tid)
{
UAVCAN_TRACE("ServiceClient", "OTR access failure, dtd=%s", descr->toString().c_str());
cancel();
return -1;
}
const TransferID transfer_id = *otr_tid;
otr_tid->increment();
/*
* Starting the subscriber
*/
const int subscriber_res = SubscriberType::startAsServiceResponseListener();
if (subscriber_res <= 0)
{
UAVCAN_TRACE("ServiceClient", "Failed to start the subscriber, error: %i", subscriber_res);
cancel();
return subscriber_res;
}
/*
* Configuring the listener so it will accept only the matching response
*/
TransferListenerType* const tl = SubscriberType::getTransferListener();
if (!tl)
{
assert(0); // Must have been created
cancel();
return -1;
}
const typename TransferListenerType::ExpectedResponseParams erp(server_node_id, transfer_id);
tl->setExpectedResponseParams(erp);
/*
* Registering the deadline handler
*/
DeadlineHandler::startWithDelay(request_timeout_);
/*
* Publishing the request
*/
const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id, transfer_id);
if (!publisher_res)
{
cancel();
}
return publisher_res;
}
void cancel()
{
pending_ = false;
SubscriberType::stop();
DeadlineHandler::stop();
TransferListenerType* const tl = SubscriberType::getTransferListener();
if (tl)
tl->stopAcceptingAnything();
}
bool isPending() const { return pending_; }
const Callback& getCallback() const { return callback_; }
void setCallback(const Callback& cb) { callback_ = cb; }
uint32_t getResponseFailureCount() const { return SubscriberType::getFailureCount(); }
/*
* Request timeouts
* There is no such config as TX timeout - TX timeouts are configured automagically according to request timeouts
*/
static MonotonicDuration getDefaultRequestTimeout() { return MonotonicDuration::fromMSec(1000); }
static MonotonicDuration getMinRequestTimeout() { return MonotonicDuration::fromMSec(10); }
static MonotonicDuration getMaxRequestTimeout() { return MonotonicDuration::fromMSec(60000); }
MonotonicDuration getRequestTimeout() const { return request_timeout_; }
void setRequestTimeout(MonotonicDuration timeout)
{
timeout = std::max(timeout, getMinRequestTimeout());
timeout = std::min(timeout, getMaxRequestTimeout());
publisher_.setTxTimeout(timeout);
request_timeout_ = std::max(timeout, publisher_.getTxTimeout()); // No less than TX timeout
}
};
}
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <uavcan/node/generic_publisher.hpp>
#include <uavcan/node/generic_subscriber.hpp>
namespace uavcan
{
template <typename DataType_,
typename Callback = void(*)(const ReceivedDataStructure<typename DataType_::Request>&,
typename DataType_::Response&),
unsigned int NumStaticReceivers = 2,
unsigned int NumStaticBufs = 1>
class ServiceServer : public GenericSubscriber<DataType_, typename DataType_::Request,
typename TransferListenerInstantiationHelper<typename DataType_::Request,
NumStaticReceivers,
NumStaticBufs>::Type>
{
public:
typedef DataType_ DataType;
typedef typename DataType::Request RequestType;
typedef typename DataType::Response ResponseType;
private:
typedef typename TransferListenerInstantiationHelper<RequestType, NumStaticReceivers, NumStaticBufs>::Type
TransferListenerType;
typedef GenericSubscriber<DataType, RequestType, TransferListenerType> SubscriberType;
typedef GenericPublisher<DataType, ResponseType> PublisherType;
PublisherType publisher_;
Callback callback_;
uint32_t response_failure_count_;
ResponseType response_;
void handleReceivedDataStruct(ReceivedDataStructure<RequestType>& request)
{
assert(request.getTransferType() == TransferTypeServiceRequest);
if (try_implicit_cast<bool>(callback_, true))
{
response_ = ResponseType(); // The application needs newly initialized structure
callback_(request, response_);
}
else
handleFatalError("Invalid service server callback");
const int res = publisher_.publish(response_, TransferTypeServiceResponse, request.getSrcNodeID(),
request.getTransferID());
if (res <= 0)
{
UAVCAN_TRACE("ServiceServer", "Response publication failure: %i", res);
response_failure_count_++;
}
}
public:
ServiceServer(Scheduler& scheduler, IAllocator& allocator, IMarshalBufferProvider& buffer_provider)
: SubscriberType(scheduler, allocator)
, publisher_(scheduler, buffer_provider, getDefaultTxTimeout())
, callback_()
, response_failure_count_(0)
{
assert(getTxTimeout() == getDefaultTxTimeout()); // Making sure it is valid
StaticAssert<DataTypeKind(DataType::DataTypeKind) == DataTypeKindService>::check();
}
int start(const Callback& callback)
{
stop();
if (!try_implicit_cast<bool>(callback, true))
{
UAVCAN_TRACE("ServiceServer", "Invalid callback");
return -1;
}
callback_ = callback;
return SubscriberType::startAsServiceRequestListener();
}
using SubscriberType::stop;
static MonotonicDuration getDefaultTxTimeout() { return MonotonicDuration::fromMSec(1000); }
static MonotonicDuration getMinTxTimeout() { return PublisherType::getMinTxTimeout(); }
static MonotonicDuration getMaxTxTimeout() { return PublisherType::getMaxTxTimeout(); }
MonotonicDuration getTxTimeout() const { return publisher_.getTxTimeout(); }
void setTxTimeout(MonotonicDuration tx_timeout) { publisher_.setTxTimeout(tx_timeout); }
uint32_t getRequestFailureCount() const { return SubscriberType::getFailureCount(); }
uint32_t getResponseFailureCount() const { return response_failure_count_; }
};
}
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cassert>
#include <uavcan/node/generic_subscriber.hpp>
namespace uavcan
{
template <typename DataType_,
typename Callback = void(*)(const ReceivedDataStructure<DataType_>&),
unsigned int NumStaticReceivers = 2,
unsigned int NumStaticBufs = 1>
class Subscriber : public GenericSubscriber<DataType_, DataType_,
typename TransferListenerInstantiationHelper<DataType_,
NumStaticReceivers,
NumStaticBufs>::Type>
{
typedef typename TransferListenerInstantiationHelper<DataType_, NumStaticReceivers, NumStaticBufs>::Type
TransferListenerType;
typedef GenericSubscriber<DataType_, DataType_, TransferListenerType> BaseType;
Callback callback_;
void handleReceivedDataStruct(ReceivedDataStructure<DataType_>& msg)
{
if (try_implicit_cast<bool>(callback_, true))
callback_(msg);
else
handleFatalError("Invalid subscriber callback");
}
public:
typedef DataType_ DataType;
Subscriber(Scheduler& scheduler, IAllocator& allocator)
: BaseType(scheduler, allocator)
, callback_()
{
StaticAssert<DataTypeKind(DataType::DataTypeKind) == DataTypeKindMessage>::check();
}
int start(const Callback& callback)
{
stop();
if (!try_implicit_cast<bool>(callback, true))
{
UAVCAN_TRACE("Subscriber", "Invalid callback");
return -1;
}
callback_ = callback;
return BaseType::startAsMessageListener();
}
using BaseType::stop;
using BaseType::getFailureCount;
};
}
+85
View File
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <stdint.h>
#include <uavcan/node/scheduler.hpp>
#include <uavcan/util/compile_time.hpp>
#include <uavcan/linked_list.hpp>
#include <uavcan/fatal_error.hpp>
namespace uavcan
{
class Timer;
struct TimerEvent
{
MonotonicTime scheduled_time;
MonotonicTime real_time;
TimerEvent(MonotonicTime scheduled_time, MonotonicTime real_time)
: scheduled_time(scheduled_time)
, real_time(real_time)
{ }
};
class Timer : private DeadlineHandler
{
MonotonicDuration period_;
void handleDeadline(MonotonicTime current_timestamp);
public:
using DeadlineHandler::stop;
using DeadlineHandler::isRunning;
using DeadlineHandler::getDeadline;
using DeadlineHandler::getScheduler;
explicit Timer(Scheduler& scheduler)
: DeadlineHandler(scheduler)
, period_(MonotonicDuration::getInfinite())
{ }
void startOneShotWithDeadline(MonotonicTime deadline);
void startOneShotWithDelay(MonotonicDuration delay);
void startPeriodic(MonotonicDuration period);
MonotonicDuration getPeriod() const { return period_; }
virtual void handleTimerEvent(const TimerEvent& event) = 0;
};
template <typename Callback>
class TimerEventForwarder : public Timer
{
Callback callback_;
public:
explicit TimerEventForwarder(Scheduler& node)
: Timer(node)
, callback_()
{ }
TimerEventForwarder(Scheduler& node, Callback callback)
: Timer(node)
, callback_(callback)
{ }
const Callback& getCallback() const { return callback_; }
void setCallback(const Callback& callback) { callback_ = callback; }
void handleTimerEvent(const TimerEvent& event)
{
if (try_implicit_cast<bool>(callback_, true))
callback_(event);
else
handleFatalError("Invalid timer callback");
}
};
}