Page MenuHomePhorge

No OneTemporary

Size
47 KB
Referenced Files
None
Subscribers
None
diff --git a/include/rosa/app/Application.hpp b/include/rosa/app/Application.hpp
index e7a80ee..9943e8d 100644
--- a/include/rosa/app/Application.hpp
+++ b/include/rosa/app/Application.hpp
@@ -1,931 +1,939 @@
//===-- 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.
///
+ /// \note Simulation data sources are cleared at the end of the simulation.
+ ///
/// \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;
+ void simulate(const size_t NumCycles) noexcept;
+
+protected:
+ /// Clears simulation data sources from all the *sensors*.
+ ///
+ /// \see \c rosa::app::Application::initializeSimulation,
+ /// \c rosa::app::Application::simulate
+ void clearSimulationDataSources(void) 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
diff --git a/lib/app/Application.cpp b/lib/app/Application.cpp
index 38d3b8f..1bce142 100644
--- a/lib/app/Application.cpp
+++ b/lib/app/Application.cpp
@@ -1,224 +1,233 @@
//===-- app/Application.cpp -------------------------------------*- 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 app/Application.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2020
///
/// \brief Implementation for rosa/app/Application.hpp.
///
//===----------------------------------------------------------------------===//
#define ROSA_LIB_APP_APPLICATION_CPP // For including helper macros.
#include "rosa/app/Application.hpp"
#include <algorithm>
#include <sstream>
namespace rosa {
namespace app {
std::unique_ptr<Application>
Application::create(const std::string &Name) noexcept {
return std::unique_ptr<Application>(new Application(Name));
}
Application::Application(const std::string &Name) noexcept
: System(AppSystem::createSystem(Name)) {
LOG_TRACE("Application for '" + System->name() + "' is created.");
}
Application::~Application(void) noexcept {
// \c rosa::app::Application::System is not used outside, just clean it.
for (auto U : AppUnits) {
System->destroyAgent(U);
}
// \note \c System will be marked clean by SystemImpl::~SystemImpl.
LOG_TRACE("Application for '" + System->name() +
"' prepared for destruction.");
}
Optional<const AppExecutionPolicy &>
Application::getExecutionPolicy(AgentHandle Unit) const noexcept {
if (System->isAppSensor(Unit)) {
return {System->getAppSensor(Unit)->executionPolicy()};
} else if (System->isAppAgent(Unit)) {
return {System->getAppAgent(Unit)->executionPolicy()};
} else {
return {};
}
}
Application::ErrorCode Application::setExecutionPolicy(
AgentHandle Unit,
std::unique_ptr<AppExecutionPolicy> &&ExecutionPolicy) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Setting execution policy of " << System->unwrapAgent(Unit).FullName
<< " to ";
if (ExecutionPolicy) {
Trace << "'" << ExecutionPolicy->dump() << "'\n";
} else {
Trace << "[]\n";
APPRETERROR(ErrorCode::UnsuitableExecutionPolicy);
}
if (System->isAppSensor(Unit)) {
const bool Success = System->getAppSensor(Unit)->setExecutionPolicy(
std::move(ExecutionPolicy));
if (!Success) {
APPRETERROR(ErrorCode::UnsuitableExecutionPolicy);
} else {
return ErrorCode::NoError;
}
} else if (System->isAppAgent(Unit)) {
const bool Success = System->getAppAgent(Unit)->setExecutionPolicy(
std::move(ExecutionPolicy));
if (!Success) {
APPRETERROR(ErrorCode::UnsuitableExecutionPolicy);
} else {
return ErrorCode::NoError;
}
} else {
APPRETERROR(ErrorCode::NotUnit);
}
}
Application::ErrorCode
Application::connectSensor(AgentHandle Agent, const size_t Pos,
AgentHandle Sensor,
const std::string &Description) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Establishing connection";
if (!Description.empty()) {
Trace << " '" << Description << "'";
}
Trace << " between '" << System->unwrapAgent(Sensor).FullName << "' and '"
<< System->unwrapAgent(Agent).FullName << "'\n";
// Make sure preconditions are met.
if (!System->isAppAgent(Agent)) {
APPRETERROR(ErrorCode::NotAgent);
} else if (!System->isAppSensor(Sensor)) {
APPRETERROR(ErrorCode::NotSensor);
}
auto A = System->getAppAgent(Agent);
auto S = System->getAppSensor(Sensor);
ASSERT(A && S); // Sanity check.
if (Pos >= A->NumberOfInputs) {
APPRETERROR(ErrorCode::WrongPosition);
} else if (A->inputType(Pos) != S->OutputType ||
(!emptyToken(A->masterOutputType(Pos)) &&
A->masterOutputType(Pos) != S->MasterInputType)) {
APPRETERROR(ErrorCode::TypeMismatch);
} else if (A->slave(Pos)) {
APPRETERROR(ErrorCode::AlreadyHasSlave);
} else if (S->master()) {
APPRETERROR(ErrorCode::AlreadyHasMaster);
}
// Do register.
A->registerSlave(Pos, {Sensor});
S->registerMaster({Agent});
return ErrorCode::NoError;
}
Application::ErrorCode
Application::connectAgents(AgentHandle Master, const size_t Pos,
AgentHandle Slave,
const std::string &Description) noexcept {
// Generate trace log.
auto &Trace = LOG_TRACE_STREAM;
Trace << "Establishing connection";
if (!Description.empty()) {
Trace << " '" << Description << "'";
}
Trace << " between '" << System->unwrapAgent(Slave).FullName << "' and '"
<< System->unwrapAgent(Master).FullName << "'\n";
// Make sure preconditions are met.
if (!(System->isAppAgent(Master) && System->isAppAgent(Slave))) {
APPRETERROR(ErrorCode::NotAgent);
}
auto M = System->getAppAgent(Master);
auto S = System->getAppAgent(Slave);
ASSERT(M && S); // Sanity check.
if (Pos >= M->NumberOfInputs) {
APPRETERROR(ErrorCode::WrongPosition);
} else if (M->inputType(Pos) != S->OutputType ||
(!emptyToken(M->masterOutputType(Pos)) &&
M->masterOutputType(Pos) != S->MasterInputType)) {
APPRETERROR(ErrorCode::TypeMismatch);
} else if (M->slave(Pos)) {
APPRETERROR(ErrorCode::AlreadyHasSlave);
} else if (S->master()) {
APPRETERROR(ErrorCode::AlreadyHasMaster);
}
// Do register.
M->registerSlave(Pos, {Slave});
S->registerMaster({Master});
return ErrorCode::NoError;
}
std::weak_ptr<MessagingSystem> Application::getSystem(void) const noexcept {
return std::weak_ptr<MessagingSystem>(System);
}
void Application::initializeSimulation(void) noexcept {
- LOG_INFO_STREAM << "Initializing simulation for " << System->name()
- << ". Clearing all data sources.\n";
+ LOG_INFO_STREAM << "Initializing simulation for " << System->name() << ".\n";
// Clear simulation data sources from sensors.
- for (auto U : AppUnits) {
- if (auto S = System->getAppSensor(U)) {
- S->clearSimulationDataSource();
- }
- }
+ clearSimulationDataSources();
}
-void Application::simulate(const size_t NumCycles) const noexcept {
+void Application::simulate(const size_t NumCycles) noexcept {
+ LOG_INFO_STREAM << "Simulating " << System->name() << "...\n";
DEBUG(for (auto H
: AppUnits) {
std::stringstream Message;
Message << System->unwrapAgent(H).FullName << " is an App "
<< " " << (System->isAppSensor(H) ? "Sensor" : "Agent");
if (System->isAppSensor(H))
Message << " with it's data source "
<< (!System->getAppSensor(H)->simulationDataSourceIsSet()
? "not "
: "set");
Message << '\n';
LOG_TRACE_STREAM << Message.str();
});
ASSERT(
std::all_of(AppUnits.begin(), AppUnits.end(), [&](const AgentHandle &H) {
return System->isAppAgent(H) ||
System->isAppSensor(H) &&
System->getAppSensor(H)->simulationDataSourceIsSet();
}));
for (size_t I = 1; I <= NumCycles; ++I) {
- LOG_TRACE("Simulation cycle: " + std::to_string(I));
+ LOG_INFO("Simulation cycle: " + std::to_string(I));
for (auto U : AppUnits) {
U.sendMessage(Message::create(atoms::Trigger::Value));
}
}
+ LOG_INFO_STREAM << "Simulation done.\n";
+
+ // Clear simulation data sources from sensors.
+ clearSimulationDataSources();
+}
+
+void Application::clearSimulationDataSources(void) noexcept {
+ LOG_INFO_STREAM << "Clearing data sources in " << System->name() << ".\n";
+ for (auto U : AppUnits) {
+ if (auto S = System->getAppSensor(U)) {
+ S->clearSimulationDataSource();
+ }
+ }
}
} // End namespace app
} // End namespace rosa

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 30, 11:29 AM (21 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
242240
Default Alt Text
(47 KB)

Event Timeline