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 #include #include /// 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 System; /// References to all *sensors* and *agents* created by \p this object. std::set 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 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 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>::Value && !std::is_same::value>> AgentHandle createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&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 ::Value || (IsAppTuple::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function &&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 , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function)> &&MF, std::function &&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 >::Value && !std::is_same::value && (true && ... && (!std::is_same::value))>> AgentHandle createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&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 , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function(std::pair...)> &&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 , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&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, BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&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 , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&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 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 &&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 ::Value || IsTuple::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 struct MapToEmptyAppTuple { using Type = EmptyAppTuple; }; /// Convenience template alias for \c MapToEmptyAppTuple. template using empty_app_t = typename MapToEmptyAppTuple::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, BuiltinTypes>::Value && /// sizeof...(Ts) == sizeof...(S0) /// \endcode template std::tuple>...> wrapBuiltinInAppTuple(const std::tuple...> &Values, Seq) noexcept { STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); return std::make_tuple( std::get(Values) ? Optional>(make_app_tuple(*std::get(Values))) : Optional>()...); } } // End namespace template AgentHandle Application::createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); AppUnits.emplace(H); return H; } template AgentHandle Application::createSensor(const std::string &Name, std::function &&F) noexcept { auto EmptyMF = std::function)>( [](std::pair) {}); if constexpr (TypeListContains::Value) { using OutputType = AppTuple; return createSensorImpl( Name, std::move(EmptyMF), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (IsAppTuple::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); } else { ASSERT(false && "Unexpected type argument"); } } template AgentHandle Application::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = AppTuple; using OutputType = AppTuple; return createSensorImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); }), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (TypeListAllAppTuple>::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(MF), std::move(F)); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); AppUnits.emplace(H); return H; } template AgentHandle Application::createAgent( const std::string &Name, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; auto EmptyMF = std::function)>( [](std::pair) { return NoMasterOutputType(); }); if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using OutputType = AppTuple; return createAgentImpl( Name, std::move(EmptyMF), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, 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>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::move(EmptyMF), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgent( const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = AppTuple; using OutputType = AppTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); return NoMasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, 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>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF(Arg); return NoMasterOutputType(); }), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterOutputType = std::tuple>...>; using OutputType = AppTuple; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, 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>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = AppTuple; using MasterOutputType = std::tuple>...>; using OutputType = AppTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Result = MF({std::get<0>(Arg.first), Arg.second}); return wrapBuiltinInAppTuple(Result, seq_t()); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, 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>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Output = MF(Arg); return Output; }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template 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::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::Value) { if (S->OutputType != TypeToken::Value) { APPRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource(std::function(void)>([= ](void) mutable noexcept->AppTuple { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << PRINTABLE(*Start) << '\n'; return make_app_tuple(*Start++); } else { LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << PRINTABLE(Default) << '\n'; return make_app_tuple(Default); } })); } else if constexpr (IsTuple::Value) { using TT = matching_app_tuple_t; if (std::is_same::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([=](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 #include namespace rosa { namespace app { std::unique_ptr Application::create(const std::string &Name) noexcept { return std::unique_ptr(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 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 &&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 Application::getSystem(void) const noexcept { return std::weak_ptr(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