Adds immediate migration results
This commit is contained in:
2395 files changed
+357322
-2524
No files matched your search
@@ -0,0 +1,703 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_INPUT_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_INPUT_HPP_
|
||||
|
||||
#include <dds/core/Value.hpp>
|
||||
#include <dds/core/SafeEnumeration.hpp>
|
||||
#include <rti/core/NativeValueType.hpp>
|
||||
#include <rti/core/Entity.hpp>
|
||||
#include <dds/sub/DataReader.hpp>
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
|
||||
#include "routingservice/routingservice_processor.h"
|
||||
#include "routingservice/routingservice_adapter_new.h"
|
||||
#include <rti/routing/StreamInfo.hpp>
|
||||
#include <rti/routing/adapter/StreamReader.hpp>
|
||||
#include <rti/routing/processor/LoanedSamples.hpp>
|
||||
#include <rti/routing/processor/Query.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
class Route;
|
||||
|
||||
template <typename Data, typename Info> class TypedInput;
|
||||
|
||||
template <typename Data, typename Info = dds::sub::SampleInfo>
|
||||
class TypedInput;
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::Input
|
||||
*
|
||||
* @brief Generic Representation of a Route's input.
|
||||
*
|
||||
*
|
||||
* This class provides a base representation of the inputs contained in a Route.
|
||||
* Since it is generic, this object does not allow you to perform any operation
|
||||
* that requires dealing with the data provided by the underlying
|
||||
* rti::routing::adapter::StreamReader.
|
||||
*/
|
||||
class Input {
|
||||
public:
|
||||
|
||||
/*i
|
||||
*/
|
||||
Input(RTI_RoutingServiceInput *native,
|
||||
int32_t index,
|
||||
RTI_RoutingServiceRoute *native_route)
|
||||
: native_(native),
|
||||
index_(index),
|
||||
native_route_(native_route),
|
||||
stream_info_("", ""),
|
||||
name_(RTI_RoutingServiceInput_get_name(native))
|
||||
{
|
||||
const RTI_RoutingServiceStreamInfo *native_stream_info =
|
||||
RTI_RoutingServiceInput_get_stream_info(native);
|
||||
if (native_stream_info == nullptr) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
"invalid argument: invalid StreamInfo for input");
|
||||
}
|
||||
stream_info_ = *native_stream_info;
|
||||
}
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
const rti::routing::StreamInfo& stream_info() const
|
||||
{
|
||||
return stream_info_;
|
||||
}
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
const std::string& name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
int32_t index()
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a typed version of this Input.
|
||||
*
|
||||
* Note: This operation is not type-safe. It is the caller's responsibility
|
||||
* to use the appropriate type.
|
||||
*
|
||||
*
|
||||
* @see TypedInput
|
||||
* @see <tt>TypedInput<DataRep, Info> Route<DataRep, InfoRep>::input(int32_t index)</tt>
|
||||
*/
|
||||
template <typename Data, typename Info>
|
||||
TypedInput<Data, Info> get()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a typed version of this Input.
|
||||
*
|
||||
* Note: This operation is not type-safe. It is the caller's responsibility
|
||||
* to use the appropriate type.
|
||||
*
|
||||
* @see TypedInput
|
||||
* @see TypedInput<tt><DataRep, dds::sub::SampleInfo></tt> Route<DataRep>::input(int32_t index)
|
||||
*/
|
||||
template <typename Data>
|
||||
TypedInput<Data, dds::sub::SampleInfo> get()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
RTI_RoutingServiceInput* native()
|
||||
{
|
||||
return native_;
|
||||
}
|
||||
|
||||
// Implementation details
|
||||
private:
|
||||
|
||||
template <typename Data, typename Info> friend class TypedInput;
|
||||
friend class Route;
|
||||
friend class std::allocator<Input>;
|
||||
RTI_RoutingServiceInput *native_;
|
||||
int32_t index_;
|
||||
RTI_RoutingServiceRoute *native_route_;
|
||||
rti::routing::StreamInfo stream_info_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
template <typename Data, typename Info = dds::sub::SampleInfo>
|
||||
class Selector;
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::TypedInput
|
||||
*
|
||||
* @brief Representation of an Input whose data representation is \p DataRep,
|
||||
* whose info representation is \p InfoRep.
|
||||
*
|
||||
*/
|
||||
template <typename Data, typename Info>
|
||||
class TypedInput{
|
||||
public:
|
||||
|
||||
typedef rti::routing::processor::Selector<Data, Info> Selector;
|
||||
|
||||
TypedInput(Input *input);
|
||||
|
||||
/**
|
||||
* @brief Returns the StreamInfo associated with this object.
|
||||
*
|
||||
* The associated StreamInfo represents an equivalent object to the one
|
||||
* used to create the underlying rti::routing::adapter::StreamReader.
|
||||
*/
|
||||
const rti::routing::StreamInfo& stream_info() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the name of this output.
|
||||
*
|
||||
* The name is given by the name of the corresponding XML configuration tag.
|
||||
*/
|
||||
const std::string& name() const;
|
||||
|
||||
/**
|
||||
* @brief Returns all the available samples in this object.
|
||||
*
|
||||
* This operation will call rti::routing::adapter::StreamReader::take on
|
||||
* the underlying rti::routing::adapter::StreamReader.
|
||||
*
|
||||
* The returned samples are interpreted to contain data represented
|
||||
* as \c Data and info represented as \c Info.
|
||||
*
|
||||
* @see LoanedSamples
|
||||
* @see rti::routing::adapter::StreamReader::take
|
||||
*/
|
||||
LoanedSamples<Data, Info> take();
|
||||
|
||||
/**
|
||||
* @brief Same as take() but this calls
|
||||
* rti::routing::adapter::StreamReader::read instead.
|
||||
*
|
||||
* @see LoanedSamples
|
||||
* @see rti::routing::adapter::StreamReader::read
|
||||
* @see take
|
||||
*/
|
||||
LoanedSamples<Data, Info> read();
|
||||
|
||||
/**
|
||||
* @brief Gets a Selector to perform complex data selections, such as
|
||||
* per-instance selection, content and status filtering.
|
||||
*
|
||||
* The selector can be used as follows:
|
||||
* \code{.c++}
|
||||
* LoanedSamples<DynamicData> samples = input.select()
|
||||
* .instance(handle)
|
||||
* .content(query)
|
||||
* .state(state)
|
||||
* .take();
|
||||
* \endcode
|
||||
*
|
||||
* This shows how samples can be taken by selecting a specific instance,
|
||||
* then filtering by state and content.
|
||||
*
|
||||
* Note that when the application wants to access all available samples,
|
||||
* it can simply call read() or take().
|
||||
*
|
||||
* @return A Selector, typically used in-line to configure it and finally
|
||||
* call Selector::read() or Selector::take().
|
||||
*
|
||||
* @see Selector for the different parameters that can be set to
|
||||
* configure which samples to read or take. Selecting what samples to
|
||||
* read.
|
||||
*/
|
||||
Selector select();
|
||||
|
||||
rti::routing::processor::Query query(
|
||||
const dds::topic::Filter& filter);
|
||||
|
||||
/**
|
||||
* @brief Indicates whether this input has received new data since the last
|
||||
* time any variant of the read operations was called.
|
||||
*
|
||||
* @note Given the time decoupling between the notification of new data and
|
||||
* when it the data is read, it is possible this function may return
|
||||
* true and yet there is no data to be returned.
|
||||
*
|
||||
* @return True if this input has new data received since the last read
|
||||
* access.
|
||||
*/
|
||||
bool active();
|
||||
|
||||
/**
|
||||
* @brief Creates a new data sample from this input.
|
||||
*
|
||||
* The data constructor called in this operation depends on the data
|
||||
* representation kind of the TypeInfo associated with this input.
|
||||
* If the type representation is a dynamic type, then it will assume
|
||||
* the following constructor:
|
||||
* \code Data(const dds::core::xtypes::DynamicType&) \endcode
|
||||
*
|
||||
* and will use the type code object downcasted from the value
|
||||
* returned by TypeInfo::type_representation.
|
||||
*
|
||||
* Otherwise:
|
||||
* \code Data() \endcode
|
||||
*
|
||||
* @return A new data sample
|
||||
*/
|
||||
Data create_data();
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
TypedInput<Data, Info>* operator->()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the underlying DDS DataReader that is part of this
|
||||
* StreamReader implementation, assuming this input holds a DDS StreamReader.
|
||||
*
|
||||
* This operation will throw an exception if this input does not hold a
|
||||
* DDS StreamReader.
|
||||
*
|
||||
* @note The access to the underlying DataReader is allowed only as long as
|
||||
* this Input remains enabled. The object representing the DataReader
|
||||
* must be invalidated before the input is disabled, otherwise the behavior
|
||||
* is undefined.
|
||||
*
|
||||
* @throw std::exception
|
||||
*
|
||||
* @return dds::sub::DataReader<Data>
|
||||
*/
|
||||
dds::sub::DataReader<Data> dds_data_reader()
|
||||
{
|
||||
using rti::core::detail::create_from_native_entity;
|
||||
|
||||
DDS_DataReader *native_reader =
|
||||
RTI_RoutingServiceInput_get_dds_reader(input_->native_);
|
||||
if (native_reader == NULL) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
"invalid argument: input does not hold a DDS StreamReader");
|
||||
}
|
||||
|
||||
typedef dds::sub::DataReader<Data> data_reader_type;
|
||||
return rti::core::detail::create_from_native_entity<data_reader_type>(
|
||||
native_reader);
|
||||
}
|
||||
|
||||
private:
|
||||
TypedInput();
|
||||
|
||||
friend class rti::routing::processor::Selector<Data, Info>;
|
||||
|
||||
LoanedSamples<Data, Info> take(
|
||||
const rti::routing::adapter::SelectorState& selector_state);
|
||||
|
||||
LoanedSamples<Data, Info> read(
|
||||
const rti::routing::adapter::SelectorState& selector_state);
|
||||
|
||||
friend class rti::routing::processor::Route;
|
||||
Input *input_;
|
||||
};
|
||||
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::Selector
|
||||
*
|
||||
* @brief An element that allows reading data that meet a set of specified
|
||||
* attributes.
|
||||
*
|
||||
* The Selector class is used by the TypedInput to compose read and take
|
||||
* operations.
|
||||
*
|
||||
* A Selector has an associated TypedInput and configures the behavior of the
|
||||
* read or take operation performed by that TypedInput.
|
||||
*
|
||||
* For example, to perform a read of at most 5 unread samples:
|
||||
* \code
|
||||
* LoanedSamples<Foo> samples = reader.select()
|
||||
* .read()
|
||||
* .max_samples(5)
|
||||
* .state(dds::sub::status::DataState::new_data());
|
||||
* \endcode
|
||||
*
|
||||
* @note It's very important to consider that the behavior of this class is
|
||||
* tightly coupled and dependent on the underlying
|
||||
* rti::routing::adapter::StreamReader of the attached TypedInput. In
|
||||
* particular, the samples returned by a Selector depend on the behavior
|
||||
* of the rti::routing::adapter::StreamReader::take(SelectorState) and
|
||||
* rti::routing::adapter::StreamReader::read(SelectorState). If the
|
||||
* implementation of these operations behave according to the requirements,
|
||||
* then the behavior offered by this class will be consistent.
|
||||
*
|
||||
* @see rti::routing::adapter::SelectorState
|
||||
* @see dds::sub::DataReader::Selector for equivalent semantics and behavior
|
||||
* with DataReaders
|
||||
*/
|
||||
template <typename Data, typename Info>
|
||||
class Selector {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Create a Selector for a TypedInput.
|
||||
*
|
||||
* Selectors are created with the default filter state of the TypedInput.
|
||||
*
|
||||
* @param[in] input
|
||||
* The TypedInput that this Selector is attached to.
|
||||
*/
|
||||
Selector(const rti::routing::processor::TypedInput<Data, Info> input)
|
||||
: typed_input_(input), query_(dds::core::null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
Selector(const Selector& other)
|
||||
: typed_input_(other.typed_input_),
|
||||
state_(other.state_),
|
||||
query_(other.query_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select a specific dds::sub::status::DataState.
|
||||
*
|
||||
* By setting the DataState, you can specify the state of the samples that
|
||||
* should be read or taken. The DataState of a sample encapsulates the
|
||||
* SampleState, ViewState, and InstanceState of a sample.
|
||||
*
|
||||
* @param[in] the_state
|
||||
* The selected DataState
|
||||
*
|
||||
*/
|
||||
Selector& state(
|
||||
const dds::sub::status::DataState& the_state)
|
||||
{
|
||||
state_.state(the_state);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Choose to only read/take up to a maximum number of samples.
|
||||
*
|
||||
* @param[in] count
|
||||
* The maximum number of samples to read/take.
|
||||
*/
|
||||
Selector& max_samples(
|
||||
const int32_t count)
|
||||
{
|
||||
state_.max_samples(count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select a specific instance to read/take.
|
||||
*
|
||||
* This operation causes the subsequent read or take operation to access
|
||||
* only samples belonging the single specified instance whose handle is
|
||||
* \p the_handle.
|
||||
*
|
||||
* Upon successful completion, the data collection will contain samples all
|
||||
* belonging to the same instance.
|
||||
*
|
||||
* The subsequent read/take may operation may fail if the InstanceHandle
|
||||
* does not correspond to an existing data-object known to the TypedInput.
|
||||
*
|
||||
* @param[in] the_handle
|
||||
* The handle of the instance to select
|
||||
*
|
||||
* @see rti::routing::adapter::SelectorState
|
||||
* @see dds::sub::DataReader::Selector::instance
|
||||
*/
|
||||
Selector& instance(
|
||||
const dds::core::InstanceHandle& the_handle)
|
||||
{
|
||||
state_.instance(the_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select the instance after a specific instance.
|
||||
*
|
||||
* This operation causes the subsequent read or take operation to
|
||||
* access only samples belonging a single instance whose handle is
|
||||
* considered 'next' after the provided InstanceHandle, \p the_handle.
|
||||
*
|
||||
* The accessed samples will all belong to the 'next' instance with
|
||||
* InstanceHandle 'greater' than the specified previous handle that has
|
||||
* available samples.
|
||||
*
|
||||
* The special value dds::core::InstanceHandle::nil() is guaranteed to be
|
||||
* 'less than' any valid instance_handle. So the use of the parameter value
|
||||
* previous_handle == dds::core::InstanceHandle::nil() will return the
|
||||
* samples for the instance that has the smallest instance_handle among all
|
||||
* the instances that contain available samples.
|
||||
*
|
||||
* Note that it is possible to call the
|
||||
* dds::sub::next_instance(const dds::core::InstanceHandle& h) operation
|
||||
* with a previous_handle that does not correspond to an instance currently
|
||||
* managed by the underlying rti::routing::adapter::StreamReader.
|
||||
*
|
||||
* @param[in] the_handle
|
||||
* The reference instance. The instance after this one will be
|
||||
* selected.
|
||||
*
|
||||
* @see rti::routing::adapter::SelectorState
|
||||
* @see dds::sub::DataReader::Selector::next_instance
|
||||
*/
|
||||
Selector& next_instance(
|
||||
const dds::core::InstanceHandle& the_handle)
|
||||
{
|
||||
state_.next_instance(the_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select samples based on a content filter parameters.
|
||||
*
|
||||
* The effect of using this manipulator is that the subsequent read/take
|
||||
* will filter the samples based on the dds::topic::Filter. If the
|
||||
* Input has no samples that meet the constraints, the read/take will not
|
||||
* return any data.
|
||||
*
|
||||
* This selection is applied in combination with the other settings of
|
||||
* this Selector.
|
||||
*
|
||||
* @param[in] the_filter
|
||||
*/
|
||||
Selector& filter(
|
||||
const dds::topic::Filter& the_filter)
|
||||
{
|
||||
state_.filter(the_filter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Select samples based on a rti::routing::processor::Query.
|
||||
*
|
||||
* When passing a Query, the effect of calling this method is that the
|
||||
* underlying rti::routing::adapter::StreamReader shall filter the samples
|
||||
* based only on the Query's settings.
|
||||
*
|
||||
* Using this manipulator will always override the selection specified
|
||||
* with Selector::filter, no matter the order in which operations are
|
||||
* called. To reset this selection, you can call this operation passing
|
||||
* dds::core::null.
|
||||
*
|
||||
* @param[in] the_query
|
||||
* The Query to read/take with.
|
||||
*/
|
||||
Selector& query(const rti::routing::processor::Query& the_query)
|
||||
{
|
||||
query_ = the_query;
|
||||
state_.content(the_query.delegate().get()->query_data_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Take samples based on the state associated with this Selector.
|
||||
*
|
||||
* @note This operation will call rti::routing::adapter::StreamReader::take(SelectorState)
|
||||
* on the underlying rti::routing::adapter::StreamReader.
|
||||
*
|
||||
* @throw std::exception
|
||||
*
|
||||
* @return rti::routing::processor::LoanedSamples
|
||||
*/
|
||||
LoanedSamples<Data, Info> take()
|
||||
{
|
||||
return typed_input_.take(state_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read samples based on the state associated with this Selector.
|
||||
*
|
||||
* @note This operation will call rti::routing::adapter::StreamReader::read(SelectorState)
|
||||
* on the underlying rti::routing::adapter::StreamReader.
|
||||
*
|
||||
* @throw std::exception
|
||||
*
|
||||
* @return rti::routing::processor::LoanedSamples
|
||||
*/
|
||||
LoanedSamples<Data, Info> read()
|
||||
{
|
||||
return typed_input_.read(state_);
|
||||
}
|
||||
|
||||
private:
|
||||
rti::routing::processor::TypedInput<Data, Info> typed_input_;
|
||||
rti::routing::adapter::SelectorState state_;
|
||||
rti::routing::processor::Query query_;
|
||||
};
|
||||
|
||||
|
||||
template <typename Data, typename Info>
|
||||
struct create_data_from_input {
|
||||
|
||||
static Data get(TypedInput<Data, Info>& )
|
||||
{
|
||||
return Data();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Info>
|
||||
struct create_data_from_input<dds::core::xtypes::DynamicData, Info> {
|
||||
|
||||
static dds::core::xtypes::DynamicData get(
|
||||
TypedInput<dds::core::xtypes::DynamicData, Info>& input)
|
||||
{
|
||||
if (input->stream_info().type_info().type_representation_kind()
|
||||
!= TypeRepresentationKind::DYNAMIC_TYPE) {
|
||||
throw dds::core::PreconditionNotMetError(
|
||||
"inconsistent data representation kind");
|
||||
}
|
||||
dds::core::xtypes::DynamicType *type_code =
|
||||
static_cast<dds::core::xtypes::DynamicType *> (
|
||||
input->stream_info().type_info().type_representation());
|
||||
return dds::core::xtypes::DynamicData(*type_code);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Data, typename Info>
|
||||
TypedInput<Data, Info>::TypedInput(Input *input)
|
||||
: input_(input)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
const rti::routing::StreamInfo& TypedInput<Data, Info>::stream_info() const
|
||||
{
|
||||
return input_->stream_info_;
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
const std::string& TypedInput<Data, Info>::name() const
|
||||
{
|
||||
return input_->name_;
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
LoanedSamples<Data, Info> TypedInput<Data, Info>::take()
|
||||
{
|
||||
RTI_RoutingServiceLoanedSamples native_samples =
|
||||
RTI_RoutingServiceLoanedSamples_INITIALIZER;
|
||||
if (!RTI_RoutingServiceInput_take(
|
||||
input_->native_,
|
||||
&native_samples)) {
|
||||
throw dds::core::Error("error taking samples from native input");
|
||||
}
|
||||
return LoanedSamples<Data, Info>(input_->native_, native_samples);
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
LoanedSamples<Data, Info> TypedInput<Data, Info>::read()
|
||||
{
|
||||
RTI_RoutingServiceLoanedSamples native_samples =
|
||||
RTI_RoutingServiceLoanedSamples_INITIALIZER;
|
||||
if (!RTI_RoutingServiceInput_read(
|
||||
input_->native_,
|
||||
&native_samples)) {
|
||||
throw dds::core::Error("error reading samples from native input");
|
||||
}
|
||||
return LoanedSamples<Data, Info>(input_->native_, native_samples);
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
rti::routing::processor::Selector<Data, Info>
|
||||
TypedInput<Data, Info>::select()
|
||||
{
|
||||
return rti::routing::processor::Selector<Data, Info>(*this);
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
rti::routing::processor::Query TypedInput<Data, Info>::query(
|
||||
const dds::topic::Filter& filter)
|
||||
{
|
||||
return rti::routing::processor::Query(input_->native_, filter);
|
||||
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
LoanedSamples<Data, Info> TypedInput<Data, Info>::take(
|
||||
const rti::routing::adapter::SelectorState& selector_state)
|
||||
{
|
||||
RTI_RoutingServiceLoanedSamples native_samples =
|
||||
RTI_RoutingServiceLoanedSamples_INITIALIZER;
|
||||
if (!RTI_RoutingServiceInput_take_w_selector(
|
||||
input_->native_,
|
||||
&native_samples,
|
||||
&selector_state.native())) {
|
||||
throw dds::core::Error("error taking samples with selector from native input");
|
||||
}
|
||||
return LoanedSamples<Data, Info>(input_->native_, native_samples);
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
LoanedSamples<Data, Info> TypedInput<Data, Info>::read(
|
||||
const rti::routing::adapter::SelectorState& selector_state)
|
||||
{
|
||||
RTI_RoutingServiceLoanedSamples native_samples =
|
||||
RTI_RoutingServiceLoanedSamples_INITIALIZER;
|
||||
if (!RTI_RoutingServiceInput_read_w_selector(
|
||||
input_->native_,
|
||||
&native_samples,
|
||||
&selector_state.native())) {
|
||||
throw dds::core::Error("error reading samples with selector from native input");
|
||||
}
|
||||
return LoanedSamples<Data, Info>(input_->native_, native_samples);
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
bool TypedInput<Data, Info>::active()
|
||||
{
|
||||
return RTI_RoutingServiceInput_is_active(input_->native_) ? true : false;
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
Data TypedInput<Data,Info>::create_data()
|
||||
{
|
||||
return create_data_from_input<Data, Info>::get(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convenient definition of a TypedInput whose data representation
|
||||
* is DynamicData and info representation is dds::sub::SampleInfo.
|
||||
*
|
||||
* These data and info representations are expected be used by most of the
|
||||
* rti::routing::adapter::StreamReader implementations.
|
||||
*/
|
||||
typedef TypedInput<dds::core::xtypes::DynamicData> DynamicDataInput;
|
||||
typedef dds::sub::DataReader<dds::core::xtypes::DynamicData> DynamicDataReader;
|
||||
|
||||
} } }
|
||||
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_INPUT_HPP_
|
||||
@@ -0,0 +1,130 @@
|
||||
/* $Id$
|
||||
|
||||
(c) Copyright, Real-Time Innovations, 2015-2016.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
|
||||
|
||||
============================================================================= */
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_LOANED_SAMPLE_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_LOANED_SAMPLE_HPP_
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::LoanedSample
|
||||
*
|
||||
* @brief
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
class LoanedSample
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The data type
|
||||
*/
|
||||
typedef T DataType;
|
||||
/**
|
||||
* @brief The info type
|
||||
*/
|
||||
typedef U InfoType;
|
||||
|
||||
LoanedSample()
|
||||
: _data_ptr(NULL),
|
||||
_info_ptr(NULL)
|
||||
{}
|
||||
|
||||
LoanedSample(const DataType * the_data, const InfoType * the_info)
|
||||
: _data_ptr(the_data),
|
||||
_info_ptr(the_info)
|
||||
{}
|
||||
|
||||
|
||||
LoanedSample(const void * the_data, const void * the_info)
|
||||
: _data_ptr(static_cast<const T*>(the_data)),
|
||||
_info_ptr(static_cast<const U*>(the_info))
|
||||
{}
|
||||
|
||||
|
||||
const DataType & data() const
|
||||
{
|
||||
if (_info_ptr != NULL && !info().valid()) {
|
||||
throw dds::core::PreconditionNotMetError("Invalid data");
|
||||
}
|
||||
|
||||
return *_data_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the sample info
|
||||
*/
|
||||
const InfoType & info() const {
|
||||
if (_info_ptr == NULL) {
|
||||
throw dds::core::PreconditionNotMetError(
|
||||
"Sample Info not available");
|
||||
}
|
||||
|
||||
return *_info_ptr;
|
||||
}
|
||||
|
||||
|
||||
operator const DataType & () const
|
||||
{
|
||||
return data();
|
||||
}
|
||||
|
||||
LoanedSample<DataType, InfoType> * operator ->() {
|
||||
return this;
|
||||
}
|
||||
|
||||
const LoanedSample<DataType, InfoType> * operator ->() const {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares the data and info
|
||||
*/
|
||||
bool operator==(const LoanedSample& other) const
|
||||
{
|
||||
if (info() != other.info()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info().valid()) {
|
||||
if (data() != other.data()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const LoanedSample& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void swap(LoanedSample & other) throw()
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(_data_ptr, other._data_ptr);
|
||||
swap(_info_ptr, other._info_ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
const DataType * _data_ptr;
|
||||
const InfoType * _info_ptr;
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_LOANED_SAMPLE_HPP_
|
||||
@@ -0,0 +1,306 @@
|
||||
/* $Id$
|
||||
|
||||
(c) Copyright, Real-Time Innovations, 2013-2016.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
|
||||
|
||||
============================================================================= */
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_LOANED_SAMPLES_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_LOANED_SAMPLES_HPP_
|
||||
|
||||
#include <utility> // std::move
|
||||
#include <iterator>
|
||||
|
||||
#include "routingservice/routingservice_adapter_new.h"
|
||||
#include <dds/core/types.hpp>
|
||||
#include <rti/routing/processor/LoanedSample.hpp>
|
||||
#include <rti/routing/adapter/StreamReader.hpp>
|
||||
#include <rti/routing/processor/SampleIterator.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
template <typename T, typename U> class TypedInput;
|
||||
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::LoanedSamples
|
||||
*
|
||||
* @brief Provides temporary access to a collection of samples (data and info)
|
||||
* from a TypedInput.
|
||||
*
|
||||
* @tparam T The sample data type representation. It has to match the type of
|
||||
* the underlying rti::routing::adapter::StreamReader of the TypedInput.
|
||||
*
|
||||
* @tparam U The sample info type representation. It has to match the type of
|
||||
* the underlying rti::routing::adapter::StreamReader of the TypedInput.
|
||||
*
|
||||
* This STL-like container encapsulates a collection of loaned, read-only data
|
||||
* samples rti::sub::LoanedSample::data() and
|
||||
* rti::sub::LoanedSample::info() from a TypedInput.
|
||||
*
|
||||
* To obtain a LoanedSamples, you need to call one of the read/take operations
|
||||
* from a TypedInput. The samples have to be eventually returned to the
|
||||
* TypedInput by calling rti::routing::adapter::StreamReader::return_loan on
|
||||
* the underlying rti::routing::adapter::StreamReader of the TypedInput. The
|
||||
* destructor takes care of that, and the return_loan() function lets you do
|
||||
* it explicitly if needed.
|
||||
*
|
||||
* As a move-only type, copying a LoanedSamples is not allowed. If you need
|
||||
* to return a LoanedSamples from a function or assign it to another variable,
|
||||
* use \p dds::core::move() (or \p std::move() for C++11).
|
||||
*
|
||||
* Iterators and overloaded subscript operators let you access the samples
|
||||
* in this container, which are of the type rti::routing::processor::LoanedSample.
|
||||
*
|
||||
* This code demonstrates how to access the info and data of each sample in
|
||||
* a DataReader:
|
||||
*
|
||||
* \code
|
||||
* auto samples = reader.take();
|
||||
* for (const auto& sample : samples) {
|
||||
* if (sample.info().valid()) {
|
||||
* std::cout << sample.data() << std::endl;
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* @see dds::sub::LoanedSamples
|
||||
*
|
||||
*/
|
||||
template <typename T, typename U = dds::sub::SampleInfo>
|
||||
class LoanedSamples {
|
||||
public:
|
||||
|
||||
typedef rti::routing::adapter::StreamReader StreamReader;
|
||||
typedef typename std::vector<StreamReader::SamplePtr> SampleSeqType;
|
||||
typedef typename std::vector<StreamReader::InfoPtr> InfoSeqType;
|
||||
|
||||
/**
|
||||
* @brief The iterator type
|
||||
*/
|
||||
typedef SampleIterator<T, U> iterator;
|
||||
typedef SampleIterator<T, U> const_iterator;
|
||||
typedef typename SampleIterator<T, U>::value_type value_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
/**
|
||||
* @brief Creates an empty LoanedSamples object.
|
||||
*/
|
||||
LoanedSamples() : input_(NULL)
|
||||
{
|
||||
native_samples_.data_array = NULL;
|
||||
native_samples_.info_array = NULL;
|
||||
native_samples_.length = 0;
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
// Private c-tor only to be used by static method create_from_loans()
|
||||
LoanedSamples(
|
||||
RTI_RoutingServiceInput *native_input,
|
||||
RTI_RoutingServiceLoanedSamples& native_samples)
|
||||
: input_(native_input),
|
||||
native_samples_(native_samples)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Automatically returns the loan to the TypedInput used
|
||||
* to obtained these samples.
|
||||
*
|
||||
* @see return_loan
|
||||
*/
|
||||
~LoanedSamples() throw()
|
||||
{
|
||||
try {
|
||||
return_loan();
|
||||
} catch (const std::exception&) { // Do not throw in destructor
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides access to the underlying LoanedSample object in
|
||||
* array-like syntax.
|
||||
*
|
||||
* @param[in] index
|
||||
* The index of the Sample. Allowed values are from 0 to length()-1.
|
||||
*
|
||||
* @return A LoanedSample object that refers to data and info
|
||||
* at the specified \p index.
|
||||
*/
|
||||
LoanedSample<T,U> operator [] (size_t index)
|
||||
{
|
||||
return native_samples_.info_array == NULL
|
||||
? LoanedSample<T, U>(native_samples_.data_array[index], NULL)
|
||||
: LoanedSample<T, U>(
|
||||
native_samples_.data_array[index],
|
||||
native_samples_.info_array[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of samples in this collection
|
||||
*/
|
||||
int32_t length() const
|
||||
{
|
||||
return native_samples_.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the samples to the TypedInput used to get these
|
||||
* samples.
|
||||
*
|
||||
* Thes operation returns the samples by calling the
|
||||
* \link rti::routing::adapter::StreamReader::return_loan return_loan\endlink
|
||||
* operation on the underlying rti::routing::stream::StreamReader of the
|
||||
* TypedInput.
|
||||
*
|
||||
* @note Explicitly calling return_loan is optional, since the
|
||||
* destructor does it implicitly.
|
||||
*
|
||||
* This operation tells the TypedInput that the application
|
||||
* is done accessing the collection of samples.
|
||||
*
|
||||
* It is not necessary for an application to return the loans immediately
|
||||
* after the call to read or take. However, as these buffers correspond to
|
||||
* internal resources, the application should not retain them indefinitely.
|
||||
*
|
||||
* @throw std::exception
|
||||
* If there was an error returning the loan to the TypedInput.
|
||||
*/
|
||||
void return_loan()
|
||||
{
|
||||
if (input_) {
|
||||
if (!RTI_RoutingServiceInput_return_loan(
|
||||
input_,
|
||||
&native_samples_)) {
|
||||
throw dds::core::Error("error returning loaned samples to native input");
|
||||
}
|
||||
input_ = NULL; // Indicate that this object doesn't hold a loan anymore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns whether the Info part is available for each Data item of
|
||||
* this set of loaned samples.
|
||||
*/
|
||||
bool has_infos()
|
||||
{
|
||||
return (native_samples_.info_array != NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets an iterator to the first sample
|
||||
*/
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(
|
||||
native_samples_.data_array,
|
||||
native_samples_.info_array,
|
||||
native_samples_.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets an iterator to one past the last sample
|
||||
*/
|
||||
iterator end()
|
||||
{
|
||||
return iterator(
|
||||
native_samples_.data_array,
|
||||
native_samples_.info_array,
|
||||
native_samples_.length,
|
||||
native_samples_.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets an iterator to the first sample
|
||||
*/
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(
|
||||
native_samples_.data_array,
|
||||
native_samples_.info_array,
|
||||
native_samples_.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets an iterator to one past the last sample
|
||||
*/
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(
|
||||
native_samples_.data_array,
|
||||
native_samples_.info_array,
|
||||
native_samples_.length,
|
||||
native_samples_.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two LoanedSamples containers
|
||||
*/
|
||||
void swap(LoanedSamples& other) throw()
|
||||
{
|
||||
std::swap(input_, other.input_);
|
||||
std::swap(native_samples_, other.native_samples_);
|
||||
}
|
||||
|
||||
LoanedSamples(LoanedSamples&& other)
|
||||
:input_(NULL)
|
||||
{
|
||||
native_samples_.data_array = NULL;
|
||||
native_samples_.info_array = NULL;
|
||||
native_samples_.length = 0;
|
||||
other.swap(*this);
|
||||
}
|
||||
|
||||
|
||||
LoanedSamples& operator= (LoanedSamples&& other) throw ()
|
||||
{
|
||||
// clean up existing values
|
||||
LoanedSamples temp(std::move(other));
|
||||
temp.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class TypedInput<T, U>;
|
||||
|
||||
void release()
|
||||
{
|
||||
input_ = NULL;
|
||||
}
|
||||
|
||||
RTI_RoutingServiceLoanedSamples& native_samples()
|
||||
{
|
||||
return native_samples_;
|
||||
}
|
||||
|
||||
private:
|
||||
// reference to the reader that created this LoanedSamples object
|
||||
RTI_RoutingServiceInput *input_;
|
||||
RTI_RoutingServiceLoanedSamples native_samples_;
|
||||
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
LoanedSamples<T, U> move(LoanedSamples<T,U> & ls) OMG_NOEXCEPT
|
||||
{
|
||||
return std::move(ls);
|
||||
}
|
||||
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_LOANED_SAMPLES_HPP_
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_OUTPUT_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_OUTPUT_HPP_
|
||||
|
||||
#include <dds/core/Value.hpp>
|
||||
#include <dds/core/SafeEnumeration.hpp>
|
||||
#include <rti/core/NativeValueType.hpp>
|
||||
#include <dds/pub/DataWriter.hpp>
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
|
||||
#include "routingservice/routingservice_processor.h"
|
||||
#include "routingservice/routingservice_adapter_new.h"
|
||||
#include <rti/routing/adapter/StreamWriter.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
class Route;
|
||||
|
||||
template <typename Data, typename Info> class TypedOutput;
|
||||
|
||||
template <typename Data, typename Info = dds::sub::SampleInfo>
|
||||
class TypedOutput;
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::Output
|
||||
*
|
||||
* @brief Generic Representation of a Route's output.
|
||||
*
|
||||
* This class provides a base representation of the outputs contained in a Route.
|
||||
* Since it is generic, this object does not allow performing any operation
|
||||
* that requires dealing with the data passed to the underlying
|
||||
* rti::routing::adapter::StreamWriter.
|
||||
*/
|
||||
class Output {
|
||||
public:
|
||||
|
||||
/*i
|
||||
*/
|
||||
Output(
|
||||
RTI_RoutingServiceOutput *native,
|
||||
int32_t index,
|
||||
RTI_RoutingServiceRoute *native_route) :
|
||||
native_(native),
|
||||
index_(index),
|
||||
native_route_(native_route),
|
||||
stream_info_(*RTI_RoutingServiceOutput_get_stream_info(native)),
|
||||
name_(RTI_RoutingServiceOutput_get_name(native))
|
||||
{
|
||||
}
|
||||
|
||||
/*i
|
||||
*/
|
||||
const rti::routing::StreamInfo& stream_info() const
|
||||
{
|
||||
return stream_info_;
|
||||
}
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
const std::string& name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/*i
|
||||
*
|
||||
*/
|
||||
int32_t index()
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a typed version of this Output.
|
||||
*
|
||||
* Note: This operation is not type-safe and it is the caller's
|
||||
* responsibility to use the appropriate type.
|
||||
*
|
||||
* @see TypedOutput
|
||||
* @see <tt>TypedOutput<DataRep, Info> Route<DataRep, InfoRep>::output(int32_t index)</tt>
|
||||
*/
|
||||
template <typename Data, typename Info>
|
||||
TypedOutput<Data, Info> get()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a typed version of this Output.
|
||||
*
|
||||
* Note: This operation is not type-safe and it is the caller's
|
||||
* responsibility to use the appropriate type.
|
||||
*
|
||||
* @see TypedOutput
|
||||
* @see TypedOutput<tt><DataRep, dds::sub::SampleInfo></tt> Route<DataRep>::output(int32_t index)
|
||||
*/
|
||||
template <typename Data>
|
||||
TypedOutput<Data, dds::sub::SampleInfo> get()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Data, typename Info> friend class TypedOutput;
|
||||
friend class Route;
|
||||
RTI_RoutingServiceOutput *native_;
|
||||
int32_t index_;
|
||||
RTI_RoutingServiceRoute *native_route_;
|
||||
rti::routing::StreamInfo stream_info_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::TypedOutput
|
||||
*
|
||||
* @brief Representation of an Output whose data representation is \p DataRep,
|
||||
* whose info representation is \p InfoRep.
|
||||
*
|
||||
*/
|
||||
template <typename Data, typename Info>
|
||||
class TypedOutput{
|
||||
public:
|
||||
TypedOutput(Output *output) ;
|
||||
|
||||
TypedOutput<Data, Info>* operator->();
|
||||
|
||||
/**
|
||||
* @brief Returns the StreamInfo associated with this object.
|
||||
*
|
||||
* The associated StreamInfo represents an equivalent object to the one
|
||||
* used to create the underlying rti::routing::adapter::StreamWriter.
|
||||
*/
|
||||
const rti::routing::StreamInfo& stream_info() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the name of this output.
|
||||
*
|
||||
* The name is given by the name of the corresponding XML configuration tag.
|
||||
*/
|
||||
const std::string& name() const;
|
||||
|
||||
/**
|
||||
* @brief Writes the specified data and info sample in this output.
|
||||
*
|
||||
* This operation will call rti::routing::adapter::StreamWriter::write on
|
||||
* the underlying rti::routing::adapter::StreamWriter.
|
||||
*
|
||||
* The provided sample is interpreted to contain data represented
|
||||
* as \c Data and the info as \c Info.
|
||||
*
|
||||
* @see rti::routing::adapter::StreamWriter::write
|
||||
*/
|
||||
void write(const Data& sample, const Info& info);
|
||||
|
||||
/*
|
||||
* @brief Writes a sample given its data only.
|
||||
*
|
||||
* This operation will call rti::routing::adapter::StreamWriter::write
|
||||
* on the underlying rti::routing::adapter::StreamWriter, providing a null
|
||||
* value for the info sample. The underlying adapter will compute any
|
||||
* associated metadata from the data sample.
|
||||
*/
|
||||
void write(const Data& sample);
|
||||
|
||||
/**
|
||||
* @brief Creates a new data sample that can be used to write in this
|
||||
* output.
|
||||
*
|
||||
* The data constructor called in this operation depends on the data
|
||||
* representation kind of the TypeInfo associated with this output.
|
||||
* If the type representation is a dynamic type, then it will assume
|
||||
* the following constructor:
|
||||
* \code Data(const dds::core::xtypes::DynamicType&) \endcode
|
||||
*
|
||||
* and will use the type code object downcasted from the value
|
||||
* returned by TypeInfo::type_representation.
|
||||
*
|
||||
* Otherwise:
|
||||
* \code Data() \endcode
|
||||
*
|
||||
* @return A new data sample
|
||||
*/
|
||||
Data create_data();
|
||||
|
||||
/**
|
||||
* @brief Returns the underlying DDS DataWriter that is part of this
|
||||
* StreamWriter implementation, assuming this input holds a DDS StreamWriter.
|
||||
*
|
||||
* This operation will throw an exception if this output does not hold a
|
||||
* DDS StreamWriter.
|
||||
*
|
||||
* @note The access to the underlying DataWriter is allowed only as long as
|
||||
* this Output remains enabled. The object representing the DataWriter
|
||||
* must be invalidated before the output is disabled, otherwise the behavior
|
||||
* is undefined.
|
||||
*
|
||||
* @throw std::exception
|
||||
*
|
||||
* @return dds::pub::DataWriter<Data>
|
||||
*/
|
||||
dds::pub::DataWriter<Data> dds_data_writer()
|
||||
{
|
||||
DDS_DataWriter *native_writer =
|
||||
RTI_RoutingServiceOutput_get_dds_writer(output_->native_);
|
||||
if (native_writer == NULL) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
"invalid argument: input does not hold a DDS StreamWriter");
|
||||
}
|
||||
|
||||
typedef dds::pub::DataWriter<Data> data_writer_type;
|
||||
return rti::core::detail::create_from_native_entity<data_writer_type>(
|
||||
native_writer);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class rti::routing::processor::Route;
|
||||
|
||||
Output *output_;
|
||||
};
|
||||
|
||||
template <typename Data, typename Info>
|
||||
struct create_data_from_output {
|
||||
|
||||
static Data get(TypedOutput<Data, Info>& )
|
||||
{
|
||||
return Data();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Info>
|
||||
struct create_data_from_output<dds::core::xtypes::DynamicData, Info> {
|
||||
|
||||
static dds::core::xtypes::DynamicData get(TypedOutput<dds::core::xtypes::DynamicData, Info>& output)
|
||||
{
|
||||
if (output->stream_info().type_info().type_representation_kind()
|
||||
!= TypeRepresentationKind::DYNAMIC_TYPE) {
|
||||
throw dds::core::PreconditionNotMetError(
|
||||
"inconsistent data representation kind");
|
||||
}
|
||||
dds::core::xtypes::DynamicType *type_code =
|
||||
static_cast<dds::core::xtypes::DynamicType *> (
|
||||
output->stream_info().type_info().type_representation());
|
||||
return dds::core::xtypes::DynamicData(*type_code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Data, typename Info>
|
||||
TypedOutput<Data,Info>::TypedOutput(Output* output) : output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
const rti::routing::StreamInfo& TypedOutput<Data, Info>::stream_info() const
|
||||
{
|
||||
return output_->stream_info_;
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
const std::string& TypedOutput<Data, Info>::name() const
|
||||
{
|
||||
return output_->name_;
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
TypedOutput<Data, Info>* TypedOutput<Data,Info>::operator->()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
void TypedOutput<Data, Info>::write(const Data& sample, const Info& info)
|
||||
{
|
||||
if (!RTI_RoutingServiceOutput_write_sample(
|
||||
output_->native_,
|
||||
const_cast<void *> (reinterpret_cast<const void*> (&sample)),
|
||||
const_cast<void *> (reinterpret_cast<const void*> (&info)))) {
|
||||
throw dds::core::Error("error writing sample to native output");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
void TypedOutput<Data,Info>::write(const Data& sample)
|
||||
{
|
||||
if (!RTI_RoutingServiceOutput_write_sample(
|
||||
output_->native_,
|
||||
const_cast<void *> (reinterpret_cast<const void*> (&sample)),
|
||||
NULL)) {
|
||||
throw dds::core::Error("error writing sample to native output");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Data, typename Info>
|
||||
Data TypedOutput<Data,Info>::create_data()
|
||||
{
|
||||
return create_data_from_output<Data, Info>::get(*this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convenient definition of a TypedOutput whose data representation
|
||||
* is DynamicData and info representation is dds::sub::SampleInfo.
|
||||
*
|
||||
* These data and info representations are expected be used by most
|
||||
* rti::routing::adapter::StreamWriter implementations.
|
||||
*/
|
||||
typedef TypedOutput<dds::core::xtypes::DynamicData> DynamicDataOutput;
|
||||
typedef dds::pub::DataWriter<dds::core::xtypes::DynamicData> DynamicDataWriter;
|
||||
|
||||
} } }
|
||||
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_OUTPUT_HPP_
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_PORTS_ITERATOR_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_PORTS_ITERATOR_HPP_
|
||||
|
||||
#include <dds/core/Value.hpp>
|
||||
#include <dds/core/SafeEnumeration.hpp>
|
||||
#include <rti/core/NativeValueType.hpp>
|
||||
|
||||
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
#include <dds/sub/SampleInfo.hpp>
|
||||
#include <rti/routing/processor/Input.hpp>
|
||||
#include <rti/routing/processor/Output.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
template <typename PORT, typename DataRep, typename InfoRep> struct TypedPortType;
|
||||
|
||||
template <typename DataRep, typename InfoRep>
|
||||
struct TypedPortType<Input, DataRep, InfoRep>
|
||||
{
|
||||
typedef TypedInput<DataRep, InfoRep> type;
|
||||
typedef RTI_RoutingServiceInput native_type;
|
||||
|
||||
static native_type * begin(RTI_RoutingServiceRoute *native_route)
|
||||
{
|
||||
return RTI_RoutingServiceRoute_get_first_input(native_route);
|
||||
}
|
||||
|
||||
static native_type * next(
|
||||
RTI_RoutingServiceRoute *native_route,
|
||||
native_type *native)
|
||||
{
|
||||
return RTI_RoutingServiceRoute_get_next_input(native_route, native);
|
||||
}
|
||||
|
||||
static Input* from_native(native_type *native)
|
||||
{
|
||||
return static_cast<Input*>(RTI_RoutingServiceInput_get_user_data(
|
||||
native));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename DataRep, typename InfoRep>
|
||||
struct TypedPortType<Output, DataRep, InfoRep>
|
||||
{
|
||||
typedef TypedOutput<DataRep, InfoRep> type;
|
||||
typedef RTI_RoutingServiceOutput native_type;
|
||||
|
||||
static native_type * begin(RTI_RoutingServiceRoute *native_route)
|
||||
{
|
||||
return RTI_RoutingServiceRoute_get_first_output(native_route);
|
||||
}
|
||||
|
||||
static native_type * next(
|
||||
RTI_RoutingServiceRoute *native_route,
|
||||
native_type *native)
|
||||
{
|
||||
return RTI_RoutingServiceRoute_get_next_output(native_route, native);
|
||||
}
|
||||
|
||||
static Output* from_native(native_type *native)
|
||||
{
|
||||
return static_cast<Output*>(RTI_RoutingServiceOutput_get_user_data(
|
||||
native));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PORT,
|
||||
typename DataRep,
|
||||
typename InfoRep = dds::sub::SampleInfo>
|
||||
class PortIterator {
|
||||
public:
|
||||
// iterator traits
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef typename TypedPortType<PORT, DataRep, InfoRep>::type value_type;
|
||||
typedef value_type reference;
|
||||
typedef value_type pointer;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
|
||||
explicit PortIterator(
|
||||
RTI_RoutingServiceRoute *native_route)
|
||||
: native_route_(native_route)
|
||||
{
|
||||
native_ = TypedPortType<PORT, DataRep, InfoRep>::begin(native_route);
|
||||
}
|
||||
|
||||
explicit PortIterator()
|
||||
: native_route_(NULL), native_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PortIterator (const PortIterator<PORT, DataRep, InfoRep>& other)
|
||||
: native_route_(other.native_route_),
|
||||
native_(other.native_)
|
||||
{}
|
||||
|
||||
PortIterator & operator = (const PortIterator<PORT, DataRep, InfoRep>& other)
|
||||
{
|
||||
native_route_ = other.native_route_;
|
||||
native_ = other.native_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*()
|
||||
{
|
||||
return TypedPortType<PORT, DataRep, InfoRep>::from_native(native_);
|
||||
}
|
||||
|
||||
value_type operator->()
|
||||
{
|
||||
return *(*this);
|
||||
}
|
||||
|
||||
PortIterator & operator++()
|
||||
{
|
||||
native_ = TypedPortType<PORT, DataRep, InfoRep>::next(
|
||||
native_route_,
|
||||
native_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PortIterator operator++(int)
|
||||
{
|
||||
PortIterator temp(*this);
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
friend bool operator<(
|
||||
const PortIterator & s1,
|
||||
const PortIterator & s2)
|
||||
{
|
||||
return s1.index() < s2.index();
|
||||
}
|
||||
|
||||
friend bool operator>(
|
||||
const PortIterator & s1,
|
||||
const PortIterator & s2)
|
||||
{
|
||||
return s1.index() > s2.index();
|
||||
}
|
||||
|
||||
friend bool operator<=(
|
||||
const PortIterator & s1,
|
||||
const PortIterator & s2)
|
||||
{
|
||||
return s1.index() <= s2.index();
|
||||
}
|
||||
|
||||
friend bool operator>=(
|
||||
const PortIterator & s1,
|
||||
const PortIterator & s2)
|
||||
{
|
||||
return s1.index() >= s2.index();
|
||||
}
|
||||
|
||||
friend bool operator==(
|
||||
const PortIterator & s1,
|
||||
const PortIterator & s2)
|
||||
{
|
||||
return (s1.index() == s2.index());
|
||||
}
|
||||
|
||||
friend bool operator!=(
|
||||
const PortIterator & s1,
|
||||
const PortIterator & s2)
|
||||
{
|
||||
return !(s1 == s2);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t index() const
|
||||
{
|
||||
return (native_ == NULL)
|
||||
? RTI_INT32_MAX
|
||||
: TypedPortType<PORT, DataRep, InfoRep>::from_native(native_)->index();
|
||||
}
|
||||
|
||||
RTI_RoutingServiceRoute *native_route_;
|
||||
typename TypedPortType<PORT, DataRep, InfoRep>::native_type *native_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_PORTS_ITERATOR_HPP_
|
||||
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_PROCESSOR_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_PROCESSOR_HPP_
|
||||
|
||||
#include <dds/core/Value.hpp>
|
||||
#include <dds/core/SafeEnumeration.hpp>
|
||||
#include <rti/core/NativeValueType.hpp>
|
||||
|
||||
#include "routingservice/routingservice_processor.h"
|
||||
|
||||
#include <rti/routing/processor/Route.hpp>
|
||||
#include <rti/routing/UpdatableEntity.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::Processor
|
||||
*
|
||||
* @brief Processor interface definition. Provides a way to process Route events
|
||||
* and control the data forwarding process.
|
||||
*
|
||||
* A Processor receives event notifications from its Route owner in the form of
|
||||
* operation callbacks. Each event occurrence will be dispatched to the Processor
|
||||
* by calling the corresponding method.
|
||||
*
|
||||
* Each dispatching method has a signature according to the event kind and data
|
||||
* it is associated with. Each event is associated with a Route state; hence
|
||||
* limitations and constraints may apply individually to each method.
|
||||
*
|
||||
* Note that throwing an exception while processing any of the
|
||||
* event notifications is allowed. In this situation, the Route owner will
|
||||
* reject the events and none of the associated post conditions will be applied,
|
||||
* including state transitions.
|
||||
*
|
||||
* @mtsafety Partially Safe
|
||||
* All operations on a concrete Processor object are safe and
|
||||
* always serialized on a given Session. Operations on different
|
||||
* Processor objects may be called concurrently if they belong
|
||||
* to different Routes.
|
||||
*
|
||||
* @see ProcessorPlugin
|
||||
* @see Route
|
||||
*/
|
||||
class Processor : public rti::routing::UpdatableEntity {
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
* @brief Notification of the \c INPUT_ENABLED event.
|
||||
*
|
||||
* This operation is called when an Input enabled event occurs that
|
||||
* affects any of the inputs contained in the owner Route.
|
||||
*
|
||||
* This operation is called right after the affected Input has been enabled.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
*
|
||||
* \code
|
||||
* [.../sessions/Session/routes/Route/inputs/Input1|ENABLE] stream=Example
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
* @param[in] input
|
||||
* The just enabled Input.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p input
|
||||
* to be automatically disabled right after.
|
||||
*/
|
||||
virtual void on_input_enabled(
|
||||
Route& route,
|
||||
rti::routing::processor::Input& input) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Input disabled event.
|
||||
*
|
||||
* This operation is called when an \c INPUT_DISABLED event occurs that
|
||||
* affects any of the inputs contained in the owner Route.
|
||||
*
|
||||
* This operation is called right before the affected Input is disabled.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
*
|
||||
* \code
|
||||
* [.../sessions/Session/routes/Route/inputs/Input1|DISABLE] stream=Example
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
* @param[in] input
|
||||
* The Input about to be disabled.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p input
|
||||
* to remain enabled.
|
||||
*/
|
||||
virtual void on_input_disabled(
|
||||
Route& route,
|
||||
rti::routing::processor::Input& input) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Output enabled event.
|
||||
*
|
||||
* This operation is called when an \c OUTPUT_DISABLED event occurs that
|
||||
* affects any of the outputs contained in the owner Route.
|
||||
*
|
||||
* This operation is called right after the affected Output has been enabled.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
*
|
||||
* \code
|
||||
* [.../sessions/Session/routes/Route/outputs/Output1|ENABLE] stream=Example
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
* @param[in] output
|
||||
* The just enabled output.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p output
|
||||
* to be automatically disabled right after.
|
||||
*/
|
||||
virtual void on_output_enabled(
|
||||
Route& route,
|
||||
rti::routing::processor::Output& output) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Output disabled event.
|
||||
*
|
||||
* This operation is called when an \c OUTPUT_DISABLED event occurs that
|
||||
* affects any of the outputs contained in the owner Route.
|
||||
*
|
||||
* This operation is called right before the affected Output is disabled.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
*
|
||||
* \code
|
||||
* [.../sessions/Session/routes/Route/outputs/Output1|DISABLE] stream=Example
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
* @param[in] output
|
||||
* The Output about to be disabled.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p output
|
||||
* to remain enabled.
|
||||
*/
|
||||
virtual void on_output_disabled(
|
||||
Route& route,
|
||||
rti::routing::processor::Output& output) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Route started event.
|
||||
*
|
||||
* This operation is called right before the Route enters the \c STARTED
|
||||
* state. At the time this operation is called, all the inputs and outputs
|
||||
* within the Route are enabled.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the
|
||||
* occurrence of the event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute/sessions/Session/routes/Route|START]
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p route
|
||||
* to remain \c STOPPED.
|
||||
*/
|
||||
virtual void on_start(Route& route) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Route stopped event.
|
||||
*
|
||||
* This operation is called right before the Route enters the \c STOPPED
|
||||
* state. At the time this operation is called, all the inputs and outputs
|
||||
* within the Route are still enabled.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the
|
||||
* occurrence of the event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute/sessions/Session/routes/Route|STOP]
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p route
|
||||
* to remain \c STARTED.
|
||||
*
|
||||
*/
|
||||
virtual void on_stop(Route& route) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Route \c RUN event.
|
||||
*
|
||||
* This operation is called right before the Route enters the \c RUNNING
|
||||
* state. This operation is called after the Route went to \c STARTED after
|
||||
* a successful notification to this Processor,
|
||||
*
|
||||
* \note
|
||||
* If the Route was manually paused before via an Administration call, this
|
||||
* operation will not be called until a manual run operation is performed.
|
||||
*
|
||||
* A Routing Service running instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute/sessions/Session/routes/Route|RUN]
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p route
|
||||
* to remain \c PAUSED.
|
||||
*
|
||||
*/
|
||||
virtual void on_run(Route& route) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Route stopped event.
|
||||
*
|
||||
* This operation is called right before the Route enters the \c PAUSED
|
||||
* state. At the time this operation is called, all the inputs and outputs
|
||||
* within the Route are still enabled.
|
||||
*
|
||||
* A Routing Service running instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute/sessions/Session/routes/Route|PAUSE]
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will cause the \p route
|
||||
* to remain \c RUNNING.
|
||||
*
|
||||
*/
|
||||
virtual void on_pause(Route& route) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Route periodic action event.
|
||||
*
|
||||
* This operation is called periodically at the rate specified in the parent
|
||||
* Session of the Route owner.
|
||||
*
|
||||
* Periodic notifications can occur only while the Route is in the \c RUNNING
|
||||
* state.
|
||||
*
|
||||
* Implementations are allowed to access any of the Input and Output of the
|
||||
* owner Route to read and write data, respectively.
|
||||
*
|
||||
* A Routing Service running instance will produce a log indicating the occurrence
|
||||
* of the event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute/sessions/Session/routes/Route] process event=PERODIC_ACTION
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will produce a log message
|
||||
* without affecting the Route state and notifications of this event
|
||||
* to this Processor will keep occurring.
|
||||
*/
|
||||
virtual void on_periodic_action(Route& route) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notification of the Route \c DATA_AVAILABLE event
|
||||
*
|
||||
* This operation is called each time any of the inputs contained in the
|
||||
* owner Route is notified about new data. Notifications of this event can
|
||||
* occur only while the Route is in the \c RUNNING state.
|
||||
*
|
||||
* Implementations are allowed to access any of the Input and Output of the
|
||||
* owner Route to read and write data, respectively.
|
||||
*
|
||||
* A running Routing Service instance will produce a log indicating the
|
||||
* occurrence of the event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute/sessions/Session/routes/Route] process event=DATA_ON_INPUTS
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @param[in] route
|
||||
* Route owner that contains this Processor.
|
||||
*
|
||||
* @throw std::exception
|
||||
* Throwing an error in this operation will produce a log message
|
||||
* without affecting the Route state and notifications of this event
|
||||
* to this Processor will keep occurring.
|
||||
*
|
||||
*/
|
||||
virtual void on_data_available(Route& route) = 0;
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~Processor() {}
|
||||
};
|
||||
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::NoOpProcessor
|
||||
*
|
||||
* @brief An empty implementation of the pure virtual interface
|
||||
* rti::routing::processor::Processor.
|
||||
*
|
||||
* Your Processor implementation can inherit from this class and override
|
||||
* just the methods that are of interest.
|
||||
*/
|
||||
class NoOpProcessor : public Processor {
|
||||
|
||||
|
||||
virtual void on_input_enabled(Route&, rti::routing::processor::Input&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_input_disabled(Route&, rti::routing::processor::Input&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_output_enabled(Route&, rti::routing::processor::Output&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_output_disabled(Route&, rti::routing::processor::Output&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_start(Route&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_stop(Route&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_run(Route&)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_pause(Route&)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual void on_data_available(Route&)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual void on_periodic_action(Route&)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_PROCESSOR_HPP_
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_PROCESSOR_PLUGIN_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_PROCESSOR_PLUGIN_HPP_
|
||||
|
||||
#include "routingservice/routingservice_log.h"
|
||||
|
||||
#include <rti/config/Version.hpp>
|
||||
#include <rti/routing/PropertySet.hpp>
|
||||
#include <rti/routing/processor/Route.hpp>
|
||||
#include <rti/routing/processor/Processor.hpp>
|
||||
|
||||
/** @file
|
||||
* @brief \product C++ Processor API
|
||||
*/
|
||||
/** @defgroup RTI_RoutingServiceProcessorModule RTI Routing Service Processor API
|
||||
* @ingroup ROUTER
|
||||
* @brief This module describes the Processor API.
|
||||
*
|
||||
* @product rti::routing::processor::Processor are **event-oriented pluggable
|
||||
* components** that allow you to control the forwarding process that occur within a Route.
|
||||
*
|
||||
* ## Development Requirements
|
||||
*
|
||||
* | | Linux/macOS Systems | Windows Systems |
|
||||
* | --------------: | :----------------: | :-------------: |
|
||||
* | Shared Libraries| librtiroutingservice.so | librtiroutingservice.lib |
|
||||
* | ^ | librtirsinfrastructure.so | rtirsinfrastructure.lib |
|
||||
* | ^ | libnddscpp2.so | nddscpp2.lib |
|
||||
* | ^ | libnddsc.so | nddsc.lib |
|
||||
* | ^ | libnddscore.so | nddscore.lib |
|
||||
* | Headers | rti/routing/processor/ProcessorPlugin.hpp ||
|
||||
*
|
||||
* ## Architecture
|
||||
*
|
||||
* A rti::routing::processor::Processor represents a multiple-input,
|
||||
* multiple-output component attached to a rti::routing::processor::Route,
|
||||
* which dispatches events to the rti::routing::processor::Processor.
|
||||
* The figure below shows the role of a rti::routing::processor::Processor within
|
||||
* @product.
|
||||
*
|
||||
* \htmlonly <div style="margin:auto;width:700px;">\endhtmlonly
|
||||
* \image html RouterProcessorComponent.svg "Overview of a Processor component in @product" width=700px
|
||||
* \htmlonly </div>\endhtmlonly
|
||||
*
|
||||
* A Processor can access the *N* inputs and *M* outputs of the owner Route.
|
||||
* Upon event notification, the Processor can read data from any input,
|
||||
* perform any manipulation on the data, and write it on any of the outputs.
|
||||
*
|
||||
* Note in the figure above that any data a Processor writes on an output is
|
||||
* first transformed with an rti::routing::transf::Transformation before passing
|
||||
* it to the underlying rti::routing::adapter::StreamWriter.
|
||||
*
|
||||
* An example snippet is shown below. The example code is reading data from
|
||||
* two inputs, merging it together in an intermediate data buffer and writing
|
||||
* into a single output.
|
||||
*
|
||||
* \code{.cpp}
|
||||
* on_data_available(Route& route)
|
||||
* {
|
||||
* auto output = route.output<DynamicData>(0);
|
||||
* auto output_sample = output.create_data();
|
||||
* for(const auto& status : route.input<DynamicData>(0).take()) {
|
||||
* auto periodic = route.input<DynamicData>(1)
|
||||
* .select().instance(status.info().instance_handle()).read();
|
||||
* output_sample.value<int>(
|
||||
* "id",
|
||||
* status.data().get<int>("id"));
|
||||
* output_sample.value<int>(
|
||||
* "config",
|
||||
* status.data().get<string>("config"));
|
||||
* output_sample.value<int>(
|
||||
* "latency",
|
||||
* periodic[0].data().get<int>("latency"));
|
||||
* output.write(output_sample);
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* The Processor API component model is shown in figure below.
|
||||
*
|
||||
* \htmlonly <div style="margin:auto;width:800px;">\endhtmlonly
|
||||
* \image html RouterProcessorClass.svg "Processor API component model" width=800px
|
||||
* \htmlonly </div>\endhtmlonly
|
||||
*
|
||||
* * rti::routing::processor::Processor
|
||||
* * rti::routing::processor::Route
|
||||
* * rti::routing::processor::Input
|
||||
* * rti::routing::processor::Output
|
||||
* * rti::routing::processor::Selector
|
||||
*
|
||||
* ## Event dispatching
|
||||
*
|
||||
* A rti::routing::processor::Route will notify its contained
|
||||
* rti::routing::processor::Processor about the events that affect the Route
|
||||
* object. Each event kind maps to a virtual operation in the
|
||||
* rti::routing::processor::Processor interface:
|
||||
*
|
||||
* * **Input Enabled**: Event that represents the transition from disables to
|
||||
* enabled on any of the contained rti::routing::processor::Input. Enabling
|
||||
* an Input results in the creation of the underlying
|
||||
* rti::routing::adapter::StreamReader.
|
||||
* This event is notified through rti::routing::processor::Processor::on_input_enabled.
|
||||
*
|
||||
* * **Input Disabled**: Event that represents the transition from enabled to
|
||||
* disabled on any of the contained rti::routing::processor::Input. Disabling
|
||||
* an Input results in the deletion of the underlying
|
||||
* rti::routing::adapter::StreamReader.
|
||||
* This event is notified through rti::routing::processor::Processor::on_input_disabled.
|
||||
*
|
||||
* * **Output Enabled**: Event that represents the transition from disabled to
|
||||
* enabled on any of the contained rti::routing::processor::Output. Enabling
|
||||
* an Output results in the creation of the underlying
|
||||
* rti::routing::transf::Transformation and rti::routing::adapter::StreamWriter.
|
||||
* This event is notified through rti::routing::processor::Processor::on_output_enabled.
|
||||
*
|
||||
* * **Output Disabled**: Event that represents the transition from enabled to
|
||||
* disabled on any of the contained rti::routing::processor::Output. Disabling
|
||||
* an Output results in the deletion of the underlying
|
||||
* rti::routing::transf::Transformation and rti::routing::adapter::StreamWriter.
|
||||
* This event is notified through rti::routing::processor::Processor::on_output_disabled.
|
||||
*
|
||||
* * **Route Started**: This event indicates that the owner
|
||||
* rti::routing::processor::Route has enabled all inputs and outputs.
|
||||
* This event is notified through rti::routing::processor::Processor::on_start.
|
||||
*
|
||||
* * **Route Stopped**: This event indicates that the owner
|
||||
* rti::routing::processor::Route is about to disable at least one of its
|
||||
* inputs or outputs.
|
||||
* This event is notified through rti::routing::processor::Processor::on_stop.
|
||||
*
|
||||
* * **Route Running**: This event indicates that the owner
|
||||
* rti::routing::processor::Route is ready to provide user event
|
||||
* notifications.
|
||||
* This event is notified through rti::routing::processor::Processor::on_run.
|
||||
*
|
||||
* * **Route Paused**: This event indicates that the owner
|
||||
* rti::routing::processor::Route will temporarily stop providing user
|
||||
* event notifications.
|
||||
* This event is notified through rti::routing::processor::Processor::on_pause.
|
||||
*
|
||||
* * **Data Available**: This user event indicates at least one of the inputs
|
||||
* within the owner rti::routing::processor::Route has received new data.
|
||||
* This event is notified through rti::routing::processor::Processor::on_data_available.
|
||||
*
|
||||
* * **Periodic Action**: This user event represents a periodic notification,
|
||||
* at a rate configured in the parent Session of the owner
|
||||
* rti::routing::processor::Route.
|
||||
* This event is notified through rti::routing::processor::Processor::on_periodic_action.
|
||||
*
|
||||
*
|
||||
* ## Threading
|
||||
*
|
||||
* A rti::routing::processor::Route dispatches all the events to its contained
|
||||
* processors **sequentially**, from within one of the parent Session's threads.
|
||||
* For any given Route, and hence a Processor, it is guaranteed that only
|
||||
* one event is dispatched at at time. That is, the operations on the Processor
|
||||
* are never called concurrently for a given Route.
|
||||
*
|
||||
* A parent Session with *L* threads can only process one Route at
|
||||
* a time but it can dispatch different Routes concurrently. Namely, it can
|
||||
* dispatch up to *L* Routes concurrently. You should keep this behavior in mind
|
||||
* for implementations that share state between Processor objects.
|
||||
*
|
||||
* ## Constraints
|
||||
*
|
||||
* There are several constraints that affect the behavior and relationship
|
||||
* of the Processor API components:
|
||||
*
|
||||
* * A Route can have one and only one Processor. Alternatively, *semantically* a
|
||||
* Processor can belong to one and only one Route. That is, each Route will
|
||||
* call rti::routing::processor::ProcessorPlugin::create_processor and
|
||||
* rti::routing::processor::ProcessorPlugin::delete_processor on the
|
||||
* corresponding plug-in instance, if the implementation is returning the
|
||||
* same physical object.
|
||||
*
|
||||
* * An Input/Output can belong to one and only one Route. Thus, they cannot
|
||||
* be shared across different Routes. Alternatively, operations on an
|
||||
* Input, Output and Route can be performed only from within the associated
|
||||
* Processor. It is forbidden to pass Input, Output and Route objects between
|
||||
* Processors
|
||||
*/
|
||||
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::ProcessorPlugin
|
||||
* @brief The top-level plug-in class.
|
||||
*
|
||||
* Represents a factory of Processor.
|
||||
*/
|
||||
class ProcessorPlugin {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Creates a Route Processor.
|
||||
*
|
||||
* This function is called when the Route containing the Processor
|
||||
* is enabled.
|
||||
*
|
||||
* A Routing Service running instance will product a log indicating the
|
||||
* occurrence of this event. For example:
|
||||
* \code
|
||||
* [.../domain_routes/DomainRoute|START|/sessions/Session|START|/routes/Route|ENABLE]
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* <b>Required:</b> yes
|
||||
*
|
||||
* @param route
|
||||
* @rs_st_in An object representation of the Route that owns
|
||||
* the Processor.
|
||||
* @param properties
|
||||
* @rs_st_in Configuration properties for the Processor.
|
||||
* These properties corresponds to the properties specified within
|
||||
* the tag <processor>.
|
||||
*
|
||||
* @return New Processor if successful. Cannot return @null_value.
|
||||
* @throw std::exception
|
||||
* @mtsafety Safe
|
||||
*/
|
||||
virtual Processor * create_processor(
|
||||
Route& route,
|
||||
const rti::routing::PropertySet& properties) = 0;
|
||||
|
||||
/**
|
||||
* @brief Deletes a Route Processor.
|
||||
*
|
||||
* This function is called when the Route containing the Processor
|
||||
* is disabled.
|
||||
*
|
||||
* <b>Required:</b> yes
|
||||
*
|
||||
* @param route
|
||||
* @rs_st_in An object representation of the Route that owns
|
||||
* the Processor.
|
||||
* @param processor
|
||||
* @rs_st_in Processor object to be deleted.
|
||||
*
|
||||
* @throw std::exception
|
||||
* @mtsafety Safe
|
||||
*/
|
||||
virtual void delete_processor(
|
||||
Route& route,
|
||||
Processor *processor) = 0;
|
||||
|
||||
/**
|
||||
* @return The version of this TransformationPlugin.
|
||||
*
|
||||
* The version is used for logging purposes. It allows you to track which
|
||||
* version of the TransformationPlugin the @product is using.
|
||||
*
|
||||
* Default implementation of this operation returns the version of the
|
||||
* required Connext libraries.
|
||||
*/
|
||||
virtual rti::config::LibraryVersion get_version() const
|
||||
{
|
||||
return rti::config::LibraryVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~ProcessorPlugin() {}
|
||||
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
|
||||
#include <rti/routing/processor/detail/ProcessorPluginForwarder.hpp>
|
||||
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @brief Utility macro that declares a native extern function that can
|
||||
* be used to load a ProcessorPlugin through a shared library.
|
||||
*
|
||||
* The prototype of the function is given by a native definition of the
|
||||
* create operation.
|
||||
*
|
||||
* To register a ProcessorPlugin with @product, you must use the tag
|
||||
* <processor_plugin> within <plugin_library>. For example:
|
||||
*
|
||||
* \code
|
||||
* <dds>
|
||||
* ...
|
||||
* <plugin_library name="MyPluginLib">
|
||||
* <processor_plugin name="MyProcessorPlugin">
|
||||
* <dll>myprocessor</dll>
|
||||
* <create_function>
|
||||
* MyProcessorPlugin_create
|
||||
* </create_function>
|
||||
* </processor_plugin>
|
||||
* ...
|
||||
* </plugin_library>
|
||||
* ...
|
||||
* <routing_service>
|
||||
* ...
|
||||
* </routing_service>
|
||||
* ...
|
||||
* </dds>
|
||||
* \endcode
|
||||
*
|
||||
* Once a ProcessorPlugin is registered, a Route can use it to create a
|
||||
* Processor.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* \code
|
||||
* <topic_route name="SquareAndCircleToTriangle">
|
||||
* ...
|
||||
* <processor plugin_name="MyPluginLib::MyProcessorPlugin">
|
||||
* <property>
|
||||
* <value>
|
||||
* <element>
|
||||
* <name>my_property_name</name>
|
||||
* <value>my_property_value</value>
|
||||
* </element>
|
||||
* </value>
|
||||
* </property>
|
||||
* </processor>
|
||||
* <input participant="DomainIn">
|
||||
* <topic_name>Square</topic_name>
|
||||
* <registered_type_name>ShapeType</registered_type_name>
|
||||
* </input>
|
||||
* <input participant="DomainIn">
|
||||
* <topic_name>Circle</topic_name>
|
||||
* <registered_type_name>ShapeType</registered_type_name>
|
||||
* </input>
|
||||
* <output participant="DomainOut">
|
||||
* <topic_name>Triangle</topic_name>
|
||||
* <registered_type_name>ShapeType</registered_type_name>
|
||||
* </output>
|
||||
* </topic_route>
|
||||
* \endcode
|
||||
*
|
||||
* For additional information on configuring Processors, see the
|
||||
* \ref_url_routing_service_users_manual.
|
||||
*
|
||||
* @param PROCESSOR_CLASS
|
||||
* Class name of a ProcessorPlugin implementation
|
||||
*/
|
||||
#define RTI_PROCESSOR_PLUGIN_CREATE_FUNCTION_DECL(PROCESSOR_CLASS) \
|
||||
extern "C" RTI_USER_DLL_EXPORT struct RTI_RoutingServiceProcessorPlugin* \
|
||||
PROCESSOR_CLASS ## _create_processor_plugin(\
|
||||
const struct RTI_RoutingServiceProperties *, \
|
||||
RTI_RoutingServiceEnvironment *); \
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @brief Utility macro that implements the ProcessorPlugin entry point
|
||||
* declared with RTI_PROCESSOR_PLUGIN_CREATE_FUNCTION_DECL.
|
||||
*/
|
||||
#define RTI_PROCESSOR_PLUGIN_CREATE_FUNCTION_DEF(PROCESSOR_CLASS) \
|
||||
struct RTI_RoutingServiceProcessorPlugin * PROCESSOR_CLASS ## _create_processor_plugin( \
|
||||
const struct RTI_RoutingServiceProperties * native_properties, \
|
||||
RTI_RoutingServiceEnvironment *environment) \
|
||||
{ \
|
||||
PropertySet properties; \
|
||||
rti::routing::PropertyAdapter::add_properties_from_native(\
|
||||
properties,\
|
||||
native_properties); \
|
||||
try { \
|
||||
return rti::routing::processor::detail::ProcessorPluginForwarder::create_plugin(\
|
||||
new PROCESSOR_CLASS(properties)); \
|
||||
} catch (const std::exception& ex) {\
|
||||
RTI_RoutingServiceEnvironment_set_error(\
|
||||
environment,\
|
||||
"%s",\
|
||||
ex.what());\
|
||||
} catch (...) {}\
|
||||
\
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_PROCESSOR_PLUGIN_HPP_
|
||||
@@ -0,0 +1,138 @@
|
||||
/* $Id$
|
||||
|
||||
(c) Copyright, Real-Time Innovations, 2015-2016.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
|
||||
|
||||
============================================================================= */
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_QUERY_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_QUERY_HPP_
|
||||
|
||||
#include "routingservice/routingservice_processor.h"
|
||||
|
||||
#include <dds/core/Reference.hpp>
|
||||
#include <rti/core/detail/SelfReference.hpp>
|
||||
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
class Input;
|
||||
|
||||
class QueryHolder : public rti::core::detail::RetainableType<QueryHolder>{
|
||||
public:
|
||||
|
||||
QueryHolder() : input_(NULL), query_data_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
QueryHolder(RTI_RoutingServiceInput *input, const dds::topic::Filter& the_filter)
|
||||
: input_(input),
|
||||
query_data_(NULL)
|
||||
{
|
||||
filter(the_filter);
|
||||
}
|
||||
|
||||
~QueryHolder()
|
||||
{
|
||||
if (query_data_ != NULL) {
|
||||
/*
|
||||
* Ignore the error. We can't throw in the destructor and this
|
||||
* layer does not log anything
|
||||
*/
|
||||
(void) RTI_RoutingServiceInput_delete_content_query(
|
||||
input_,
|
||||
query_data_);
|
||||
}
|
||||
}
|
||||
|
||||
void filter(const dds::topic::Filter& filter)
|
||||
{
|
||||
RTI_RoutingServiceSelectorContent content =
|
||||
RTI_RoutingServiceSelectorContent_INITIALIZER;
|
||||
content.expression = (char *) filter.expression().c_str();
|
||||
rti::core::native_conversions::to_native(
|
||||
content.expression_parameters,
|
||||
filter->parameters());
|
||||
query_data_ = RTI_RoutingServiceInput_create_content_query(
|
||||
input_,
|
||||
query_data_,
|
||||
&content);
|
||||
DDS_StringSeq_finalize(&content.expression_parameters);
|
||||
if (query_data_ == NULL) {
|
||||
throw dds::core::Error("error creating content query from native input");
|
||||
}
|
||||
}
|
||||
|
||||
RTI_RoutingServiceInput *input_;
|
||||
void* query_data_;
|
||||
};
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::Query
|
||||
*
|
||||
* @brief Encapsulates a content query to select data from a
|
||||
* rti::routing::adapter::StreamReader.
|
||||
*
|
||||
* Note that syntax for the query filter is StreamReader implementation
|
||||
* dependent. For the DDS Adapter case, the syntax corresponds to that
|
||||
* of the SQL filter.
|
||||
*
|
||||
* A Query object holds a custom query obtained by calling
|
||||
* rti::routing::adapter::StreamReader::create_content_query. Likewise,
|
||||
* the deletion of a Query object will call
|
||||
* rti::routing::adapter::StreamReader::delete_content_query.
|
||||
*/
|
||||
class Query : public dds::core::Reference<rti::routing::processor::QueryHolder> {
|
||||
public:
|
||||
typedef dds::core::Reference<QueryHolder> Base;
|
||||
OMG_DDS_REF_TYPE_NOTYPENAME(
|
||||
Query,
|
||||
dds::core::Reference,
|
||||
rti::routing::processor::QueryHolder);
|
||||
|
||||
explicit Query(Base::DELEGATE_REF_T reference) : Base(reference)
|
||||
{
|
||||
if (this->delegate()) {
|
||||
this->delegate()->remember_reference(this->delegate());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a Query described by a filter on an input.
|
||||
*
|
||||
* @param[in] input The native Input to query the data from.
|
||||
* @param[in] filter Description of the content query based on an expression
|
||||
* and parameters.
|
||||
*
|
||||
* @see dds::topic::Filter
|
||||
*/
|
||||
Query(RTI_RoutingServiceInput *input, const dds::topic::Filter& filter)
|
||||
: Base(new QueryHolder(input, filter))
|
||||
{
|
||||
this->delegate()->remember_reference(this->delegate());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Updates the current Query content filter.
|
||||
*
|
||||
* This operation will call
|
||||
* rti::routing::adapter::StreamReader::create_content_query providing
|
||||
* the current query content data this Query holds.
|
||||
*/
|
||||
void filter(const dds::topic::Filter& filter)
|
||||
{
|
||||
this->delegate()->filter(filter);
|
||||
}
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_QUERY_HPP_
|
||||
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_ROUTE_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_ROUTE_HPP_
|
||||
|
||||
#include <dds/core/Value.hpp>
|
||||
#include <dds/core/SafeEnumeration.hpp>
|
||||
#include <rti/core/NativeValueType.hpp>
|
||||
|
||||
#include "routingservice/routingservice_processor.h"
|
||||
#include "routingservice/routingservice_processor_impl.h"
|
||||
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
#include <dds/sub/SampleInfo.hpp>
|
||||
#include <rti/routing/adapter/AdapterPlugin.hpp>
|
||||
#include <rti/routing/processor/Input.hpp>
|
||||
#include <rti/routing/processor/Output.hpp>
|
||||
#include <rti/routing/processor/LoanedSamples.hpp>
|
||||
#include <rti/routing/processor/PortIterator.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class ProcessorForwarder;
|
||||
|
||||
}
|
||||
|
||||
class Route;
|
||||
|
||||
template <typename PORT> struct port_list;
|
||||
|
||||
template <> struct port_list<Input>
|
||||
{
|
||||
static std::vector<Input>& get(Route& route);
|
||||
};
|
||||
|
||||
template <> struct port_list<Output>
|
||||
{
|
||||
static std::vector<Output>& get(Route& route);
|
||||
};
|
||||
|
||||
/** @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::Route
|
||||
*
|
||||
* @brief Representation of the Route object that owns a Processor.
|
||||
*
|
||||
* This class provides an interface to access the capabilities of a Route, such
|
||||
* as retrieving any of its inputs and outputs. Usage of this class
|
||||
* is restricted within the Processor context where the object is passed.
|
||||
*
|
||||
* An example of how to use this object is shown below. This example illustrates
|
||||
* a simple use case of reading from all inputs and writing to all outputs,
|
||||
* assuming that the sample data and info have the same representation and
|
||||
* type.
|
||||
*
|
||||
* \code{.c++}
|
||||
* using dds::core::xtypes::DynamicData;
|
||||
* using dds::sub::SampleInfo;
|
||||
* using namespace rti::routing::processor;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* for (auto input : route.inputs<DynamicData>()) {
|
||||
* LoanedSamples<DynamicData> samples = input->read();
|
||||
*
|
||||
* for (auto output : route.outputs<DynamicData>()) {
|
||||
* for(auto sample = samples) {
|
||||
* output.write(sample.data(), sample.info());
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
class Route {
|
||||
|
||||
public:
|
||||
|
||||
template <typename DataRep, typename InfoRep = dds::sub::SampleInfo>
|
||||
struct Inputs {
|
||||
typedef PortIterator<Input, DataRep, InfoRep> iterator;
|
||||
|
||||
Inputs(RTI_RoutingServiceRoute *native_route)
|
||||
: native_route_(native_route)
|
||||
{
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(native_route_);
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator();
|
||||
}
|
||||
|
||||
private:
|
||||
RTI_RoutingServiceRoute *native_route_;
|
||||
};
|
||||
|
||||
template <typename DataRep, typename InfoRep = dds::sub::SampleInfo>
|
||||
struct Outputs {
|
||||
typedef PortIterator<Output, DataRep, InfoRep> iterator;
|
||||
|
||||
Outputs(RTI_RoutingServiceRoute *native_route)
|
||||
: native_route_(native_route)
|
||||
{
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(native_route_);
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator();
|
||||
}
|
||||
|
||||
private:
|
||||
RTI_RoutingServiceRoute *native_route_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the total number of inputs in this Route.
|
||||
*
|
||||
*/
|
||||
int32_t input_count() const
|
||||
{
|
||||
return RTI_RoutingServiceRoute_get_input_count(native_route_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the total number of outputs in this Route.
|
||||
*
|
||||
*/
|
||||
int32_t output_count() const
|
||||
{
|
||||
return RTI_RoutingServiceRoute_get_output_count(native_route_);
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief Checks whether the Input at the specified index is enabled
|
||||
*
|
||||
*/
|
||||
bool input_enabled(int32_t index) const
|
||||
{
|
||||
return RTI_RoutingServiceRoute_is_input_enabled(native_route_, index)
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief Checks whether the Input at the specified index is enabled
|
||||
*
|
||||
*/
|
||||
bool output_enabled(int32_t index) const
|
||||
{
|
||||
return RTI_RoutingServiceRoute_is_output_enabled(native_route_, index)
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Input object at the specified index.
|
||||
*
|
||||
* Input indexes are assigned in increasing creation order, starting at 0.
|
||||
* The creation order is the same order than the inputs are defined within
|
||||
* the Route configuration.
|
||||
*
|
||||
* The returned object can be used to construct the appropriate TypedInput,
|
||||
* which can be used for reading data.
|
||||
*
|
||||
* @param[in] index
|
||||
* The index of the Input to be retrieved.
|
||||
*
|
||||
* @return The Input at the specified index.
|
||||
*
|
||||
* @see TypedInput
|
||||
*
|
||||
*/
|
||||
Input& input(int32_t index)
|
||||
{
|
||||
return *(get_input_at_index(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Input object with the specified name or throws an
|
||||
* exception if the input with the given name is not found or if it is
|
||||
* found but is not enabled.
|
||||
*
|
||||
* \b NOTE: The condition that the input must be enabled will be removed
|
||||
* in a future version. This is being tracked with issue ID ROUTING-1131.
|
||||
*
|
||||
* @param[in] name The input configuration name
|
||||
*
|
||||
* @see TypedInput::name
|
||||
* @throw std::logic_error
|
||||
*/
|
||||
Input& input(const std::string& name)
|
||||
{
|
||||
return *(lookup_input(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the TypedInput object at the specified index.
|
||||
*
|
||||
* The returned object is typed so it can read samples whose type is
|
||||
* \p DataRep for data, and \p InfoRep for info. These types must
|
||||
* match the expected type representations of the underlying
|
||||
* rti::routing::adapter::StreamReader.
|
||||
*
|
||||
* Typically, these template arguments are typed
|
||||
* dds::core::xtypes::DynamicData and dds::sub::SampleInfo, respectively.
|
||||
*
|
||||
* @warning This operation performs no type integrity check. It is
|
||||
* responsibility of the caller to ensure the provided template
|
||||
* arguments match with the expected types of the input. Otherwise
|
||||
* behavior accessing the input is undefined.
|
||||
*
|
||||
* @return The TypedInput object at the specified index.
|
||||
*
|
||||
* @see input(int32_t)
|
||||
* @see TypedInput
|
||||
*/
|
||||
template <typename DataRep, typename InfoRep>
|
||||
TypedInput<DataRep, InfoRep> input(int32_t index)
|
||||
{
|
||||
return &input(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>input<DataRep, InfoRep>(int32_t)</tt> with
|
||||
* InfoRep = dds::sub::SampleInfo.
|
||||
*
|
||||
*/
|
||||
template <typename DataRep>
|
||||
TypedInput<DataRep> input(int32_t index)
|
||||
{
|
||||
return input<DataRep, dds::sub::SampleInfo>(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>input<DataRep, InfoRep>(int32_t)</tt> but using
|
||||
* the configuration name of the input.
|
||||
*
|
||||
*/
|
||||
template <typename DataRep, typename InfoRep>
|
||||
TypedInput<DataRep, InfoRep> input(const std::string& name)
|
||||
{
|
||||
return &input(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>input<DataRep, InfoRep>(std::string&)</tt> with
|
||||
* InfoRep = dds::sub::SampleInfo.
|
||||
*/
|
||||
template <typename DataRep>
|
||||
TypedInput<DataRep> input(const std::string& name)
|
||||
{
|
||||
return input<DataRep, dds::sub::SampleInfo>(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Output object at the specified index.
|
||||
*
|
||||
* Output indexes are assigned in increasing creation order, starting at 0.
|
||||
* The creation order is the same order than the outputs are defined within
|
||||
* the Route configuration.
|
||||
*
|
||||
* The returned object can be used to construct the appropriate TypedOutput,
|
||||
* which can be used for writing data.
|
||||
*
|
||||
* @param[in] index
|
||||
* The index of the Output to be retrieved.
|
||||
*
|
||||
* @return The Output at the specified index.
|
||||
*
|
||||
* @see TypedOutput
|
||||
*
|
||||
*/
|
||||
Output& output(int32_t index)
|
||||
{
|
||||
return *(get_output_at_index(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Output object with the specified name or throws an
|
||||
* exception if the output with the given name is not found or if it is
|
||||
* found but is not enabled.
|
||||
*
|
||||
* \b NOTE: The condition that the input must be enabled will be removed
|
||||
* in a future version. This is being tracked with issue ID ROUTING-1131.
|
||||
*
|
||||
* @param[in] name The output configuration name
|
||||
*
|
||||
* @see TypedOutput::name
|
||||
* @throw std::logic_error
|
||||
*/
|
||||
Output& output(const std::string& name)
|
||||
{
|
||||
return *(lookup_output(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the TypedOutput object at the specified index.
|
||||
*
|
||||
* The returned object is typed so it can write samples whose type is
|
||||
* \p DataRep for data, and \p InfoRep for info. These types must
|
||||
* match the expected type representations of the underlying
|
||||
* rti::routing::adapter::StreamWriter.
|
||||
*
|
||||
* Typically, these template arguments are typed
|
||||
* dds::core::xtypes::DynamicData and dds::sub::SampleInfo, respectively.
|
||||
*
|
||||
* @warning This operation performs no type integrity check. It is
|
||||
* responsibility of the caller to ensure the provided template
|
||||
* arguments match with the expected types of the output. Otherwise
|
||||
* behavior accessing the output is undefined.
|
||||
*
|
||||
* @return The TypedOutput object at the specified index.
|
||||
*
|
||||
* @see input(int32_t)
|
||||
*/
|
||||
template <typename DataRep, typename InfoRep>
|
||||
TypedOutput<DataRep, InfoRep> output(int32_t index)
|
||||
{
|
||||
return &output(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>output<DataRep, InfoRep>(int32_t)</tt> with
|
||||
* InfoRep = dds::sub::SampleInfo.
|
||||
*
|
||||
*/
|
||||
template <typename DataRep>
|
||||
TypedOutput<DataRep> output(int32_t index)
|
||||
{
|
||||
return output<DataRep, dds::sub::SampleInfo>(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>output<DataRep, InfoRep>(int32_t)</tt> but using
|
||||
* the configuration name of the output.
|
||||
*
|
||||
*/
|
||||
template <typename DataRep, typename InfoRep>
|
||||
TypedOutput<DataRep, InfoRep> output(const std::string& name)
|
||||
{
|
||||
return &output(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>output<DataRep, InfoRep>(std::string&)</tt> with
|
||||
* InfoRep = dds::sub::SampleInfo.
|
||||
*/
|
||||
template <typename DataRep>
|
||||
TypedOutput<DataRep> output(const std::string& name)
|
||||
{
|
||||
return output<DataRep, dds::sub::SampleInfo>(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an accessor for the contained inputs.
|
||||
*
|
||||
* The returned object that allows iterating over the
|
||||
* contained inputs, treating them as <tt>TypedInput<DataRep, InfoRep></tt>.
|
||||
*
|
||||
* @see <tt>Inputs<DataRep, InfoRep></tt>
|
||||
* @see <tt>TypedInput<DataRep, InfoRep> input(int32_t index)</tt>
|
||||
*/
|
||||
template <typename DataRep, typename InfoRep>
|
||||
Inputs<DataRep, InfoRep> inputs()
|
||||
{
|
||||
return Inputs<DataRep, InfoRep>(native_route_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>Inputs<DataRep, InfoRep> inputs()</tt> with
|
||||
* InfoRep = dds::sub::SampleInfo.
|
||||
*/
|
||||
template <typename DataRep>
|
||||
Inputs<DataRep> inputs()
|
||||
{
|
||||
return inputs<DataRep, dds::sub::SampleInfo>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an accessor for the contained outputs.
|
||||
*
|
||||
* The returned object that allows iterating over the
|
||||
* contained outputs, treating them as <tt>TypedOutput<DataRep, InfoRep></tt>.
|
||||
*
|
||||
* @see <tt>Outputs<DataRep, InfoRep></tt>
|
||||
* @see <tt>TypedOutput<DataRep, InfoRep> input(int32_t index)</tt>
|
||||
*/
|
||||
template <typename DataRep, typename InfoRep>
|
||||
Outputs<DataRep, InfoRep> outputs()
|
||||
{
|
||||
return Outputs<DataRep, InfoRep>(native_route_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Same as <tt>Inputs<DataRep, InfoRep> outputs()</tt> with
|
||||
* InfoRep = dds::sub::SampleInfo.
|
||||
*/
|
||||
template <typename DataRep>
|
||||
Outputs<DataRep> outputs()
|
||||
{
|
||||
return outputs<DataRep, dds::sub::SampleInfo>();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Changes the event period of this Route.
|
||||
*
|
||||
* This operation can be called many times within the same periodic event,
|
||||
* in which only the last call will have effect.
|
||||
*
|
||||
* The operation will enable the periodic event in this route if this
|
||||
* operation is called for the first time in a route with no period set
|
||||
* in the configuration.
|
||||
*
|
||||
* @param[in] period New session period
|
||||
*/
|
||||
void period(const dds::core::Duration& period)
|
||||
{
|
||||
struct DDS_Duration_t native_period = DDS_DURATION_ZERO;
|
||||
rti::core::native_conversions::to_native(native_period, period);
|
||||
RTI_RoutingServiceRoute_set_period(
|
||||
native_route_,
|
||||
&native_period);
|
||||
}
|
||||
|
||||
dds::core::Duration period() const
|
||||
{
|
||||
struct DDS_Duration_t native_period = DDS_DURATION_ZERO;
|
||||
RTI_RoutingServiceRoute_get_period(
|
||||
native_route_,
|
||||
&native_period);
|
||||
|
||||
return rti::core::native_conversions::from_native(native_period);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Returns the fully qualified of this Route, derived from the configuration.
|
||||
*
|
||||
*/
|
||||
const std::string& full_name() const
|
||||
{
|
||||
return full_name_;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
friend class detail::ProcessorForwarder;
|
||||
friend class Output;
|
||||
friend struct port_list<Input>;
|
||||
friend struct port_list<Output>;
|
||||
|
||||
typedef std::vector<Input>::iterator private_input_it;
|
||||
typedef std::vector<Output>::iterator private_output_it;
|
||||
|
||||
Route& operator= (const Route& other);
|
||||
|
||||
Route(RTI_RoutingServiceRoute *native_route,
|
||||
RTI_RoutingServiceEnvironment *native_env)
|
||||
: native_route_(native_route),
|
||||
native_env_(native_env)
|
||||
{
|
||||
const char *native_full_name =
|
||||
RTI_RoutingServiceRoute_get_full_name(native_route);
|
||||
if (native_full_name == nullptr) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
"Native Route has invalid full name");
|
||||
}
|
||||
full_name_ = native_full_name;
|
||||
}
|
||||
|
||||
Input* get_input_at_index(int32_t index)
|
||||
{
|
||||
RTI_RoutingServiceInput *native_input =
|
||||
RTI_RoutingServiceRoute_get_input_at(native_route_, index);
|
||||
if (native_input == NULL) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
RTI_RoutingServiceEnvironment_get_error_message(native_env_));
|
||||
}
|
||||
|
||||
return static_cast<Input*>(RTI_RoutingServiceInput_get_user_data(native_input));
|
||||
}
|
||||
|
||||
Output* get_output_at_index(int32_t index)
|
||||
{
|
||||
RTI_RoutingServiceOutput *native_output =
|
||||
RTI_RoutingServiceRoute_get_output_at(native_route_, index);
|
||||
if (native_output == NULL) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
RTI_RoutingServiceEnvironment_get_error_message(native_env_));
|
||||
}
|
||||
|
||||
return static_cast<Output*>(RTI_RoutingServiceOutput_get_user_data(native_output));
|
||||
}
|
||||
|
||||
|
||||
Input* lookup_input(const std::string& name)
|
||||
{
|
||||
RTI_RoutingServiceInput *native_input =
|
||||
RTI_RoutingServiceRoute_lookup_input_by_name(
|
||||
native_route_,
|
||||
name.c_str());
|
||||
if (native_input == NULL) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
RTI_RoutingServiceEnvironment_get_error_message(native_env_));
|
||||
}
|
||||
return static_cast<Input*>(RTI_RoutingServiceInput_get_user_data(native_input));
|
||||
|
||||
}
|
||||
|
||||
Output* lookup_output(const std::string& name)
|
||||
{
|
||||
RTI_RoutingServiceOutput *native_output =
|
||||
RTI_RoutingServiceRoute_lookup_output_by_name(
|
||||
native_route_,
|
||||
name.c_str());
|
||||
if (native_output == NULL) {
|
||||
throw dds::core::InvalidArgumentError(
|
||||
RTI_RoutingServiceEnvironment_get_error_message(native_env_));
|
||||
}
|
||||
return static_cast<Output*>(RTI_RoutingServiceOutput_get_user_data(native_output));
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
RTI_RoutingServiceRoute *native_route_;
|
||||
RTI_RoutingServiceEnvironment *native_env_;
|
||||
std::string full_name_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_ROUTE_HPP_
|
||||
@@ -0,0 +1,275 @@
|
||||
/* $Id$
|
||||
|
||||
(c) Copyright, Real-Time Innovations, 2014-2016.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
|
||||
|
||||
modification history
|
||||
--------------------
|
||||
1.0a,05may14,acr Added ValidSampleIterator and valid_samples helper
|
||||
1.0a,20mar13,acr Created
|
||||
============================================================================= */
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_SAMPLE_ITERATOR_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_SAMPLE_ITERATOR_HPP_
|
||||
|
||||
|
||||
#include <rti/routing/processor/LoanedSample.hpp>
|
||||
#include <exception>
|
||||
|
||||
namespace rti {
|
||||
namespace routing {
|
||||
namespace processor {
|
||||
|
||||
/*i
|
||||
* @brief Tag type to emulate/indicate that sample infos are not available
|
||||
*/
|
||||
struct no_sample_info_t {
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTI_RoutingServiceProcessorModule
|
||||
*
|
||||
* @class rti::routing::processor::SampleIterator
|
||||
* @brief A random-access iterator of LoanedSample
|
||||
*
|
||||
* @see rti::routing::processor::LoanedSamples
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
class SampleIterator {
|
||||
public:
|
||||
// iterator traits
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = const LoanedSample<T, U>;
|
||||
using reference = value_type;
|
||||
using pointer = value_type;
|
||||
using difference_type = std::size_t;
|
||||
using SampleSeqType = RTI_RoutingServiceSample*;
|
||||
using InfoSeqType = RTI_RoutingServiceSampleInfo*;
|
||||
|
||||
private:
|
||||
static no_sample_info_t *_null_info_scrachtpad[1];
|
||||
static InfoSeqType _null_info_seq;
|
||||
static difference_type _null_info_pos;
|
||||
|
||||
public:
|
||||
SampleIterator()
|
||||
: _sample_seq(0),
|
||||
_info_seq(0),
|
||||
_length(0),
|
||||
_pos(0),
|
||||
_info_pos(_pos)
|
||||
{
|
||||
}
|
||||
|
||||
SampleIterator(
|
||||
const SampleSeqType sample_seq,
|
||||
const InfoSeqType info_seq,
|
||||
int length,
|
||||
int position = 0)
|
||||
: _sample_seq(sample_seq),
|
||||
_info_seq(info_seq == NULL ? _null_info_seq : info_seq),
|
||||
_length(length),
|
||||
_info_pos(info_seq == NULL ? _null_info_pos : _pos)
|
||||
{
|
||||
if (position < 0) {
|
||||
throw dds::core::IllegalOperationError("SampleIterator out of range");
|
||||
}
|
||||
|
||||
_pos = static_cast<difference_type>(position);
|
||||
}
|
||||
|
||||
SampleIterator(const SampleIterator<T, U> & other)
|
||||
: _sample_seq(other._sample_seq),
|
||||
_info_seq(other._info_seq),
|
||||
_length(other._length),
|
||||
_pos(other._pos),
|
||||
_info_pos(other._info_pos)
|
||||
{
|
||||
}
|
||||
|
||||
SampleIterator & operator=(const SampleIterator<T, U> & other)
|
||||
{
|
||||
_sample_seq = other._sample_seq;
|
||||
_info_seq = other._info_seq;
|
||||
_length = other._length;
|
||||
_pos = other._pos;
|
||||
_info_pos = other._info_pos;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
value_type operator*() const
|
||||
{
|
||||
return value_type(_sample_seq[_pos], _info_seq[_info_pos]);
|
||||
}
|
||||
|
||||
value_type operator->() const
|
||||
{
|
||||
return value_type(_sample_seq[_pos], _info_seq[_info_pos]);
|
||||
}
|
||||
|
||||
value_type operator[](difference_type offset) const
|
||||
{
|
||||
return (_info_seq == _null_info_seq)
|
||||
? value_type(_sample_seq[_pos], NULL)
|
||||
: value_type(_sample_seq[_pos + offset], _info_seq[_info_pos + offset]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Windows defines min and max in one of the Windows headers, and they
|
||||
* collide with the max() definition we use below
|
||||
*/
|
||||
#ifdef RTI_WIN32
|
||||
#undef max
|
||||
#undef min
|
||||
#endif
|
||||
SampleIterator & operator++()
|
||||
{
|
||||
if (_pos == std::numeric_limits<difference_type>::max()) {
|
||||
throw dds::core::IllegalOperationError("SampleIterator out of range");
|
||||
}
|
||||
|
||||
++_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleIterator operator++(int)
|
||||
{
|
||||
SampleIterator temp(*this);
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
SampleIterator & operator--()
|
||||
{
|
||||
if (_pos == std::numeric_limits<difference_type>::min()) {
|
||||
throw dds::core::IllegalOperationError("SampleIterator out of range");
|
||||
}
|
||||
|
||||
--_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleIterator operator--(int)
|
||||
{
|
||||
SampleIterator temp(*this);
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
SampleIterator & operator-=(difference_type i)
|
||||
{
|
||||
if (i > _pos) {
|
||||
throw dds::core::IllegalOperationError("SampleIterator out of range");
|
||||
}
|
||||
|
||||
_pos -= i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleIterator & operator+=(difference_type i)
|
||||
{
|
||||
if ((std::numeric_limits<difference_type>::max() - _pos) < i) {
|
||||
throw dds::core::IllegalOperationError("SampleIterator out of range");
|
||||
}
|
||||
|
||||
_pos += i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend SampleIterator operator-(
|
||||
const SampleIterator& si,
|
||||
difference_type i)
|
||||
{
|
||||
SampleIterator temp(si);
|
||||
temp -= i;
|
||||
return temp;
|
||||
}
|
||||
|
||||
friend SampleIterator operator+(
|
||||
const SampleIterator& si,
|
||||
difference_type i)
|
||||
{
|
||||
SampleIterator temp(si);
|
||||
temp += i;
|
||||
return temp;
|
||||
}
|
||||
|
||||
friend difference_type operator-(
|
||||
const SampleIterator& s1,
|
||||
const SampleIterator& s2)
|
||||
{
|
||||
return s1._pos - s2._pos;
|
||||
}
|
||||
|
||||
friend bool operator<(const SampleIterator & s1,
|
||||
const SampleIterator & s2)
|
||||
{
|
||||
return s1._pos < s2._pos;
|
||||
}
|
||||
|
||||
friend bool operator>(const SampleIterator & s1,
|
||||
const SampleIterator & s2)
|
||||
{
|
||||
return s1._pos > s2._pos;
|
||||
}
|
||||
|
||||
friend bool operator<=(const SampleIterator & s1,
|
||||
const SampleIterator & s2)
|
||||
{
|
||||
return s1._pos <= s2._pos;
|
||||
}
|
||||
|
||||
friend bool operator>=(const SampleIterator & s1,
|
||||
const SampleIterator & s2)
|
||||
{
|
||||
return s1._pos >= s2._pos;
|
||||
}
|
||||
|
||||
friend bool operator==(const SampleIterator & s1,
|
||||
const SampleIterator & s2)
|
||||
{
|
||||
return (s1._sample_seq == s2._sample_seq) && (s1._pos == s2._pos);
|
||||
}
|
||||
|
||||
friend bool operator!=(const SampleIterator & s1,
|
||||
const SampleIterator & s2)
|
||||
{
|
||||
return !(s1 == s2);
|
||||
}
|
||||
|
||||
bool is_end() const
|
||||
{
|
||||
return (_length == _pos);
|
||||
}
|
||||
|
||||
// Keep these members public otherwise some of the
|
||||
// converting constructors will not compile.
|
||||
public:
|
||||
SampleSeqType _sample_seq;
|
||||
InfoSeqType _info_seq;
|
||||
int _length;
|
||||
difference_type _pos;
|
||||
difference_type& _info_pos;
|
||||
};
|
||||
|
||||
|
||||
template <typename T, typename U>
|
||||
std::size_t SampleIterator<T, U>::_null_info_pos = 0;
|
||||
|
||||
template <typename T, typename U>
|
||||
no_sample_info_t * SampleIterator<T, U>::_null_info_scrachtpad[1] = {NULL};
|
||||
|
||||
template <typename T, typename U>
|
||||
typename SampleIterator<T, U>::InfoSeqType SampleIterator<T, U>::_null_info_seq =
|
||||
reinterpret_cast<InfoSeqType> (SampleIterator<T, U>::_null_info_scrachtpad);
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_SAMPLE_ITERATOR_HPP_
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_DETAIL_PROCESSOR_FORWARDER_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_DETAIL_PROCESSOR_FORWARDER_HPP_
|
||||
|
||||
#include <rti/core/Exception.hpp>
|
||||
|
||||
#include "rti/apputils/ServiceException.hpp"
|
||||
|
||||
#include "routingservice/routingservice_processor_impl.h"
|
||||
#include <rti/routing/detail/ForwarderUtils.hpp>
|
||||
#include <rti/routing/detail/UpdatableEntityForwarder.hpp>
|
||||
#include <rti/routing/processor/Route.hpp>
|
||||
#include <rti/routing/processor/Input.hpp>
|
||||
#include <rti/routing/processor/Output.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor { namespace detail {
|
||||
|
||||
class ProcessorForwarder {
|
||||
|
||||
private:
|
||||
|
||||
template <typename PORT, typename NATIVE>
|
||||
class ScopedPort {
|
||||
|
||||
public:
|
||||
ScopedPort(Route& route, int32_t index, NATIVE *native)
|
||||
: route_(route),
|
||||
port_(new PORT(native, index, route.native_route_))
|
||||
{
|
||||
}
|
||||
|
||||
PORT* get()
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
port_ = NULL;
|
||||
}
|
||||
|
||||
~ScopedPort()
|
||||
{
|
||||
if (port_ != NULL) {
|
||||
delete port_;
|
||||
port_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Route& route_;
|
||||
PORT *port_;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
static RTI_RoutingServiceProcessor * create_native(
|
||||
ProcessorPlugin *plugin,
|
||||
RTI_RoutingServiceRoute *native_route,
|
||||
const struct RTI_RoutingServiceProperties *native_properties,
|
||||
RTI_RoutingServiceEnvironment *environment)
|
||||
{
|
||||
try {
|
||||
using rti::routing::detail::ScopedForwarder;
|
||||
|
||||
std::map<std::string, std::string> properties;
|
||||
rti::routing::PropertyAdapter::add_properties_from_native(
|
||||
properties,
|
||||
native_properties);
|
||||
|
||||
ProcessorForwarder *forwarder = new ProcessorForwarder(
|
||||
native_route,
|
||||
environment);
|
||||
ScopedForwarder<ProcessorPlugin, ProcessorForwarder> scoped(
|
||||
plugin,
|
||||
forwarder,
|
||||
environment);
|
||||
forwarder->processor_ = plugin->create_processor(
|
||||
forwarder->route(),
|
||||
properties);
|
||||
RTI_ROUTING_THROW_ON_NULL(forwarder->processor_);
|
||||
|
||||
scoped.release();
|
||||
|
||||
// TRUST-134: the forwarder is not leaked here. It might be leaked
|
||||
// if delete_native() were not to be called, but Routing Service
|
||||
// will make sure that is not the case
|
||||
|
||||
/* coverity[leaked_storage : FALSE] */
|
||||
return forwarder->native();
|
||||
} catch(const rti::apputils::FatalServiceException& ex) {
|
||||
RTI_RoutingServiceEnvironment_fatal_error(
|
||||
environment,
|
||||
ex.what());
|
||||
return NULL;
|
||||
} catch(const std::exception& ex) {
|
||||
RTI_RoutingServiceEnvironment_set_error(
|
||||
environment,
|
||||
ex.what());
|
||||
return NULL;
|
||||
} catch (...) {
|
||||
RTI_RoutingServiceEnvironment_set_error(
|
||||
environment,
|
||||
"unexpected exception");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void delete_native(
|
||||
ProcessorPlugin *plugin,
|
||||
RTI_RoutingServiceProcessor *native_processor,
|
||||
RTI_RoutingServiceEnvironment *environment)
|
||||
{
|
||||
ProcessorForwarder *processor_forwarder =
|
||||
static_cast<ProcessorForwarder*>(native_processor->processor_data);
|
||||
try {
|
||||
if (processor_forwarder->processor_ != NULL) {
|
||||
plugin->delete_processor(
|
||||
processor_forwarder->route_,
|
||||
processor_forwarder->processor_);
|
||||
processor_forwarder->processor_ = NULL;
|
||||
}
|
||||
} catch(const rti::apputils::FatalServiceException& ex) {
|
||||
RTI_RoutingServiceEnvironment_fatal_error(
|
||||
environment,
|
||||
ex.what());
|
||||
} catch(const std::exception& ex) {
|
||||
RTI_RoutingServiceEnvironment_set_error(
|
||||
environment,
|
||||
ex.what());
|
||||
} catch (...) {
|
||||
RTI_RoutingServiceEnvironment_set_error(
|
||||
environment,
|
||||
"unexpected exception");
|
||||
}
|
||||
|
||||
delete processor_forwarder;
|
||||
}
|
||||
|
||||
|
||||
static void forward_on_route_event(
|
||||
void *native_processor_data,
|
||||
RTI_RoutingServiceRouteEvent *native_route_event,
|
||||
RTI_RoutingServiceEnvironment *environment)
|
||||
{
|
||||
|
||||
ProcessorForwarder *forwarder =
|
||||
static_cast<ProcessorForwarder*>(native_processor_data);
|
||||
|
||||
try {
|
||||
|
||||
// build up wrapper objects based on the event
|
||||
switch (RTI_RoutingServiceRouteEvent_get_kind(native_route_event)) {
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_DATA_ON_INPUTS:
|
||||
forwarder->processor_->on_data_available(forwarder->route());
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_PERIODIC_ACTION:
|
||||
|
||||
forwarder->processor_->on_periodic_action(forwarder->route());
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_INPUT_ENABLED:
|
||||
{
|
||||
void *affected_entity =
|
||||
RTI_RoutingServiceRouteEvent_get_affected_entity(native_route_event);
|
||||
void *index =
|
||||
RTI_RoutingServiceRouteEvent_get_event_data(native_route_event);
|
||||
/* ScopedPort helps to delete the Input if an exception occurs */
|
||||
ScopedPort<Input, RTI_RoutingServiceInput> port(
|
||||
forwarder->route_,
|
||||
*(static_cast<int32_t*>(index)),
|
||||
static_cast<RTI_RoutingServiceInput *>(affected_entity));
|
||||
forwarder->processor_->on_input_enabled(
|
||||
forwarder->route(),
|
||||
*port.get());
|
||||
RTI_RoutingServiceInput_set_user_data(
|
||||
static_cast<RTI_RoutingServiceInput *>(affected_entity),
|
||||
port.get());
|
||||
/*
|
||||
* Input successfully enabled, so clear the ScopedPort so it
|
||||
* doesn't delete it
|
||||
*/
|
||||
port.clear();
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_INPUT_DISABLED:
|
||||
{
|
||||
|
||||
void *affected_entity =
|
||||
RTI_RoutingServiceRouteEvent_get_affected_entity(native_route_event);
|
||||
RTI_RoutingServiceInput *native_input =
|
||||
static_cast<RTI_RoutingServiceInput *>(affected_entity);
|
||||
Input *input = reinterpret_cast<Input*>(
|
||||
RTI_RoutingServiceInput_get_user_data(native_input));
|
||||
forwarder->processor_->on_input_disabled(
|
||||
forwarder->route(),
|
||||
*input);
|
||||
RTI_RoutingServiceInput_set_user_data(native_input, NULL);
|
||||
delete input;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_OUTPUT_ENABLED:
|
||||
{
|
||||
void *affected_entity =
|
||||
RTI_RoutingServiceRouteEvent_get_affected_entity(native_route_event);
|
||||
void *index =
|
||||
RTI_RoutingServiceRouteEvent_get_event_data(native_route_event);
|
||||
/* ScopedPort helps to delete the Output if an exception occurs */
|
||||
ScopedPort<Output, RTI_RoutingServiceOutput> port(
|
||||
forwarder->route_,
|
||||
*(static_cast<int32_t*>(index)),
|
||||
static_cast<RTI_RoutingServiceOutput *>(affected_entity));
|
||||
forwarder->processor_->on_output_enabled(
|
||||
forwarder->route(),
|
||||
*port.get());
|
||||
RTI_RoutingServiceOutput_set_user_data(
|
||||
static_cast<RTI_RoutingServiceOutput *>(affected_entity),
|
||||
port.get());
|
||||
/*
|
||||
* Output successfully enabled, so clear the ScopedPort so it
|
||||
* doesn't delete it
|
||||
*/
|
||||
port.clear();
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_OUTPUT_DISABLED:
|
||||
{
|
||||
void *affected_entity =
|
||||
RTI_RoutingServiceRouteEvent_get_affected_entity(native_route_event);
|
||||
RTI_RoutingServiceOutput *native_output =
|
||||
static_cast<RTI_RoutingServiceOutput *>(affected_entity);
|
||||
Output *output = reinterpret_cast<Output*>(
|
||||
RTI_RoutingServiceOutput_get_user_data(native_output));
|
||||
forwarder->processor_->on_output_disabled(
|
||||
forwarder->route(),
|
||||
*output);
|
||||
RTI_RoutingServiceOutput_set_user_data(native_output, NULL);
|
||||
delete output;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_ROUTE_STARTED:
|
||||
|
||||
forwarder->processor_->on_start(forwarder->route());
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_ROUTE_STOPPED:
|
||||
|
||||
forwarder->processor_->on_stop(forwarder->route());
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_ROUTE_RUNNING:
|
||||
|
||||
forwarder->processor_->on_run(forwarder->route());
|
||||
break;
|
||||
|
||||
case RTI_ROUTING_SERVICE_ROUTE_EVENT_ROUTE_PAUSED:
|
||||
|
||||
forwarder->processor_->on_pause(forwarder->route());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
} catch (const rti::apputils::FatalServiceException& ex) {
|
||||
RTI_RoutingServiceEnvironment_fatal_error(
|
||||
environment,
|
||||
ex.what());
|
||||
} catch (const std::exception& ex) {
|
||||
RTI_RoutingServiceEnvironment_set_error(
|
||||
environment,
|
||||
ex.what());
|
||||
} catch (...) {
|
||||
RTI_RoutingServiceEnvironment_set_error(
|
||||
environment,
|
||||
"%s",
|
||||
"unexpected exception");
|
||||
}
|
||||
}
|
||||
|
||||
static void forward_update(
|
||||
void *native_processor_data,
|
||||
const struct RTI_RoutingServiceProperties *native_properties,
|
||||
RTI_RoutingServiceEnvironment *environment)
|
||||
{
|
||||
|
||||
ProcessorForwarder *processorForwarder =
|
||||
static_cast<ProcessorForwarder*> (native_processor_data);
|
||||
|
||||
rti::routing::detail::UpdatableEntityForwarder::update(
|
||||
processorForwarder->processor_,
|
||||
native_properties,
|
||||
environment);
|
||||
}
|
||||
|
||||
RTI_RoutingServiceProcessor* native()
|
||||
{
|
||||
return &native_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ProcessorForwarder(
|
||||
RTI_RoutingServiceRoute *native_route,
|
||||
RTI_RoutingServiceEnvironment *native_env) :
|
||||
processor_(NULL),
|
||||
route_(native_route, native_env)
|
||||
{
|
||||
RTIOsapiMemory_zero(&native_, sizeof(native_));
|
||||
/* initialize native implementation */
|
||||
native_.processor_data = static_cast<void *>(this);
|
||||
native_.on_route_event = ProcessorForwarder::forward_on_route_event;
|
||||
native_.update = ProcessorForwarder::forward_update;
|
||||
}
|
||||
|
||||
rti::routing::processor::Route& route()
|
||||
{
|
||||
return route_;
|
||||
}
|
||||
|
||||
~ProcessorForwarder()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
RTI_RoutingServiceProcessor native_;
|
||||
Processor *processor_;
|
||||
rti::routing::processor::Route route_;
|
||||
|
||||
};
|
||||
|
||||
}}}}
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_DETAIL_PROCESSOR_FORWARDER_HPP_
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* (c) Copyright, Real-Time Innovations, 2017.
|
||||
* All rights reserved.
|
||||
*
|
||||
* No duplications, whole or partial, manual or electronic, may be made
|
||||
* without express written permission. Any such copies, or
|
||||
* revisions thereof, must display this notice unaltered.
|
||||
* This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_ROUTING_PROCESSOR_PLUGIN_DETAIL_PROCESSOR_PLUGIN_FORWARDER_HPP_
|
||||
#define RTI_ROUTING_PROCESSOR_PLUGIN_DETAIL_PROCESSOR_PLUGIN_FORWARDER_HPP_
|
||||
|
||||
#include <rti/core/Exception.hpp>
|
||||
|
||||
#include <rti/routing/processor/detail/ProcessorForwarder.hpp>
|
||||
#include <rti/routing/detail/ForwarderUtils.hpp>
|
||||
|
||||
namespace rti { namespace routing { namespace processor { namespace detail {
|
||||
|
||||
|
||||
class ProcessorPluginForwarder {
|
||||
public:
|
||||
|
||||
static RTI_RoutingServiceProcessorPlugin * create_plugin(
|
||||
ProcessorPlugin *plugin)
|
||||
{
|
||||
RTI_RoutingServiceProcessorPlugin *native_plugin = NULL;
|
||||
RTIOsapiHeap_allocateStructure(
|
||||
&native_plugin,
|
||||
struct RTI_RoutingServiceProcessorPlugin);
|
||||
rti::core::check_create_entity(
|
||||
native_plugin,
|
||||
"RTI_RoutingServiceProcessorPlugin");
|
||||
RTI_RoutingServiceProcessorPlugin_initialize(native_plugin);
|
||||
|
||||
// Set adapter version
|
||||
rti::config::LibraryVersion version = plugin->get_version();
|
||||
native_plugin->plugin_version.major = version.major_version();
|
||||
native_plugin->plugin_version.minor = version.minor_version();
|
||||
native_plugin->plugin_version.release = version.release_version();
|
||||
|
||||
// Initialize native implementation
|
||||
native_plugin->processor_plugin_data =
|
||||
static_cast<void *>(plugin);
|
||||
native_plugin->plugin_delete =
|
||||
ProcessorPluginForwarder::delete_plugin;
|
||||
native_plugin->create_processor =
|
||||
ProcessorPluginForwarder::forward_create_processor;
|
||||
native_plugin->delete_processor =
|
||||
ProcessorPluginForwarder::forward_delete_processor;
|
||||
|
||||
return native_plugin;
|
||||
}
|
||||
|
||||
static void delete_plugin(
|
||||
RTI_RoutingServiceProcessorPlugin *native_plugin,
|
||||
RTI_RoutingServiceEnvironment *)
|
||||
{
|
||||
ProcessorPlugin *plugin = static_cast<ProcessorPlugin*>(
|
||||
native_plugin->processor_plugin_data);
|
||||
// Plug-in is destructor not allowed to throw
|
||||
delete plugin;
|
||||
RTIOsapiHeap_freeStructure(native_plugin);
|
||||
}
|
||||
|
||||
|
||||
static RTI_RoutingServiceProcessor * forward_create_processor(
|
||||
void *native_plugin_data,
|
||||
RTI_RoutingServiceRoute *native_route,
|
||||
const struct RTI_RoutingServiceProperties *native_properties,
|
||||
RTI_RoutingServiceEnvironment *environment)
|
||||
{
|
||||
return ProcessorForwarder::create_native(
|
||||
static_cast<ProcessorPlugin *>(native_plugin_data),
|
||||
native_route,
|
||||
native_properties,
|
||||
environment);
|
||||
|
||||
}
|
||||
|
||||
static void forward_delete_processor(
|
||||
void *native_plugin_data,
|
||||
struct RTI_RoutingServiceProcessor *native_processor,
|
||||
RTI_RoutingServiceRoute *,
|
||||
RTI_RoutingServiceEnvironment *environment)
|
||||
{
|
||||
|
||||
ProcessorForwarder::delete_native(
|
||||
static_cast<ProcessorPlugin *>(native_plugin_data),
|
||||
native_processor,
|
||||
environment);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}}}}
|
||||
|
||||
#endif // RTI_ROUTING_PROCESSOR_PLUGIN_DETAIL_PROCESSOR_PLUGIN_FORWARDER_HPP_
|
||||
Reference in new issue
Block a user