mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-14 06:13:33 +08:00
Size optimized ServiceClient<>
This commit is contained in:
@@ -69,6 +69,33 @@ static Stream& operator<<(Stream& s, const ServiceCallResult<DataType>& scr)
|
||||
}
|
||||
|
||||
|
||||
class ServiceClientBase : protected DeadlineHandler
|
||||
{
|
||||
protected:
|
||||
MonotonicDuration request_timeout_;
|
||||
bool pending_;
|
||||
|
||||
ServiceClientBase(INode& node)
|
||||
: DeadlineHandler(node.getScheduler())
|
||||
, request_timeout_(getDefaultRequestTimeout())
|
||||
, pending_(false)
|
||||
{ }
|
||||
|
||||
virtual ~ServiceClientBase() { }
|
||||
|
||||
int prepareToCall(INode& node, const char* dtname, NodeID server_node_id, TransferID& out_transfer_id);
|
||||
|
||||
public:
|
||||
bool isPending() const { return pending_; }
|
||||
|
||||
static MonotonicDuration getDefaultRequestTimeout() { return MonotonicDuration::fromMSec(1000); }
|
||||
static MonotonicDuration getMinRequestTimeout() { return MonotonicDuration::fromMSec(10); }
|
||||
static MonotonicDuration getMaxRequestTimeout() { return MonotonicDuration::fromMSec(60000); }
|
||||
|
||||
using DeadlineHandler::getDeadline;
|
||||
};
|
||||
|
||||
|
||||
template <typename DataType_,
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>
|
||||
@@ -79,7 +106,7 @@ template <typename DataType_,
|
||||
class UAVCAN_EXPORT ServiceClient
|
||||
: public GenericSubscriber<DataType_, typename DataType_::Response,
|
||||
typename ServiceResponseTransferListenerInstantiationHelper<DataType_>::Type >
|
||||
, protected DeadlineHandler
|
||||
, public ServiceClientBase
|
||||
{
|
||||
public:
|
||||
typedef DataType_ DataType;
|
||||
@@ -96,70 +123,21 @@ private:
|
||||
|
||||
PublisherType publisher_;
|
||||
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 invokeCallback(ServiceCallResultType& result);
|
||||
|
||||
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 handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
void handleDeadline(MonotonicTime);
|
||||
|
||||
public:
|
||||
explicit ServiceClient(INode& node, const Callback& callback = Callback())
|
||||
: SubscriberType(node)
|
||||
, DeadlineHandler(node.getScheduler())
|
||||
, ServiceClientBase(node)
|
||||
, publisher_(node, getDefaultRequestTimeout())
|
||||
, callback_(callback)
|
||||
, request_timeout_(getDefaultRequestTimeout())
|
||||
, pending_(false)
|
||||
{
|
||||
setRequestTimeout(getDefaultRequestTimeout());
|
||||
#if UAVCAN_DEBUG
|
||||
@@ -174,102 +152,9 @@ public:
|
||||
return publisher_.init();
|
||||
}
|
||||
|
||||
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 -ErrInvalidParam;
|
||||
}
|
||||
pending_ = true;
|
||||
int call(NodeID server_node_id, const RequestType& request);
|
||||
|
||||
/*
|
||||
* 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 -ErrUnknownDataType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determining the Transfer ID
|
||||
*/
|
||||
const OutgoingTransferRegistryKey otr_key(descr->getID(), TransferTypeServiceRequest, server_node_id);
|
||||
const MonotonicTime otr_deadline =
|
||||
SubscriberType::getNode().getMonotonicTime() + TransferSender::getDefaultMaxTransferInterval();
|
||||
TransferID* const otr_tid = SubscriberType::getNode().getDispatcher().getOutgoingTransferRegistry()
|
||||
.accessOrCreate(otr_key, otr_deadline);
|
||||
if (!otr_tid)
|
||||
{
|
||||
UAVCAN_TRACE("ServiceClient", "OTR access failure, dtd=%s", descr->toString().c_str());
|
||||
cancel();
|
||||
return -ErrMemory;
|
||||
}
|
||||
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 -ErrLogic;
|
||||
}
|
||||
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_; }
|
||||
void cancel();
|
||||
|
||||
const Callback& getCallback() const { return callback_; }
|
||||
void setCallback(const Callback& cb) { callback_ = cb; }
|
||||
@@ -280,10 +165,6 @@ public:
|
||||
* 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)
|
||||
{
|
||||
@@ -293,8 +174,133 @@ public:
|
||||
publisher_.setTxTimeout(timeout);
|
||||
request_timeout_ = std::max(timeout, publisher_.getTxTimeout()); // No less than TX timeout
|
||||
}
|
||||
|
||||
using DeadlineHandler::getDeadline;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::invokeCallback(ServiceCallResultType& result)
|
||||
{
|
||||
if (isCallbackValid())
|
||||
{
|
||||
callback_(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
handleFatalError("Invalid caller callback");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::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();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::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();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_>
|
||||
int ServiceClient<DataType_, Callback_>::call(NodeID server_node_id, const RequestType& request)
|
||||
{
|
||||
cancel();
|
||||
if (!isCallbackValid())
|
||||
{
|
||||
UAVCAN_TRACE("ServiceClient", "Invalid callback");
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common procedures that don't depend on the struct data type
|
||||
*/
|
||||
TransferID transfer_id;
|
||||
const int prep_res =
|
||||
prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, transfer_id);
|
||||
if (prep_res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("ServiceClient", "Failed to prepare the call, error: %i", prep_res);
|
||||
cancel();
|
||||
return prep_res;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 -ErrLogic;
|
||||
}
|
||||
const typename TransferListenerType::ExpectedResponseParams erp(server_node_id, transfer_id);
|
||||
tl->setExpectedResponseParams(erp);
|
||||
|
||||
/*
|
||||
* Publishing the request
|
||||
*/
|
||||
const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id, transfer_id);
|
||||
if (!publisher_res)
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
return publisher_res;
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::cancel()
|
||||
{
|
||||
pending_ = false;
|
||||
SubscriberType::stop();
|
||||
DeadlineHandler::stop();
|
||||
TransferListenerType* const tl = SubscriberType::getTransferListener();
|
||||
if (tl)
|
||||
{
|
||||
tl->stopAcceptingAnything();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <uavcan/node/service_client.hpp>
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
|
||||
int ServiceClientBase::prepareToCall(INode& node, const char* dtname, NodeID server_node_id,
|
||||
TransferID& out_transfer_id)
|
||||
{
|
||||
pending_ = true;
|
||||
|
||||
/*
|
||||
* Determining the Data Type ID
|
||||
*/
|
||||
GlobalDataTypeRegistry::instance().freeze();
|
||||
const DataTypeDescriptor* const descr = GlobalDataTypeRegistry::instance().find(DataTypeKindService, dtname);
|
||||
if (!descr)
|
||||
{
|
||||
UAVCAN_TRACE("ServiceClient", "Type [%s] is not registered", dtname);
|
||||
return -ErrUnknownDataType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determining the Transfer ID
|
||||
*/
|
||||
const OutgoingTransferRegistryKey otr_key(descr->getID(), TransferTypeServiceRequest, server_node_id);
|
||||
const MonotonicTime otr_deadline = node.getMonotonicTime() + TransferSender::getDefaultMaxTransferInterval();
|
||||
TransferID* const otr_tid =
|
||||
node.getDispatcher().getOutgoingTransferRegistry().accessOrCreate(otr_key, otr_deadline);
|
||||
if (!otr_tid)
|
||||
{
|
||||
UAVCAN_TRACE("ServiceClient", "OTR access failure, dtd=%s", descr->toString().c_str());
|
||||
return -ErrMemory;
|
||||
}
|
||||
out_transfer_id = *otr_tid;
|
||||
otr_tid->increment();
|
||||
|
||||
/*
|
||||
* Registering the deadline handler
|
||||
*/
|
||||
DeadlineHandler::startWithDelay(request_timeout_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user