Page MenuHomePhorge

Application.hpp
No OneTemporary

Size
37 KB
Referenced Files
None
Subscribers
None

Application.hpp

//===-- rosa/app/Application.hpp --------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/app/Application.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2020
///
/// \brief Public interface for the *application interface* for working with
/// agent
/// systems.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_APP_APPLICATION_HPP
#define ROSA_APP_APPLICATION_HPP
#include "rosa/app/AppSystem.hpp"
#include "rosa/support/types.hpp"
#include <iterator>
#include <memory>
#include <set>
/// Local helper macro to log and return a
/// \c rosa::app::Application::ErrorCode value.
///
/// Creates a debug message with the stringified value and returns the value.
///
/// \param Err \c rosa::app::Application::ErrorCode value to log and
/// return
#define APPRETERROR(Err) \
{ \
LOG_DEBUG(#Err); \
return Err; \
}
namespace rosa {
namespace app {
/// Defines the *application interface*.
///
/// \todo The classes \c rosa::app::AppSensor and \c
/// rosa::app::AppAgent share some common features in relation to their
/// *slave* role in the *application interface*. But their definitions are
/// completely independent. It could be investigated how to lift their common
/// parts into a new *application slave* class, which would serve as base for
/// both, to avoid code duplication.
class Application {
/// A system owned by \p this object.
///
/// \note The reference is kept in a \c std::shared_ptr because of the member
/// function \c rosa::app::Application::getSystem.
std::shared_ptr<AppSystem> System;
/// References to all *sensors* and *agents* created by \p this object.
std::set<AgentHandle> AppUnits;
public:
/// Errors that may be resulted by some of the member functions of the class.
enum struct ErrorCode {
NoError,
TypeMismatch,
NotSensor,
NotAgent,
NotUnit,
WrongPosition,
AlreadyHasSlave,
AlreadyHasMaster,
AlreadyHasValueStream,
UnsuitableExecutionPolicy
};
/// Returns a new instance of \c rosa::app::Application.
///
/// \param Name name of the underlying \c rosa::AppSystem
///
/// \return \c std::unique_ptr for the new instance of
/// \c rosa::app::Application with a new, empty \c rosa::AppSystem
static std::unique_ptr<Application> create(const std::string &Name) noexcept;
private:
/// Creates a new instance.
///
/// \note Private constructor restricts instantiation to member functions of
/// the class.
///
/// \param Name name of the underlying \c rosa::MessagingSystem
Application(const std::string &Name) noexcept;
public:
/// Destroys \p this object.
~Application(void) noexcept;
/// Returns a reference for the underlying \c rosa::MessagingSystem.
///
/// \note One cannot do much with a \c rosa::MessagingSystem currently, this
/// is for future use.
///
/// \return reference for the underlying \c rosa::MessagingSystem.
std::weak_ptr<MessagingSystem> getSystem(void) const noexcept;
private:
/// Creates a new *sensor* in the context of \p this object.
///
/// The new *sensor* handles master-input by \p MF.
///
/// \tparam MT type of master-input the new *sensor* handles
/// \tparam T type of data the new *sensor* operates on
///
/// \note Instantiation fails if any of the type arguments \p MT and \p T
/// is not an instance of \c rosa::app::AppTuple or \p T is \c
/// rosa::app::EmptyAppTuple.
///
/// \param Name name of the new *sensor*
/// \param MF function for the new *sensors* to process master-input
/// values with
/// \param F function for the new *sensor* to generate the next value with
/// during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::app::Application::registerSensorValues is used to
/// register an alternative simulation data source with \c
/// rosa::app::AppSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
/// \see \c rosa::app::AppSensor::AppSensor.
///
/// \return \c rosa::AgentHandle for the new *sensor*
template <
typename MT, typename T,
typename = std::enable_if<TypeListAllAppTuple<TypeList<MT, T>>::Value &&
!std::is_same<T, EmptyAppTuple>::value>>
AgentHandle createSensorImpl(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept;
public:
/// Creates a new *sensor* in the context of \p this object.
///
/// The new *sensor* does not receive master-input.
///
/// \tparam T type of data the new *sensor* operates on
///
/// \note Instantiation fails if type argument \p T is neither a built-in type
/// nor an instance of \c rosa::app::AppTuple with at least one element.
///
/// \param Name name of the new *sensor*
/// \param F function for the new *sensor* to generate the next value with
/// during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::app::Application::registerSensorValues is used to register
/// an alternative simulation data source with
/// \c rosa::app::AppSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
/// \see \c rosa::app::AppSensor::AppSensor.
///
/// \return \c rosa::AgentHandle for the new *sensor*
template <typename T, typename = std::enable_if_t<
TypeListContains<BuiltinTypes, T>::Value ||
(IsAppTuple<T>::Value &&
!std::is_same<T, EmptyAppTuple>::value)>>
AgentHandle createSensor(
const std::string &Name,
std::function<T(void)> &&F = [](void) { return T(); }) noexcept;
/// Creates a new *sensor* in the context of \p this object.
///
/// The new *sensor* handles master-input by \p MF.
///
/// \tparam MT type of master-input the new *sensor* handles
/// \tparam T type of data the new *sensor* operates on
///
/// \note The type arguments \p MT and \p T must be either all built-in types
/// or all instances of \c rosa::app::AppTuple. Moreover, \p T cannot be
/// \c rosa::app::EmptyAppTuple. Instantiation fails if these conditions
/// do not hold.
///
/// \param Name name of the new *sensor*
/// \param MF function for the new *sensors* to process master-input
/// values with \param F function for the new *sensor* to generate
/// the next value with during normal operation
///
/// \note \p F is not used during simulation, in which case
/// \c rosa::app::Application::registerSensorValues is used to
/// register an alternative simulation data source with \c
/// rosa::app::AppSensor::registerSimulationDataSource. One may
/// safely keep relying on the default value of \p F as long as only
/// simulation of the system is to be done.
///
/// \see \c rosa::app::AppSensor::AppSensor.
///
/// \return \c rosa::AgentHandle for the new *sensor*
template <typename MT, typename T,
typename = std::enable_if<
TypeListSubsetOf<TypeList<MT, T>, BuiltinTypes>::Value ||
(TypeListAllAppTuple<TypeList<MT, T>>::Value &&
!std::is_same<T, EmptyAppTuple>::value)>>
AgentHandle createSensor(
const std::string &Name, std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F = [](void) { return T(); }) noexcept;
private:
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* receives master-input by \p MF and produces
/// master-output.
///
/// \tparam MT type of master-input the new *agent* handles
/// \tparam T type of data the new *agent* outputs
/// \tparam Ts types of master-output the new *agent* produces
/// \tparam As types of inputs the new *agent* takes
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not an instance of \c rosa::app::AppTuple or
/// any of \p T and \p As... is \c rosa::app::EmptyAppTuple.
///
/// \param Name name of the new *agent*
/// \param MF function for the new *agent* to process master-input
/// values with \param F function for the new *agent* to process
/// input values and generate output with
///
/// \see \c rosa::app::AppAgent::AppAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListAllAppTuple<TypeList<MT, T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... && (!std::is_same<As, EmptyAppTuple>::value))>>
AgentHandle createAgentImpl(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
public:
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* neither receives master-input nor produces
/// master-output.
///
/// \tparam T type of data the new *agent* outputs
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p T and \p As... must be either all built-in
/// types or all instances of \c rosa::app::AppTuple. Moreover, none of
/// them can be \c rosa::app::EmptyAppTuple. Instantiation fails if
/// these conditions do not hold.
///
/// \param Name name of the new *agent*
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \see \c rosa::app::AppAgent::AppAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <typename T, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value ||
(TypeListAllAppTuple<TypeList<T, As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... && (!std::is_same<As, EmptyAppTuple>::value)))>>
AgentHandle
createAgent(const std::string &Name,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept;
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* receives master-input by \p MF but does not
/// produce master-output.
///
/// \tparam MT type of master-input the new *agent* handles
/// \tparam T type of data the new *agent* outputs
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p MT, \p T, and \p As... must be either all
/// built-in types or all instances of \c rosa::app::AppTuple. Moreover,
/// none of \p T and \p As... can be \c rosa::app::EmptyAppTuple.
/// Instantiation fails if these conditions do not hold.
///
/// \param Name name of the new *agent*
/// \param MF function for the new *agent* to process master-input
/// values with
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \see \c rosa::app::AppAgent::AppAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <typename MT, typename T, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<MT, T, As...>, BuiltinTypes>::Value ||
(TypeListAllAppTuple<TypeList<MT, T, As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... && (!std::is_same<As, EmptyAppTuple>::value)))>>
AgentHandle
createAgent(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept;
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* does not receive master-input but produces
/// master-output.
///
/// \tparam T type of data the new *agent* outputs
/// \tparam Ts types of master-output the new *agent* produces
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p T, \p Ts, and \p As... must be either all
/// built-in types or all instances of \c rosa::app::AppTuple. Moreover,
/// none of \p T and \p As... can be \c rosa::app::EmptyAppTuple.
/// Instantiation fails if these conditions do not hold.
///
/// \param Name name of the new *agent*
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \note \p F does not produce master-output for a given position if the
/// corresponding type is \c rosa::app::EmptyAppTuple. It is not
/// possible to disable master-output at any position by using built-in types.
///
/// \see \c rosa::app::AppAgent::AppAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <
typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<T, Ts..., As...>, BuiltinTypes>::Value ||
(TypeListAllAppTuple<TypeList<T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... && (!std::is_same<As, EmptyAppTuple>::value)))>>
AgentHandle
createAgent(const std::string &Name,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Creates a new *agent* in the context of \p this object.
///
/// The new *agent* receives master-input by \p MF and produces
/// master-output.
///
/// \tparam MT type of master-input the new *agent* handles
/// \tparam T type of data the new *agent* outputs
/// \tparam Ts types of master-output the new *agent* produces
/// \tparam As types of inputs the new *agent* takes
///
/// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either
/// all built-in types or all instances of \c rosa::app::AppTuple.
/// Moreover, none of \p T and \p As... can be \c
/// rosa::app::EmptyAppTuple. Instantiation fails if these conditions
/// do not hold.
///
/// \param Name name of the new *agent*
/// \param MF function for the new *agent* to process master-input
/// values with
/// \param F function for the new *agent* to process input values and
/// generate output with
///
/// \note \p F does not produce master-output for a given position if the
/// corresponding type is \c rosa::app::EmptyAppTuple. It is not
/// possible to disable master-output at any position by using built-in types.
///
/// \see \c rosa::app::AppAgent::AppAgent.
///
/// \return \c rosa::AgentHandle for the new *agent*
template <typename MT, typename T, typename... Ts, typename... As,
typename = std::enable_if_t<
TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
BuiltinTypes>::Value ||
(TypeListAllAppTuple<TypeList<MT, T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... && (!std::is_same<As, EmptyAppTuple>::value)))>>
AgentHandle createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Returns the current execution policy of the referred \p Unit
///
/// \see \c rosa::app::AppExecutionPolicy
///
/// \note The referred \p Unit is either *sensor* or *agent*.
///
/// \note The returned reference is valid only as long as \c
/// rosa::app::Application::setExecutionPolicy() is not called with the
/// *unit* referred by \p Unit and the *unit* is not destroyed.
///
/// \param Unit the *unit* whose execution policy is to be obtained
///
/// \return the \c rosa::app::AppExecutionPolicy from \p Unit if \p Unit
/// is valid
Optional<const AppExecutionPolicy &>
getExecutionPolicy(AgentHandle Unit) const noexcept;
/// Sets the current execution policy of the referred \p Unit to \p
/// ExecutionPolicy.
///
/// \see \c rosa::app::AppExecutionPolicy
///
/// \note The referred \p Unit is either *sensor* or *agent*.
///
/// \param Unit the *unit* whose execution policy is to be set
/// \param ExecutionPolicy the new execution policy for \p Unit
///
/// \return how successful setting \p ExecutionPolicy for \p Unit was
///
/// \note The function may return the following
/// \c rosa::app::Application::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotUnit` | Referred \p Unit is not valid
/// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit
ErrorCode setExecutionPolicy(
AgentHandle Unit,
std::unique_ptr<AppExecutionPolicy> &&ExecutionPolicy) noexcept;
/// Connects a *sensor* to an *agent* in the context of \p this object.
///
/// \param Agent the *agent* to connect to
/// \param Pos the index of slot of \p Agent to connect \p Sensor to
/// \param Sensor the *sensor* to connect
/// \param Description optional textual description of the connection
///
/// \return how successfull connecting \p Sensor to \p Agent at slot
/// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::app::Application::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Agent is not \c rosa::app::AppAgent
/// `NotSensor` | Referred \p Sensor is not \c rosa::app::AppSensor
/// `WrongPosition` | \p Pos is not a valid input position of \p Agent
/// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is
/// other thanthe output type of \p Sensor or expected master-input of \p
/// Sensor is other than master-output at position \p Pos of \p Agent if any
/// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave*
/// registered `AlreadyHasMaster` | \p Sensor already has a *master*
/// registered
ErrorCode connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description = "") noexcept;
/// Connectes two *agents* in the context of \p this object.
///
/// \param Master the *agent* to connect to
/// \param Pos the index of slot of \p Master to connect \p Slave to
/// \param Slave the *agent* to connect
/// \param Description optional textual description of the connection
///
/// \return how succesfull connecting \p Slave to \p Master at slot
/// index \p Pos was
///
/// \note The function may return the following
/// \c rosa::app::Application::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `NotAgent` | Referred \p Master or \p Slave is not \c
/// rosa::app::AppAgent `WrongPosition` | \p Pos is not a valid input
/// position of \p Master `TypeMismatch` | Expected input type at position
/// \p Pos of \p Master is other than the output type of \p Slave or expected
/// master-input of \p Slave is other than master-output at position \p Pos of
/// \p Master if any `AlreadyHasSlave` | \p Master at position \p Pos already
/// has a *slave* registered `AlreadyHasMaster` | \p Slave already has a
/// *master* registered
ErrorCode connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description = "") noexcept;
/// Initializes \c this object and others managed by \p this object
/// for setting up and performing simulation.
///
/// \see \c rosa::app::Application::registerSensorValues,
/// \c rosa::app::Application::simulate
///
/// Need to clear simulation data sources from all the *sensors*.
void initializeSimulation(void) noexcept;
public:
/// Registers a stream providing values for a *sensor* during
/// simulation.
///
/// \tparam Iterator type of iterator providing values for \p Sensor
/// \tparam T type that can be matched to values \p Sensor is operating on,
/// always use default!
///
/// \note Instantiation fails if type argument \p T is neither a built-in type
/// nor a tuple (i.e., can be converted to \c rosa::app::AppTuple).
///
/// \param Sensor the *sensor* to register values for
/// \param Start provides values for \p Sensor
/// \param End denotes the end of stream of values
/// \param Default value to be used when input stream is depleted
/// during simulation
///
/// \return how successful registering \p Source for \p Sensor
///
/// \note The function may return the following
/// \c rosa::app::Application::ErrorCode values:
/// `ErrorCode` | Comment
/// ----------- | -------
/// `NoError` | Success
/// `TypeMismatch` | \p T does not match the type of values
/// generated by \p Sensor
/// `NotSensor` | Referred \p Sensor is not \c
/// rosa::app::AppSensor
/// `AlreadyHasValueStream` | \p Sensor already has simulation data source set
template <typename Iterator, typename T = typename Iterator::value_type,
typename = std::enable_if_t<
TypeListContains<BuiltinTypes, T>::Value || IsTuple<T>::Value>>
ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default = {}) noexcept;
/// Performs the system contained by \p this object.
///
/// The function performs \p NumCycles cycle of simulation. In each
/// cycle, all the *agents* and *sensors* registered in \c
/// rosa::app::Application::AppUnits are trigged for
/// execution.
///
/// \param NumCycles number of cycles to perform
///
/// \pre All the *sensors* in the system contained by \p this object
/// generate their output from simulation data sources.
void simulate(const size_t NumCycles) const noexcept;
};
/// Anonymous namespace with helper features for implementing
/// \c rosa::app::Application, consider it private.
namespace {
/// Maps any type \p T to \c rosa::app::EmptyAppTuple.
template <typename T> struct MapToEmptyAppTuple { using Type = EmptyAppTuple; };
/// Convenience template alias for \c MapToEmptyAppTuple.
template <typename T> using empty_app_t = typename MapToEmptyAppTuple<T>::Type;
/// Converts a \c std::tuple of \c rosa::Optional built-in types into a
/// corresponding \c std::tuple of \c rosa::Optional with each actual value
/// wrapped in \c rosa::app::AppTuple.
///
/// \tparam Ts types of the values
/// \tparam S0 indices for accessing values in \p Values
///
/// \param Values the \c std::tuple of \c rosa::Optional with built-in values
///
/// \note The second argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values
/// with each actual value wrapped in \c rosa::app::AppTuple
///
/// \pre Statically, all type arguments \p Ts... are built-in types and the
/// provided indices \p S0... match the length of \p Ts...: \code
/// TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value &&
/// sizeof...(Ts) == sizeof...(S0)
/// \endcode
template <typename... Ts, size_t... S0>
std::tuple<Optional<AppTuple<Ts>>...>
wrapBuiltinInAppTuple(const std::tuple<Optional<Ts>...> &Values,
Seq<S0...>) noexcept {
STATIC_ASSERT((TypeListSubsetOf<TypeList<Ts...>, BuiltinTypes>::Value),
"not built-in types");
STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments");
return std::make_tuple(
std::get<S0>(Values)
? Optional<AppTuple<Ts>>(make_app_tuple<Ts>(*std::get<S0>(Values)))
: Optional<AppTuple<Ts>>()...);
}
} // End namespace
template <typename MT, typename T, typename>
AgentHandle
Application::createSensorImpl(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept {
AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F));
AppUnits.emplace(H);
return H;
}
template <typename T, typename>
AgentHandle Application::createSensor(const std::string &Name,
std::function<T(void)> &&F) noexcept {
auto EmptyMF = std::function<void(std::pair<EmptyAppTuple, bool>)>(
[](std::pair<EmptyAppTuple, bool>) {});
if constexpr (TypeListContains<BuiltinTypes, T>::Value) {
using OutputType = AppTuple<T>;
return createSensorImpl(
Name, std::move(EmptyMF),
std::function<OutputType(void)>(
[F{std::move(F)}](void) { return OutputType(F()); }));
} else if constexpr (IsAppTuple<T>::Value &&
!std::is_same<T, EmptyAppTuple>::value) {
return createSensorImpl(Name, std::move(EmptyMF), std::move(F));
} else {
ASSERT(false && "Unexpected type argument");
}
}
template <typename MT, typename T, typename>
AgentHandle
Application::createSensor(const std::string &Name,
std::function<void(std::pair<MT, bool>)> &&MF,
std::function<T(void)> &&F) noexcept {
if constexpr (TypeListSubsetOf<TypeList<MT, T>, BuiltinTypes>::Value) {
using MasterInputType = AppTuple<MT>;
using OutputType = AppTuple<T>;
return createSensorImpl(
Name,
std::function<void(std::pair<MasterInputType, bool>)>(
[MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
MF({std::get<0>(Arg.first), Arg.second});
}),
std::function<OutputType(void)>(
[F{std::move(F)}](void) { return OutputType(F()); }));
} else if constexpr (TypeListAllAppTuple<TypeList<MT, T>>::Value &&
!std::is_same<T, EmptyAppTuple>::value) {
return createSensorImpl(Name, std::move(MF), std::move(F));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
AgentHandle Application::createAgentImpl(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F));
AppUnits.emplace(H);
return H;
}
template <typename T, typename... As, typename>
AgentHandle Application::createAgent(
const std::string &Name,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept {
using NoMasterOutputType = std::tuple<Optional<empty_app_t<As>>...>;
auto EmptyMF =
std::function<NoMasterOutputType(std::pair<EmptyAppTuple, bool>)>(
[](std::pair<EmptyAppTuple, bool>) { return NoMasterOutputType(); });
if constexpr (TypeListSubsetOf<TypeList<T, As...>, BuiltinTypes>::Value) {
using OutputType = AppTuple<T>;
return createAgentImpl(
Name, std::move(EmptyMF),
std::function<
std::tuple<Optional<OutputType>, Optional<empty_app_t<As>>...>(
std::pair<AppTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<AppTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return std::tuple_cat(
wrapBuiltinInAppTuple(std::tuple(Result), seq_t<1>()),
NoMasterOutputType());
}));
} else if constexpr (TypeListAllAppTuple<TypeList<T, As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyAppTuple>::value))) {
return createAgentImpl(
Name, std::move(EmptyMF),
std::function<std::tuple<Optional<T>, Optional<empty_app_t<As>>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Result = F(Args...);
return std::tuple_cat(std::tuple(Result), NoMasterOutputType());
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename MT, typename T, typename... As, typename>
AgentHandle Application::createAgent(
const std::string &Name, std::function<void(std::pair<MT, bool>)> &&MF,
std::function<Optional<T>(std::pair<As, bool>...)> &&F) noexcept {
using NoMasterOutputType = std::tuple<Optional<empty_app_t<As>>...>;
if constexpr (TypeListSubsetOf<TypeList<MT, T, As...>, BuiltinTypes>::Value) {
using MasterInputType = AppTuple<MT>;
using OutputType = AppTuple<T>;
return createAgentImpl(
Name,
std::function<NoMasterOutputType(std::pair<MasterInputType, bool>)>(
[MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
MF({std::get<0>(Arg.first), Arg.second});
return NoMasterOutputType();
}),
std::function<
std::tuple<Optional<OutputType>, Optional<empty_app_t<As>>...>(
std::pair<AppTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<AppTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return std::tuple_cat(
wrapBuiltinInAppTuple(std::tuple(Result), seq_t<1>()),
NoMasterOutputType());
}));
} else if constexpr (TypeListAllAppTuple<TypeList<MT, T, As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyAppTuple>::value))) {
return createAgentImpl(
Name,
std::function<NoMasterOutputType(std::pair<MT, bool>)>(
[MF{std::move(MF)}](std::pair<MT, bool> Arg) {
MF(Arg);
return NoMasterOutputType();
}),
std::function<std::tuple<Optional<T>, Optional<empty_app_t<As>>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Result = F(Args...);
return std::tuple_cat(std::tuple(Result), NoMasterOutputType());
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename T, typename... Ts, typename... As, typename>
AgentHandle
Application::createAgent(const std::string &Name,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
if constexpr (TypeListSubsetOf<TypeList<T, Ts..., As...>,
BuiltinTypes>::Value) {
using MasterOutputType = std::tuple<Optional<AppTuple<Ts>>...>;
using OutputType = AppTuple<T>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<EmptyAppTuple, bool>)>(
[](std::pair<EmptyAppTuple, bool>) { return MasterOutputType(); }),
std::function<
std::tuple<Optional<OutputType>, Optional<AppTuple<Ts>>...>(
std::pair<AppTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<AppTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return wrapBuiltinInAppTuple(Result, seq_t<1 + sizeof...(Ts)>());
}));
} else if constexpr (TypeListAllAppTuple<TypeList<T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyAppTuple>::value))) {
using MasterOutputType = std::tuple<Optional<Ts>...>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<EmptyAppTuple, bool>)>(
[](std::pair<EmptyAppTuple, bool>) { return MasterOutputType(); }),
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Output = F(Args...);
return Output;
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
AgentHandle Application::createAgent(
const std::string &Name,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
if constexpr (TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
BuiltinTypes>::Value) {
using MasterInputType = AppTuple<MT>;
using MasterOutputType = std::tuple<Optional<AppTuple<Ts>>...>;
using OutputType = AppTuple<T>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<MasterInputType, bool>)>(
[MF{std::move(MF)}](std::pair<MasterInputType, bool> Arg) {
const auto Result = MF({std::get<0>(Arg.first), Arg.second});
return wrapBuiltinInAppTuple(Result, seq_t<sizeof...(Ts)>());
}),
std::function<
std::tuple<Optional<OutputType>, Optional<AppTuple<Ts>>...>(
std::pair<AppTuple<As>, bool>...)>(
[F{std::move(F)}](std::pair<AppTuple<As>, bool>... Args) {
const auto Result = F({std::get<0>(Args.first), Args.second}...);
return wrapBuiltinInAppTuple(Result, seq_t<1 + sizeof...(Ts)>());
}));
} else if constexpr (TypeListAllAppTuple<
TypeList<MT, T, Ts..., As...>>::Value &&
!std::is_same<T, EmptyAppTuple>::value &&
(true && ... &&
(!std::is_same<As, EmptyAppTuple>::value))) {
using MasterOutputType = std::tuple<Optional<Ts>...>;
return createAgentImpl(
Name,
std::function<MasterOutputType(std::pair<MT, bool>)>(
[MF{std::move(MF)}](std::pair<MT, bool> Arg) {
const auto Output = MF(Arg);
return Output;
}),
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>(
[F{std::move(F)}](std::pair<As, bool>... Args) {
const auto Output = F(Args...);
return Output;
}));
} else {
ASSERT(false && "Unexpected type arguments");
}
}
template <typename Iterator, typename T, typename>
Application::ErrorCode
Application::registerSensorValues(AgentHandle Sensor, Iterator &&Start,
const Iterator &End, T Default) noexcept {
// Get the type of values provided by \p Iterator.
STATIC_ASSERT((std::is_same<T, typename Iterator::value_type>::value),
"type mismatch");
// Make sure preconditions are met.
if (!System->isAppSensor(Sensor)) {
APPRETERROR(ErrorCode::NotSensor);
}
auto S = System->getAppSensor(Sensor);
ASSERT(S); // Sanity check.
if (S->simulationDataSourceIsSet()) {
APPRETERROR(ErrorCode::AlreadyHasValueStream);
}
if constexpr (TypeListContains<BuiltinTypes, T>::Value) {
if (S->OutputType != TypeToken<T>::Value) {
APPRETERROR(ErrorCode::TypeMismatch);
}
// Register input stream.
// \note Need to capture parameters by value so having local copies.
S->registerSimulationDataSource(std::function<AppTuple<T>(void)>([=
](void) mutable noexcept->AppTuple<T> {
if (Start != End) {
LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
<< "': " << PRINTABLE(*Start) << '\n';
return make_app_tuple<T>(*Start++);
} else {
LOG_TRACE_STREAM << "Providing default value for sensor '"
<< S->FullName << "': " << PRINTABLE(Default) << '\n';
return make_app_tuple<T>(Default);
}
}));
} else if constexpr (IsTuple<T>::Value) {
using TT = matching_app_tuple_t<T>;
if (std::is_same<T, EmptyAppTuple>::value || S->OutputType != TT::TT) {
APPRETERROR(ErrorCode::TypeMismatch);
}
// Register input stream.
// \note Need to capture parameters by value so having local copies.
S->registerSimulationDataSource(
std::function<TT(void)>([=](void) mutable noexcept->TT {
if (Start != End) {
const TT AV(*Start++);
LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName
<< "': " << PRINTABLE(AV) << '\n';
return AV;
} else {
static const TT AD(Default);
LOG_TRACE_STREAM << "Providing default value for sensor '"
<< S->FullName << "': " << PRINTABLE(AD) << '\n';
return AD;
}
}));
} else {
ASSERT(false && "Unexpected type argument");
}
return ErrorCode::NoError;
}
} // End namespace app
} // End namespace rosa
// Undef local macro if not used in the corresponding implementation.
#ifndef ROSA_LIB_APP_APPLICATION_CPP
#undef APPRETERROR
#endif
#endif // ROSA_APP_APPLICATION_HPP

File Metadata

Mime Type
text/x-c++
Expires
Sun, Apr 12, 11:42 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
287505
Default Alt Text
Application.hpp (37 KB)

Event Timeline