Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/include/rti/routing/processor/Input.hpp
T

704 lines
21 KiB
C++

/*
* (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_