Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F562033
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
47 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 30, 11:29 AM (21 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
242240
Default Alt Text
(47 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment