diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp index d50396e..43222e4 100755 --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,177 +1,191 @@ //===-- examples/deluxe-interface/deluxe-interface.cpp ----------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/deluxe-interface/deluxe-interface.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief A simple example on the \c rosa::deluxe::DeluxeContext and related /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include #include #include using namespace rosa; using namespace rosa::deluxe; using namespace rosa::terminal; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; /// Helper function creating a deluxe agent for logging and forwarding values. /// /// Received values are dumped to \c LOG_INFO_STREAM and then returned as /// result. /// /// \tparam T type of values to handle /// /// \param C the deluxe context to create the agent in /// \param Name name of the new agent /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &C, const std::string &Name) { using handler = DeluxeAgent::D; using result = Optional; return C->createAgent( Name, handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return {I.first}; })); } int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << "deluxe-interface example" << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create("Deluxe"); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. AgentHandle IntSensor = C->createSensor("IntSensor"); AgentHandle FloatSensor = C->createSensor("FloatSensor"); + // Check and set execution policy for sensors. + LOG_INFO("Execution policies for sensors."); + + LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); + C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2)); + C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2)); + LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); + // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle IntAgent = createLowLevelAgent(C, "IntAgent"); AgentHandle FloatAgent = createLowLevelAgent(C, "FloatAgent"); + // Set execution policies for low-level agents. + LOG_INFO("Setting Execution policies for low-level agents."); + + C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0})); + C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0})); + // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel"); C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); // The new agent logs its input values and results in the the sum of them. AgentHandle SumAgent = C->createAgent( "Sum Agent", DeluxeAgent::D( [](std::pair I1, std::pair I2) -> Optional { LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" << (I1.second ? "" : "") << " int value: " << I1.first << "\n" << (I2.second ? "" : "") << " float value: " << I2.first << "\n******\n"; return {I1.first + I2.first}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(SumAgent, 0, IntAgent, "Int Agent Channel"); C->connectAgents(SumAgent, 1, FloatAgent, "Float Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a log stream. // LOG_INFO("Create a logger agent."); // The agent logs each new input value and produces nothing. AgentHandle LoggerAgent = C->createAgent("Logger Agent", DeluxeAgent::D( [](std::pair Sum) -> Optional { if (Sum.second) { LOG_INFO_STREAM << "Result: " << Sum.first << "\n"; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Create some vectors and register them for their corresponding sensors. // std::vector IntValues(NumberOfSimulationCycles); std::generate(IntValues.begin(), IntValues.end(), [i = 0](void) mutable { return ++i; }); C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end()); std::vector FloatValues(NumberOfSimulationCycles); std::generate(FloatValues.begin(), FloatValues.end(), [f = 0.5f](void) mutable { f += 0.3f; return std::floor(f) + 0.5f; }); C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp index 0e83a7c..48555ab 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,673 +1,705 @@ //===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAgent.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEAGENT_HPP #define ROSA_DELUXE_DELUXEAGENT_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" +#include "rosa/deluxe/DeluxeExecutionPolicy.h" #include /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. /// /// \param N name suffix to use #define DAHANDLERNAME(N) handleSlave_##N /// Defines member functions for handling messages from *slaves* in /// \c rosa::deluxe::DeluxeAgent. /// /// \see \c DeluxeAgentInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DAHANDLERDEFN(T, N) \ void DAHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \ saveInput(SlaveId, Value); \ } /// Convenience macro for \c DAHANDLERDEFN with identical arguments. /// /// \see \c DAHANDLERDEFN /// /// This macro can be used instead of \c DAHANDLERDEFN if the actual value of /// \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DAHANDLERDEF(T) DAHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DAHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super /// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN. /// /// \see \c DAHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// -/// \invariant All input-related container objects have a size matching -/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding -/// entry for each input. Types of input values are consistent throughout all -/// the input-related containers. No *slave* is registered at more than one -/// input position. *Slave* registrations and corresponding reverse lookup -/// information are consistent. +/// \invariant There is a compatible *execution policy* set, all input-related +/// container objects have a size matching \c +/// rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding entry +/// for each input. Types of input values are consistent throughout all the +/// input-related containers. No *slave* is registered at more than one input +/// position. *Slave* registrations and corresponding reverse lookup information +/// are consistent. /// /// \see Definition of \c rosa::deluxe::DeluxeAgent::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeAgent : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeAgent /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; + /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of + /// \c this object. + std::unique_ptr ExecutionPolicy; + public: /// Template alias for function objects used to process input and generate /// output for \c rosa::deluxe::DeluxeAgent. /// /// The output generated by the function is optional as an agent may decide /// not to output anything at some situation. /// /// \note The function used for \c D is to be \c noexcept. /// /// \tparam T type of output /// \tparam As types of input values template using D = std::function(std::pair...)>; /// The type of values produced by \p this object. /// /// That is the type of values \p this object sends to its *master*. /// /// \see \c rosa::deluxe::DeluxeAgent::master const TypeNumber OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; private: /// Types of input values produced by *slaves* of \p this object. /// /// \note The \c rosa::TypeNumber values stored here match the corresponding /// values in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a type in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. const std::vector InputTypes; /// Indicates whether any particular input value has been changed since the /// last trigger received from the system. /// /// All the flags are reset to \c false upon handling a trigger and then set /// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new /// input value in \c rosa::deluxe::DeluxeAgent::InputValues. /// /// \note The position of a flag in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. std::vector InputChanged; /// Stores the actual input values. /// /// \note The types of stored values match the corresponding /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \note The position of a value in the \c rosa::AbstractTokenizedStorage /// indicates which argument of \p this object's processing function it is. /// See also \c rosa::deluxe::DeluxeAgent::D. const std::unique_ptr InputValues; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeAgent. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c rosa::deluxe::DeluxeAgent::FP using H = std::function; /// Handles trigger from the system. /// /// The actual function processing *slave* inputs and generating optional /// output to *master* is captured in a lambda expression that is in turn /// wrapped in a \c std::function object. The lambda expression calls the /// processing function with the actual input data and sends its result -- if /// any -- to *master* by calling \c rosa::deluxe::DeluxeAgent::sendToMaster. /// Also, all the flags stored in \c rose::deluxe::DeluxeAgent::InputChanged /// are reset when the current input values are processed. The function /// \c rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the /// function object. /// /// \see \c rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunction const H FP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a /// given moment. Optional Master; /// The *slaves* sending input to \p this object. /// /// \note The position of a *slave* in the \c std::vector indicates which /// argument of \p this object's processing function it belongs to. See also /// \c rosa::deluxe::DeluxeAgent::D. /// /// \note *Slaves* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeAgent instance does have input positions without /// any *slave* associated to them. /// /// \note Reverse lookup information is maintained in /// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with /// the *slaves* stored here. std::vector> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; /// Tells whether types \p As... match the input types of \p this object. /// /// \tparam As types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes /// /// \return if types \p As... match \c rosa::TypeNumber values stored in /// \c rosa::deluxe::DeluxeAgent::InputTypes template bool inputTypesMatch(void) const noexcept; /// Gives an \c std::tuple containing the current input values and their /// change flags so that they can be used for the processing function. /// /// \tparam As types of the input values /// \tparam S0 indices for accessing input values and their change flags /// /// \note The only argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return current input values and their change flags prepared for invoking /// the processing function with them /// /// \pre The type arguments \p As... match the input types of \p this object /// and the provided indices \p S0... constitute a proper sequence for /// accessing input values and their change flags: \code /// inputTypesMatch() && sizeof...(As) == sizeof...(S0) /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; /// Invokes a processing function matching the output and input types of /// \p this object with actual arguments provided in a \c std::tuple. /// /// \note \p Args providing the actual arguments for \p F is to be created by /// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs. /// /// \tparam T output type of the processing function /// \tparam As types of inputs for the processing function /// \tparam S0 indices starting with `0` for extracting actual arguments from /// \p Args /// /// \param F the processing function to invoke /// \param Args the actual arguments to invoke \p F with /// /// \note The last argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return the result of \p F for actual arguments \p Args /// /// \pre The provided sequence of indices \p S0... constitutes a proper /// sequence for extracting all actual arguments for /// \p F from \p Args: \code /// sizeof...(As) == sizeof...(S0) /// \endcode template static Optional invokeWithTuple(D F, std::tuple...> Args, Seq) noexcept; /// Wraps a processing function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeAgent::FP /// /// \note The function cannot be const qualified because the lambda /// expression defined in it needs to capture \p this object by a non-const /// reference /// /// \tparam T type of output /// \tparam As types of input values /// /// \param F function processing inputs and generating output /// /// \pre Template arguments \p T and \p As... match the corresponding /// types \p this object was created with: \code /// OutputType == TypeNumberOf::Value && inputTypesMatch() /// \endcode template H triggerHandlerFromProcessingFunction(D &&F) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \todo Enforce F does not potentially throw exception. /// /// \tparam T type of output of \p F /// \tparam As types of input values of \p F /// /// \note Instantiation fails if any of the type arguments \p T and \p As... /// is not a built-in type. /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F function to process input values and generate output with /// /// \pre Statically, all of the type arguments \p T and \p As... is a /// built-in type: \code /// TypeListSubsetOf, BuiltinTypes>::Value /// \endcode Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::AgentKind: \code /// Kind == rosa::deluxe::atoms::AgentKind /// \endcode template , BuiltinTypes>::Value>> DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; + /// Returns the current execution policy of \p this object. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this + /// object is not destroyed. + /// + /// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy + const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; + + /// Sets the current execution policy of \p this object to \p EP. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note \p EP is set only if it can handle \p this object. + /// + /// \param EP the new execution policy for \p this object + /// + /// \return if \p EP was successfully set for \p this object. + bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; + /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \param _Master the *master* to register /// /// \pre \p _Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Tells the type of values consumed from the *slave* at a position. /// /// That is the type of values \p this object expect to be sent to it by its /// *slave* registered at position \p Pos. /// /// \see \c rosa::deluxe::DeluxeAgent::slave /// /// \param Pos position of *slave* /// /// \return \c rosa::TypeNumber representing the type of values consumed from /// the *slave* at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode TypeNumber inputType(const size_t Pos) const noexcept; /// The *slave* of \p this object registered at a position, if any. /// /// \see \c rosa::deluxe::DeluxeAgent::registerSlave /// /// \param Pos position of *slave* /// /// \return the *slave* registered for \p this object at position \p Pos /// /// \pre \p Pos is a valid index of input: \code /// Pos < NumberOfInputs /// \endcode Optional slave(const size_t Pos) const noexcept; /// Registers a *slave* for \p this object at a position. /// /// The new *slave* is registered by overwriting the reference to any already /// registered *slave* at position \p Pos. One can clear the registered /// reference by passing an *empty* \c rosa::Optional object as actual /// argument. If \p Slave is already registered for another position, the /// other position gets cleared. /// /// \note The role of the referred *slave* is validated by checking its /// *kind*. /// /// \note The type of values produced by the referred *slave* is validated by /// matching its `OutputType` against the corresponding value in /// \c rosa::deluxe::DeluxeAgent::InputTypes. /// /// \param Pos position to register \p Slave at /// \param Slave the *slave* to register /// /// \pre \p Pos is a valid index of input, \p Slave is empty or of kind /// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind, /// and \p Slave -- if not empty -- produces values of types matching the /// expected input type at position \p Pos: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos]) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == /// InputTypes[Pos])) /// \endcode void registerSlave(const size_t Pos, const Optional Slave) noexcept; /// Tells the position of a registered *slave*. /// /// \param Slave \c rosa::AgentHandle for the *slave* to check /// /// \return position of \p Slave if it is registered and found, /// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise. size_t positionOfSlave(AgentHandle Slave) const noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Value value to send /// /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void sendToMaster(const T &Value) noexcept; /// Generates the next output by processing current input values upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeAgent::FP. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from a *slave*. /// /// The function stores \p Value in \c rosa::deluxe::DeluxeAgent::InputValues /// at the position associated to \p Id in /// \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding /// flag in \c rosa::deluxe::DeluxeAgent::InputChanged. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* /// \param Value the input value to store /// /// \pre The *slave* with \p Id is registered and the input from it is /// expected to be of type \p T: \code /// SlaveIds.find(Id) != SlaveIds.end() && /// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value /// \endcode template void saveInput(id_t Id, T Value) noexcept; /// \defgroup DeluxeAgentInputHandlers Input handlers of rosa::deluxe::DeluxeAgent /// /// Definition of member functions handling messages from *slaves* with /// different types of input /// /// A *master* generally needs to be prepared to deal with values of any /// built-in type. Each type requires a separate message handler, which are /// implemented by these functions. The functions instantiate /// \c rosa::deluxe::DeluxeAgent::saveInput with the proper template argument /// and pass the content of the message on for processing. /// /// \note The member functions in this group are defined by \c DAHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DAHANDLERDEF(AtomValue) DAHANDLERDEF(int16_t) DAHANDLERDEF(int32_t) DAHANDLERDEF(int64_t) DAHANDLERDEF(int8_t) DAHANDLERDEFN(long double, long_double) DAHANDLERDEFN(std::string, std__string) DAHANDLERDEF(uint16_t) DAHANDLERDEF(uint32_t) DAHANDLERDEF(uint64_t) DAHANDLERDEF(uint8_t) DAHANDLERDEF(unit_t) DAHANDLERDEF(bool) DAHANDLERDEF(double) DAHANDLERDEF(float) /// @} }; /// Anonymous namespace with implementation for /// \c rosa::deluxe::DeluxeAgent::inputTypesMatch, consider it private. namespace { /// Template \c struct whose specializations provide a recursive implementation /// for \c rosa::deluxe::DeluxeAgent::inputTypesMatch. /// /// \note Matching a list of types \p As... against a \c std::vector of /// \c rosa::TypeNumber values, \c InputTypes, like \code /// bool match = InputTypesMatchImpl::f(InputTypes, 0); /// \endcode /// /// \tparam As types to match template struct InputTypesMatchImpl; /// Template specialization for the general case, when at least one type is to /// be matched. /// /// \tparam A first type to match /// \tparam As further types to match template struct InputTypesMatchImpl { /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values /// stored in \p InputTypes starting at position \p Pos. /// /// The function has got a recursive implementation: it matches the first /// type \p A against \c rosa::TypeNumber at position \p Pos of \p /// InputTypes, then further types \p As.. are matched recursively starting /// at position \c (Pos + 1). /// /// \param InputTypes container of \c rosa::TypeNumber values to match /// types against /// \param Pos position in \p InputTypes to start matching at /// /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored /// in \p InputTypes starting at position \p Pos static bool f(const std::vector &InputTypes, size_t Pos) noexcept { return Pos < InputTypes.size() && TypeNumberOf::Value == InputTypes[Pos] && InputTypesMatchImpl::f(InputTypes, Pos + 1); } }; /// Template specialization for the terminal case, when no type remains to /// check. template <> struct InputTypesMatchImpl<> { /// Tells whether \p Pos is the number of values stored in \p InputTypes. /// /// In this terminal case, there is no more types to matchi because all the /// types are supposed to be already matched successfully. The whole list of /// types already matched is a complete match if it covers all values in /// \p InputTypes. That is true if \p Pos points exactly to the end of /// \p InputTypes. /// /// \param InputTypes container of \c rosa::TypeNumber values to match /// types against /// \param Pos position in \p InputTypes to start matching at /// /// \return if \p Pos is the number of values stored in \p InputTypes static bool f(const std::vector &InputTypes, size_t Pos) noexcept { return Pos == InputTypes.size(); } }; } // End namespace template bool DeluxeAgent::inputTypesMatch(void) const noexcept { return InputTypesMatchImpl::f(InputTypes, 0); } template std::tuple...> DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch; ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0)); return std::make_tuple( std::make_pair(*static_cast(InputValues->pointerTo(S0)), InputChanged[S0])...); } template Optional DeluxeAgent::invokeWithTuple( D F, std::tuple...> Args, Seq) noexcept { ASSERT(sizeof...(As) == sizeof...(S0)); return F(std::get(Args)...); } template DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunction(D &&F) noexcept { // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch // inside \c ASSERT because of the comma in its template argument list. auto MFP = &DeluxeAgent::inputTypesMatch; ASSERT(OutputType == TypeNumberOf::Value && (this->*MFP)()); return [ this, F ]() noexcept { - using Indices = typename GenSeq::Type; - auto Args = prepareCurrentInputs(Indices()); - std::fill(InputChanged.begin(), InputChanged.end(), false); - Optional R = invokeWithTuple(F, Args, Indices()); - if (R) { - sendToMaster(*R); + // Call the processing function only if \p ExecutionPolicy allows. + if (ExecutionPolicy->shouldProcess(InputChanged)) { + using Indices = typename GenSeq::Type; + auto Args = prepareCurrentInputs(Indices()); + std::fill(InputChanged.begin(), InputChanged.end(), false); + Optional R = invokeWithTuple(F, Args, Indices()); + if (R) { + sendToMaster(*R); + } } }; } template DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DAHANDLERREF(AtomValue), DAHANDLERREF(int16_t), DAHANDLERREF(int32_t), DAHANDLERREF(int64_t), DAHANDLERREF(int8_t), DAHANDLERREF(long_double), DAHANDLERREF(std__string), DAHANDLERREF(uint16_t), DAHANDLERREF(uint32_t), DAHANDLERREF(uint64_t), DAHANDLERREF(uint8_t), DAHANDLERREF(unit_t), DAHANDLERREF(bool), DAHANDLERREF(double), DAHANDLERREF(float)), - OutputType(TypeNumberOf::Value), - NumberOfInputs(sizeof...(As)), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), + OutputType(TypeNumberOf::Value), NumberOfInputs(sizeof...(As)), InputTypes({TypeNumberOf::Value...}), InputChanged(NumberOfInputs, false), InputValues(new TokenizedStorage()), FP(triggerHandlerFromProcessingFunction(std::move(F))), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); LOG_TRACE("DeluxeAgent is created."); ASSERT(inv()); } template void DeluxeAgent::sendToMaster(const T &Value) noexcept { ASSERT(inv() && OutputType == TypeNumberOf::Value); // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); } + ASSERT(inv()); } template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); size_t Pos = SlaveIds.at(Id); *static_cast(InputValues->pointerTo(Pos)) = Value; InputChanged[Pos] = true; ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa #undef DAHANDLEREF #undef DAHANDLEDEF #undef DAHANDLEDEFN #undef DAHANDLENAME #endif // ROSA_DELUXE_DELUXEAGENT_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index 29794a1..6ee0c03 100755 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,294 +1,336 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. class DeluxeContext { /// 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::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; 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 + AlreadyHasValueStream, + UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem 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 DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(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; /// Creates a new *sensor* in the context of \p this object. /// /// \tparam T type of data the new *sensor* operates on /// /// \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::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::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. /// /// \return \c rosa::AgentHandle for the new *sensor* template AgentHandle createSensor(const std::string &Name, DeluxeSensor::D &&F = [](void) { return T(); }) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \return \c rosa::AgentHandle for the new *agent* template AgentHandle createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept; + /// Returns the current execution policy of the referred \p Unit + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The referred \p Unit is either *sensor* or *agent*. + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeContext::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::deluxe::DeluxeExecutionPolicy 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::deluxe::DeluxeExecutionPolicy + /// + /// \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::deluxe::DeluxeContext::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; + /// Connectes 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::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `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 than the output type of \p Sensor /// `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::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `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 /// `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::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; /// Registers a stream providing values for a *sensor* during simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor /// \tparam T type of values \p Sensor is operating on, always use default! /// /// \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::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `TypeMismatch` | \p Sensor generates values of a type other than \p T /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set template 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::deluxe::DeluxeContext::DeluxeUnits are trigged for execution. /// /// \param NumCycles number of cycles to perform /// /// \pre All the *sensors* in the system contained by \p this object generate /// their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; template AgentHandle DeluxeContext::createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(F)); DeluxeUnits.emplace(H); return H; } template DeluxeContext::ErrorCode DeluxeContext::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->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. if (S->OutputType != TypeNumberOf::Value) { DCRETERROR(ErrorCode::TypeMismatch); } else if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource( DeluxeSensor::D([=](void) mutable noexcept { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << *Start << '\n'; return *Start++; } else { LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << Default << '\n'; return Default; } })); return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/deluxe/DeluxeExecutionPolicy.h b/include/rosa/deluxe/DeluxeExecutionPolicy.h new file mode 100644 index 0000000..caaad92 --- /dev/null +++ b/include/rosa/deluxe/DeluxeExecutionPolicy.h @@ -0,0 +1,195 @@ +//===-- rosa/deluxe/DeluxeExecutionPolicy.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/deluxe/DeluxeExecutionPolicy.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Public interface of *execution policies* in the *deluxe interface*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H +#define ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H + +#include "rosa/core/AgentHandle.hpp" + +#include +#include +#include +#include + +namespace rosa { +namespace deluxe { + +// Forward declaration of DeluxeSystem. Do not include the corresponding header +// in this file because of cyclic dependency. +class DeluxeSystem; + +/// *Execution policy* that controls how *agents* and *sensors* call their +/// processing functions. +/// +/// An *execution policy* can be applied to a deluxe *unit* only if \c +/// deluxe::rosa::DeluxeExecutionPolicy::canHandle() allows it. Each deluxe +/// *unit* must have a compatible *execution policy* associated to it, and the +/// *unit* queries \c rosa::deluxe::DeluxeExecutionPolicy::shouldProcess() on each +/// triggering and calls its processing funtion only if it is allowed by the +/// *execution policy*. +/// +/// \see rosa::deluxe::DeluxeExecutionPolicy::decimation() +/// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAll() +/// \see rosa::deluxe::DeluxeExecutionPolicy::awaitAny() +/// +/// \todo Extend the interface with query functions about what kind of +/// execution policy is behind the interface. This can be done in relation +/// to the existing factory functions; for example, if the actual object is +/// decimation and with what rate. +class DeluxeExecutionPolicy { +protected: + + /// Protected constructor, only implementations can instantiate the class. + DeluxeExecutionPolicy(void) noexcept = default; + +private: + /// No instance can be copy-constructed, move-constructed, copied, and moved. + /// + ///@{ + DeluxeExecutionPolicy(const DeluxeExecutionPolicy &) = delete; + DeluxeExecutionPolicy(DeluxeExecutionPolicy &&) = delete; + DeluxeExecutionPolicy &operator=(const DeluxeExecutionPolicy &) = delete; + DeluxeExecutionPolicy &operator=(DeluxeExecutionPolicy &&) = delete; + ///@} + +public: + /// Virtual destructor for subclasses. + virtual ~DeluxeExecutionPolicy(void) noexcept = default; + + /// Creates an *execution policy* that allows execution with decimation of + /// triggering. + /// + //// *Decimation* can handle both *agents* and *sensors*. + /// Processing functions are executed only on every \p D th + /// triggering. In the case of *sensors* in simulation, the simulation data + /// source is read on each triggering as it provides values with respect to + /// the highest execution frequency, but output is generated by the *sensor* + /// only on every \p D th triggering. + /// + /// \note A rate of \c 0 is allowed as actual argument and is treated as rate + /// \c 1 (i.e., execute processing functions on each triggering). + /// + /// \param D the rate of *decimation* + /// + /// \return an *execution policy* implementing *decimation* with rate \p D + static std::unique_ptr decimation(const size_t D); + + /// Creates an *execution policy* that allows execution only if all defined + /// *slave* positions has new input. + /// + /// *Await all* can handle only *agents* and only if the particular *agent* + /// has at least as many *slave* positions as the largest position defined in + /// \p S. Processing functions are executed only if new input has been + /// received for all defined *slave* positions. + /// + /// \param S set of *slave* positions to await input from + /// + /// \return an *execution policy* implementing *awaiting all* input from set + /// \p S + static std::unique_ptr + awaitAll(const std::set &S); + + /// Creates an *execution policy* that allows execution if any of the defined + /// *slave* positions has new input. + /// + /// *Await any* can handle only *agents* and only if the particular *agent* + /// has at least as many *slave* positions as the largest position defined in + /// \p S. Processing functions are executed if new input has been received for + /// any of the defined *slave* positions. + /// + /// \param S set of *slave* positions to await input from + /// + /// \return an *execution policy* implementing *awaiting any* input from set + /// \p S + static std::unique_ptr + awaitAny(const std::set &S); + + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// The *execution policy* implemented by \p this object is applicable to the + /// given deluxe *unit* referred by \p H only if the function returns \c true. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + virtual bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept = 0; + + /// Tells if processing function should be executed on the current triggering. + /// + /// The function is to be called on each triggering of the deluxe *unit*. + /// Decision about execution of processing function is done by \p this object + /// according to the implemented *execution policy*. + /// + /// \param InputChanged flags indicating whether new input has been received + /// at *slave* positions + /// + /// \return if to execute processing function + virtual bool shouldProcess(const std::vector &InputChanged) noexcept = 0; + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + virtual std::string dump(void) const noexcept = 0; + +protected: + /// Tells whether the *unit* referred by \p H is a \c + /// rosa::deluxe::DeluxeAgent. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if the *unit* referred by \p H is a \c rosa::deluxe::DeluxeAgent + bool isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept; + + /// Tells the number of inputs handled by the *unit* referred by \p H. + /// + /// If \p H refers to a \c rosa::deluxe::DeluxeAgent, the function returns the + /// number of inputs (i.e., *slave* positions) of the *agent*. Otherwise, the + /// function returns \c 0. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return the number of inputs handled by the *unit* referred by \p H + size_t numberOfDeluxeAgentInputs(const AgentHandle H, + const DeluxeSystem &S) const noexcept; +}; + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +/// Converts a \c rosa::deluxe::DeluxeExecutionPolicy into \c std::string. +/// +/// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to convert +/// +/// \return \c std::string representing \p EP +string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP); + +/// Dumps a \c rosa::deluxe::DeluxeExecutionPolicy to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param EP \c rosa::deluxe::DeluxeExecutionPolicy to dump +/// +/// \return \p OS after dumping \p EP to it +ostream &operator<<(ostream &OS, const rosa::deluxe::DeluxeExecutionPolicy &EP); + +} // End namespace std + +#endif // ROSA_DELUXE_DELUXEEXECUTIONPOLICY_H diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 46ca0c1..55e766c 100755 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,257 +1,315 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" +#include "rosa/deluxe/DeluxeExecutionPolicy.h" namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext +/// +/// \invariant There is a compatible *execution policy* set +/// +/// \see Definition of \c rosa::deluxe::DeluxeSensor::inv on the class invariant +/// +/// \note All member functions validate the class invariant as part of their +/// precondition. Moreover, non-const functions validate the invariant before +/// return as their postcondition. class DeluxeSensor : public Agent { + /// Checks whether \p this object holds the class invariant. + /// + /// \see Invariant of the class \c rosa::deluxe::DeluxeSensor + /// + /// \return if \p this object holds the class invariant + bool inv(void) const noexcept; + + /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of + /// \c this object. + std::unique_ptr ExecutionPolicy; + public: /// Template alias for function objects used as data source for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c D is to be \c noexcept. /// /// \tparam T type of data provided by the function template using D = std::function; /// The type of values produced by \p this object. /// /// That is the type of values \p this object sends to its *master*. /// /// \see \c rosa::deluxe::DeluxeSensor::master const TypeNumber OutputType; private: /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// /// The actual data source functions are captured in a lambda expression that /// is in turn wrapped in a \c std::function object. The lambda expression /// calls the data source function to obtain the next sensory value and sends /// it to *master* by calling \c rosa::deluxe::DeluxeSensor::sendToMaster. The /// function \c rosa::deluxe::DeluxeSensor::handleTrigger needs only to call /// the proper function object. /// Handles trigger during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; /// Handles trigger during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; /// Wraps a data source function into a trigger handler. /// /// \see \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with + /// \param inSimulation if F is a data source for Simulation /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode - template H triggerHandlerFromDataSource(D &&F) noexcept; + template + H triggerHandlerFromDataSource(D &&F, bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \todo Enforce F does not potentially throw exception. /// /// \tparam T type of data to operate on /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param F function to generate the next value with during normal operation /// /// \pre Statically, \p T is a built-in type:\code /// TypeListContains::Value /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode template ::Value>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; + /// Returns the current execution policy of \p this object. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note The returned reference is valid only as long as \c + /// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this + /// object is not destroyed. + /// + /// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy + const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; + + /// Sets the current execution policy of \p this object to \p EP. + /// + /// \see \c rosa::deluxe::DeluxeExecutionPolicy + /// + /// \note \p EP is set only if it can handle \p this object. + /// + /// \param EP the new execution policy for \p this object + /// + /// \return if \p EP was successfully set for \p this object. + bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; + /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \param _Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// /// \tparam T type of data provided by \p SF /// /// \param SF function to generate value with /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void registerSimulationDataSource(D &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// \tparam T type of the value to send /// /// \param Value value to send /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode template void sendToMaster(const T &Value) noexcept; /// Generates the next sensory value upon trigger from the system. /// /// Executes \c rosa::deluxe::DeluxeSensor::FP or /// \c rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. void handleTrigger(atoms::Trigger) noexcept; }; template -DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(D &&F) noexcept { +DeluxeSensor::H +DeluxeSensor::triggerHandlerFromDataSource(D &&F, + bool inSimulation) noexcept { ASSERT(OutputType == TypeNumberOf::Value); - return [this, F](void) noexcept { sendToMaster(F()); }; + return [ this, F, inSimulation ](void) noexcept { + // Get value and send it to master only if \p ExecutionPolicy allows it. + if (ExecutionPolicy->shouldProcess({})) { + sendToMaster(F()); + } else if (inSimulation) { + // But read input value in Simulation anyway as input values are provided + // for the highest execution frequency for simulation + F(); + } + }; } template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, D &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger)), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(TypeNumberOf::Value), - FP(triggerHandlerFromDataSource(std::move(F))), - SFP(nullptr) { + FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE("DeluxeSensor is created."); + ASSERT(inv()); } template void DeluxeSensor::registerSimulationDataSource(D &&SF) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); - SFP = triggerHandlerFromDataSource(std::move(SF)); + ASSERT(inv() && OutputType == TypeNumberOf::Value); + SFP = triggerHandlerFromDataSource(std::move(SF), true); + ASSERT(inv()); } template void DeluxeSensor::sendToMaster(const T &Value) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); + ASSERT(inv() && OutputType == TypeNumberOf::Value); // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); } + ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp index 4388a4e..8ab143f 100755 --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,211 +1,212 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; + friend class DeluxeExecutionPolicy; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor /// \param F function to generate the next value with during normal operation /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor template AgentHandle createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process /// input values and generate output with /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent template AgentHandle createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; template AgentHandle DeluxeSystem::createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(F)); }); return {DS}; } template AgentHandle DeluxeSystem::createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa #endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP diff --git a/lib/deluxe/CMakeLists.txt b/lib/deluxe/CMakeLists.txt index 41a22b3..ab86f2c 100755 --- a/lib/deluxe/CMakeLists.txt +++ b/lib/deluxe/CMakeLists.txt @@ -1,20 +1,30 @@ set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/deluxe) add_library(ROSADeluxe ${LIB_INCLUDE_DIR}/namespace.h namespace.cpp ${LIB_INCLUDE_DIR}/DeluxeAtoms.hpp DeluxeAtoms.cpp + executionpolicies/Decimation.h + executionpolicies/Decimation.cpp + executionpolicies/AwaitBase.h + executionpolicies/AwaitBase.cpp + executionpolicies/AwaitAll.h + executionpolicies/AwaitAll.cpp + executionpolicies/AwaitAny.h + executionpolicies/AwaitAny.cpp + ${LIB_INCLUDE_DIR}/DeluxeExecutionPolicy.h + DeluxeExecutionPolicy.cpp ${LIB_INCLUDE_DIR}/DeluxeSensor.hpp DeluxeSensor.cpp ${LIB_INCLUDE_DIR}/DeluxeAgent.hpp DeluxeAgent.cpp ${LIB_INCLUDE_DIR}/DeluxeSystem.hpp DeluxeSystem.cpp DeluxeSystemImpl.hpp DeluxeSystemImpl.cpp ${LIB_INCLUDE_DIR}/DeluxeContext.hpp DeluxeContext.cpp ) ROSA_add_library_dependencies(ROSADeluxe ROSACore) diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp index 78d1cd4..0096b02 100755 --- a/lib/deluxe/DeluxeAgent.cpp +++ b/lib/deluxe/DeluxeAgent.cpp @@ -1,205 +1,238 @@ //===-- deluxe/DeluxeAgent.cpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeAgent.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeAgent.hpp" -#include "rosa/deluxe/DeluxeSensor.hpp" +#include "rosa/deluxe/DeluxeSystem.hpp" #include namespace rosa { namespace deluxe { bool DeluxeAgent::inv(void) const noexcept { + // Check execution policy. + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) { + return false; + } + // Check container sizes. if (!(InputTypes.size() == NumberOfInputs && InputChanged.size() == NumberOfInputs && InputValues->size() == NumberOfInputs && Slaves.size() == NumberOfInputs)) { return false; } // Check *slave* types and validate *slave* registrations and reverse lookup // information. std::map RefIds; // Build up a reference of SlaveIds in this. for (size_t I = 0; I < NumberOfInputs; ++I) { // First, validate input types at position \c I. const TypeNumber T = InputTypes[I]; if (InputValues->typeAt(I) != T) { return false; } // Check the registered *slave* at position \c I. const auto &Slave = Slaves[I]; // If \c Slave is empty, nothing to check. if (!Slave) continue; // \c Slave is not empty here. // Check the `OutputType` of the registered *slave*. const auto &A = unwrapAgent(*Slave); if (!((A.Kind == atoms::SensorKind && static_cast(A).OutputType == T) || (A.Kind == atoms::AgentKind && static_cast(A).OutputType == T))) { return false; } // Validate that the *slave* is not registered more than once. if (std::any_of( Slaves.begin() + I + 1, Slaves.end(), [&Slave](const Optional &O) { return O && *Slave == *O; })) { return false; } // Build the content of \c RefIds. RefIds.emplace(A.Id, I); } // Validate *slave* reverse lookup information against our reference. if (RefIds != SlaveIds) { return false; } // All checks were successful, the invariant is held. return true; } DeluxeAgent::~DeluxeAgent(void) noexcept { ASSERT(inv()); LOG_TRACE("Destroying DeluxeAgent..."); // Make sure \p this object is not a registered *slave*. if (Master) { ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. DeluxeAgent &M = static_cast(unwrapAgent(*Master)); ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. M.registerSlave(M.positionOfSlave(self()), {}); Master = {}; } // Also, make sure \p this object is no acting *master*. for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) { registerSlave(Pos, {}); } // Now there is no connection with other entities, safe to destroy. } +const DeluxeExecutionPolicy &DeluxeAgent::executionPolicy(void) const noexcept { + ASSERT(inv()); + return *ExecutionPolicy; +} + +bool DeluxeAgent::setExecutionPolicy( + std::unique_ptr &&EP) noexcept { + ASSERT(inv()); + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " setting execution policy " + << *EP << std::endl; + bool Success = false; + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (EP && EP->canHandle(self(), DS)) { + ExecutionPolicy.swap(EP); + Success = true; + } else { + LOG_TRACE_STREAM << "Execution policy " << *EP + << " cannot handle DeluxeAgent " << FullName << std::endl; + } + ASSERT(inv()); + return Success; +} + Optional DeluxeAgent::master(void) const noexcept { ASSERT(inv()); return Master; } void DeluxeAgent::registerMaster(const Optional _Master) noexcept { ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind)); Master = _Master; ASSERT(inv()); } TypeNumber DeluxeAgent::inputType(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return InputTypes[Pos]; } Optional DeluxeAgent::slave(const size_t Pos) const noexcept { ASSERT(inv() && Pos < NumberOfInputs); return Slaves[Pos]; } void DeluxeAgent::registerSlave(const size_t Pos, const Optional Slave) noexcept { ASSERT(inv() && Pos < NumberOfInputs && (!Slave || (unwrapAgent(*Slave).Kind == atoms::SensorKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos]) || (unwrapAgent(*Slave).Kind == atoms::AgentKind && static_cast(unwrapAgent(*Slave)).OutputType == InputTypes[Pos]))); // If registering an actual *slave*, not just clearing the slot, make sure // the same *slave* is not registered to another slot. if (Slave) { auto It = SlaveIds.find(unwrapAgent(*Slave).Id); if (It != SlaveIds.end()) { Slaves[It->second] = {};//Optional(); SlaveIds.erase(It); } } // Obtain the place whose content is to be replaced with \p Slave auto &OldSlave = Slaves[Pos]; // If there is already a *slave* registered at \p Pos, clear reverse lookup // information for it, and make sure it no longer has \p this object as // *master*. if (OldSlave) { auto &A = unwrapAgent(*OldSlave); ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check. SlaveIds.erase(A.Id); if (A.Kind == atoms::AgentKind) { static_cast(A).registerMaster({}); } else { ASSERT(A.Kind == atoms::SensorKind); // Sanity check. static_cast(A).registerMaster({}); } } // Register \p Slave at \p Pos. OldSlave = Slave; // If registering an actual *slave*, not just clearing the slot, register // reverse lookup information for the new *slave*. if (Slave) { SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos); } ASSERT(inv()); } size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept { ASSERT(inv()); bool Found = false; size_t Pos = 0; while (!Found && Pos < NumberOfInputs) { auto &ExistingSlave = Slaves[Pos]; if (ExistingSlave && *ExistingSlave == Slave) { Found = true; } else { ++Pos; } } ASSERT(Found || Pos == NumberOfInputs); // Sanity check. return Pos; } void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept { ASSERT(inv()); FP(); ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeContext.cpp b/lib/deluxe/DeluxeContext.cpp index f3fb417..0e363fe 100755 --- a/lib/deluxe/DeluxeContext.cpp +++ b/lib/deluxe/DeluxeContext.cpp @@ -1,151 +1,197 @@ //===-- deluxe/DeluxeContext.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeContext.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Implementation for rosa/deluxe/DeluxeContext.hpp. /// //===----------------------------------------------------------------------===// #define ROSA_LIB_DELUXE_DELUXECONTEXT_CPP // For including helper macros. #include "rosa/deluxe/DeluxeContext.hpp" #include namespace rosa { namespace deluxe { std::unique_ptr DeluxeContext::create(const std::string &Name) noexcept { return std::unique_ptr(new DeluxeContext(Name)); } DeluxeContext::DeluxeContext(const std::string &Name) noexcept : System(DeluxeSystem::createSystem(Name)) { LOG_TRACE("DeluxeContext for '" + System->name() + "' is created."); } DeluxeContext::~DeluxeContext(void) noexcept { // \c rosa::deluxe::DeluxeContext::System is not used outside, just clean it. for(auto U : DeluxeUnits) { System->destroyAgent(U); } // \note \c System will be marked clean by SystemImpl::~SystemImpl. LOG_TRACE("DeluxeContext for '" + System->name() + "' prepared for destruction."); } +Optional +DeluxeContext::getExecutionPolicy(AgentHandle Unit) const noexcept { + if (System->isDeluxeSensor(Unit)) { + return {System->getDeluxeSensor(Unit)->executionPolicy()}; + } else if (System->isDeluxeAgent(Unit)) { + return {System->getDeluxeAgent(Unit)->executionPolicy()}; + } else { + return {}; + } +} + +DeluxeContext::ErrorCode DeluxeContext::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"; + DCRETERROR(ErrorCode::UnsuitableExecutionPolicy); + } + + if (System->isDeluxeSensor(Unit)) { + const bool Success = System->getDeluxeSensor(Unit)->setExecutionPolicy( + std::move(ExecutionPolicy)); + if (!Success) { + DCRETERROR(ErrorCode::UnsuitableExecutionPolicy); + } else { + return ErrorCode::NoError; + } + } else if (System->isDeluxeAgent(Unit)) { + const bool Success = System->getDeluxeAgent(Unit)->setExecutionPolicy( + std::move(ExecutionPolicy)); + if (!Success) { + DCRETERROR(ErrorCode::UnsuitableExecutionPolicy); + } else { + return ErrorCode::NoError; + } + } else { + DCRETERROR(ErrorCode::NotUnit); + } +} + DeluxeContext::ErrorCode DeluxeContext::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->isDeluxeAgent(Agent)) { DCRETERROR(ErrorCode::NotAgent); } else if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto A = System->getDeluxeAgent(Agent); auto S = System->getDeluxeSensor(Sensor); ASSERT(A && S); // Sanity check. if (Pos >= A->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (A->inputType(Pos) != S->OutputType) { DCRETERROR(ErrorCode::TypeMismatch); } else if (A->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. A->registerSlave(Pos, {Sensor}); S->registerMaster({Agent}); return ErrorCode::NoError; } DeluxeContext::ErrorCode DeluxeContext::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->isDeluxeAgent(Master) && System->isDeluxeAgent(Slave))) { DCRETERROR(ErrorCode::NotAgent); } auto M = System->getDeluxeAgent(Master); auto S = System->getDeluxeAgent(Slave); ASSERT(M && S); // Sanity check. if (Pos >= M->NumberOfInputs) { DCRETERROR(ErrorCode::WrongPosition); } else if (M->inputType(Pos) != S->OutputType) { DCRETERROR(ErrorCode::TypeMismatch); } else if (M->slave(Pos)) { DCRETERROR(ErrorCode::AlreadyHasSlave); } else if (S->master()) { DCRETERROR(ErrorCode::AlreadyHasMaster); } // Do register. M->registerSlave(Pos, {Slave}); S->registerMaster({Master}); return ErrorCode::NoError; } std::weak_ptr DeluxeContext::getSystem(void) const noexcept { return std::weak_ptr(System); } void DeluxeContext::initializeSimulation(void) noexcept { // Clear simulation data sources from sensors. for (auto U : DeluxeUnits) { if (auto S = System->getDeluxeSensor(U)) { S->clearSimulationDataSource(); } } } void DeluxeContext::simulate(const size_t NumCycles) const noexcept { ASSERT(std::all_of( DeluxeUnits.begin(), DeluxeUnits.end(), [&](const AgentHandle &H) { return System->isDeluxeAgent(H) || System->isDeluxeSensor(H) && System->getDeluxeSensor(H)->simulationDataSourceIsSet(); })); for (size_t I = 1; I <= NumCycles; ++I) { LOG_TRACE("Simulation cycle: " + std::to_string(I)); for (auto U : DeluxeUnits) { U.sendMessage(Message::create(atoms::Trigger::Value)); } } } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/DeluxeExecutionPolicy.cpp b/lib/deluxe/DeluxeExecutionPolicy.cpp new file mode 100644 index 0000000..bbea1ea --- /dev/null +++ b/lib/deluxe/DeluxeExecutionPolicy.cpp @@ -0,0 +1,67 @@ +//===-- deluxe/DeluxeExecutionPolicy.cpp ------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/DeluxeExecutionPolicy.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for rosa/deluxe/DeluxeExecutionPolicy.h. +/// +//===----------------------------------------------------------------------===// + +#include "rosa/deluxe/DeluxeExecutionPolicy.h" +#include "rosa/deluxe/DeluxeSystem.hpp" + +#include "executionpolicies/Decimation.h" +#include "executionpolicies/AwaitAll.h" +#include "executionpolicies/AwaitAny.h" + +namespace rosa { +namespace deluxe { + +std::unique_ptr +DeluxeExecutionPolicy::decimation(const size_t D) { + return std::unique_ptr(new Decimation(D)); +} + +std::unique_ptr +DeluxeExecutionPolicy::awaitAll(const std::set &S) { + return std::unique_ptr(new AwaitAll(S)); +} + +std::unique_ptr +DeluxeExecutionPolicy::awaitAny(const std::set &S) { + return std::unique_ptr(new AwaitAny(S)); +} + +bool DeluxeExecutionPolicy::isDeluxeAgent(const AgentHandle H, const DeluxeSystem &S) const noexcept { + return S.isDeluxeAgent(H); +} + +size_t DeluxeExecutionPolicy::numberOfDeluxeAgentInputs( + const AgentHandle H, const DeluxeSystem &S) const noexcept { + auto A = S.getDeluxeAgent(H); + return A ? A->NumberOfInputs : 0; +} + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +string to_string(const rosa::deluxe::DeluxeExecutionPolicy &EP) { + return EP.dump(); +} + +ostream &operator<<(ostream &OS, + const rosa::deluxe::DeluxeExecutionPolicy &EP) { + OS << to_string(EP); + return OS; +} + +} // End namespace std diff --git a/lib/deluxe/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp index c55ef7f..c5269f7 100755 --- a/lib/deluxe/DeluxeSensor.cpp +++ b/lib/deluxe/DeluxeSensor.cpp @@ -1,63 +1,113 @@ //===-- deluxe/DeluxeSensor.cpp ---------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeSensor.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Implementation of rosa/deluxe/DeluxeSensor.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/deluxe/DeluxeSensor.hpp" -#include "rosa/deluxe/DeluxeAgent.hpp" +#include "rosa/deluxe/DeluxeSystem.hpp" namespace rosa { namespace deluxe { +bool DeluxeSensor::inv(void) const noexcept { + // Check execution policy. + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (!ExecutionPolicy || !ExecutionPolicy->canHandle(Self, DS)) { + return false; + } + + // All checks were successful, the invariant is held. + return true; +} + DeluxeSensor::~DeluxeSensor(void) noexcept { + ASSERT(inv()); LOG_TRACE("Destroying DeluxeSensor..."); // Make sure \p this object is not a registered *slave*. if (Master) { ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check. DeluxeAgent &M = static_cast(unwrapAgent(*Master)); ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check. M.registerSlave(M.positionOfSlave(self()), {}); Master = {}; } } +const DeluxeExecutionPolicy &DeluxeSensor::executionPolicy(void) const + noexcept { + ASSERT(inv()); + return *ExecutionPolicy; +} + +bool DeluxeSensor::setExecutionPolicy( + std::unique_ptr &&EP) noexcept { + ASSERT(inv()); + LOG_TRACE_STREAM << "DeluxeSensor " << FullName + << " setting execution policy " << *EP << std::endl; + bool Success = false; + // \note The \c rosa::System the \c rosa::Unit is created with is a + // \c rosa::DeluxeSystem. + const DeluxeSystem &DS = static_cast(Unit::system()); + if (EP && EP->canHandle(self(), DS)) { + ExecutionPolicy.swap(EP); + Success = true; + } else { + LOG_TRACE_STREAM << "Execution policy " << *EP + << " cannot handle DeluxeSensor " << FullName << std::endl; + } + ASSERT(inv()); + return Success; +} + Optional DeluxeSensor::master(void) const noexcept { + ASSERT(inv()); return Master; } void DeluxeSensor::registerMaster(const Optional _Master) noexcept { - ASSERT(!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind); + ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind)); Master = _Master; + + ASSERT(inv()); } void DeluxeSensor::clearSimulationDataSource(void) noexcept { + ASSERT(inv()); SFP = nullptr; + ASSERT(inv()); } bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept { + ASSERT(inv()); return SFP != nullptr; } void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept { + ASSERT(inv()); + // Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise // \c rosa::deluxe::DeluxeSensor::FP. const H &F = SFP ? SFP : FP; F(); + + ASSERT(inv()); } } // End namespace deluxe } // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitAll.cpp b/lib/deluxe/executionpolicies/AwaitAll.cpp new file mode 100644 index 0000000..70740b2 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAll.cpp @@ -0,0 +1,34 @@ +//===-- deluxe/executionpolicies/AwaitAll.cpp -------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAll.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/AwaitAll.h. +/// +//===----------------------------------------------------------------------===// + +#include "AwaitAll.h" + +#include + +namespace rosa { +namespace deluxe { + +AwaitAll::AwaitAll(const std::set &S) + : AwaitBase(S, + CheckerType(std::all_of::const_iterator, + std::function>)) {} + +std::string AwaitAll::dump(void) const noexcept { + return "Await all of " + dumpS(); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitAll.h b/lib/deluxe/executionpolicies/AwaitAll.h new file mode 100644 index 0000000..1374f90 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAll.h @@ -0,0 +1,48 @@ +//===-- deluxe/executionpolicies/AwaitAll.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAll.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of the *execution policy* *await all*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H + +#include "AwaitBase.h" + +namespace rosa { +namespace deluxe { + +/// Implementation of the *execution policy* *await all*. +/// +/// \see \c rosa::deluxe::DeluxeExecutionPolicy::awaitAll() +class AwaitAll : public AwaitBase { +public: + /// Constructor. + /// + /// The constructor instatiates \c rosa::deluxe::AwaitBase so that execution + /// is allowed when all *slave* positions included in \p S have received new + /// input since the last triggering. + /// + /// \param S set of *slave* positoins to check + AwaitAll(const std::set &S); + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + std::string dump(void) const noexcept override; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITALL_H diff --git a/lib/deluxe/executionpolicies/AwaitAny.cpp b/lib/deluxe/executionpolicies/AwaitAny.cpp new file mode 100644 index 0000000..109c4cd --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAny.cpp @@ -0,0 +1,34 @@ +//===-- deluxe/executionpolicies/AwaitAny.cpp -------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAny.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/AwaitAny.h. +/// +//===----------------------------------------------------------------------===// + +#include "AwaitAny.h" + +#include + +namespace rosa { +namespace deluxe { + +AwaitAny::AwaitAny(const std::set &S) + : AwaitBase(S, + CheckerType(std::any_of::const_iterator, + std::function>)) {} + +std::string AwaitAny::dump(void) const noexcept { + return "Await any of " + dumpS(); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitAny.h b/lib/deluxe/executionpolicies/AwaitAny.h new file mode 100644 index 0000000..d69df29 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitAny.h @@ -0,0 +1,48 @@ +//===-- deluxe/executionpolicies/AwaitAny.h ---------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitAny.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of the *execution policy* *await any*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H + +#include "AwaitBase.h" + +namespace rosa { +namespace deluxe { + +/// Implementation of the *execution policy* *await any*. +/// +/// \see \c rosa::deluxe::DeluxeExecutionPolicy::awaitAny() +class AwaitAny : public AwaitBase { +public: + /// Constructor. + /// + /// The constructor instatiates \c rosa::deluxe::AwaitBase so that execution + /// is allowed when any of the *slave* positions included in \p S has received + /// new input since the last triggering. + /// + /// \param S set of *slave* positoins to check + AwaitAny(const std::set &S); + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + std::string dump(void) const noexcept override; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITANY_H diff --git a/lib/deluxe/executionpolicies/AwaitBase.cpp b/lib/deluxe/executionpolicies/AwaitBase.cpp new file mode 100644 index 0000000..d4ec684 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitBase.cpp @@ -0,0 +1,60 @@ +//===-- deluxe/executionpolicies/AwaitBase.cpp ------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitBase.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/AwaitBase.h. +/// +//===----------------------------------------------------------------------===// + +#include "AwaitBase.h" +#include "rosa/support/debug.hpp" + +#include +#include + +namespace rosa { +namespace deluxe { + +AwaitBase::AwaitBase(const std::set &S, CheckerType &&Checker) + : Set(S), Checker(Checker) {} + +bool AwaitBase::canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept { + return isDeluxeAgent(H, S) && + canHandleNumberOfInputs(numberOfDeluxeAgentInputs(H, S)); +} + +bool AwaitBase::shouldProcess(const std::vector &InputChanged) noexcept { + // Sanity check of usage. + ASSERT(canHandleNumberOfInputs(InputChanged.size())); + return Checker(Set.begin(), Set.end(), + [&InputChanged](const size_t I) { return InputChanged[I]; }); +} + +bool AwaitBase::canHandleNumberOfInputs(const size_t NumberOfInputs) const + noexcept { + const auto MaxElemIt = std::max_element(Set.begin(), Set.end()); + const size_t MaxElem = (MaxElemIt == Set.end()) ? 0 : *MaxElemIt; + return MaxElem <= NumberOfInputs; +} + +std::string AwaitBase::dumpS(void) const noexcept { + std::stringstream SS; + SS << "["; + for (const auto &Value : Set) { + SS << " " << Value; + } + SS << " ]"; + return SS.str(); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/AwaitBase.h b/lib/deluxe/executionpolicies/AwaitBase.h new file mode 100644 index 0000000..e196701 --- /dev/null +++ b/lib/deluxe/executionpolicies/AwaitBase.h @@ -0,0 +1,105 @@ +//===-- deluxe/executionpolicies/AwaitBase.h --------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/AwaitBase.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of an *execution policy* that makes decisions depending +/// on input state of *slave* positions. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H + +#include "rosa/deluxe/DeluxeExecutionPolicy.h" + +#include + +namespace rosa { +namespace deluxe { + +/// Implementation of an *execution policy* that makes decisions depending on +/// whether new input has been received at some *slave* positions since the +/// laste triggering. +/// +/// The class implements the \c rosa::deluxe::DeluxeExecutionPolicy interface +/// but delegates the defintion of the actual decision-making function to +/// subclasses. +/// +/// \see rosa::deluxe::AwaitAll +/// \see rosa::deluxe::AwaitAny +class AwaitBase : public DeluxeExecutionPolicy { +protected: + /// Set of *slave* positions to check. + const std::set Set; + + /// Type of decision-making function used in \c + /// rosa::deluxe::AwaitBase::shouldProcess(). + using CheckerType = std::function::const_iterator, + std::set::const_iterator, + std::function)>; + + // Decision-making function for \c rosa::deluxe::AwaitBase::shouldProcess(). + const CheckerType Checker; + + /// Protected constructor, only subclasses can instatiate the class. + /// + /// \param S set of *slave* positions to await input from + /// \param Checker function that decides about execution + AwaitBase(const std::set &S, CheckerType &&Checker); + +public: + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// Any *execution policy* based on this class can handle *agents* with at + /// least as many *slave* positions as the largest one defined in \c + /// rosa::deluxe::AwaitBase::Set. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept override; + + /// Tells if processing function should be executed on the current triggering. + /// + /// Waiting for input allows execution when \c + /// rosa::deluxe::AwaitBase::Checker evaluates to \c true with respect to \c + /// rosa::deluxe::AwaitBase::Set and \p InputChanged. + /// + /// \param InputChanged flags indicating whether new input has been received + /// at *slave* positions + /// + /// \return if to execute processing function + bool shouldProcess(const std::vector &InputChanged) noexcept override; + +private: + /// Tells if \p this object can handle a *unit* with \p NumberOfInputs *slave* + /// positions. + /// + /// \param NumberOfInputs the number of *slave* positions to consider + /// + /// \return if \p this object can handle a *unit* with \p NumberOfInputs + /// *slave* positions + bool canHandleNumberOfInputs(const size_t NumberOfInputs) const noexcept; + +protected: + /// Dumps the set of *slave* positions that \p this object checks. + /// + /// \return textual representation of \c rosa::deluxe::AwaitBase::Set of \p + /// this object + std::string dumpS(void) const noexcept; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_AWAITBASE_H diff --git a/lib/deluxe/executionpolicies/Decimation.cpp b/lib/deluxe/executionpolicies/Decimation.cpp new file mode 100644 index 0000000..2f324f9 --- /dev/null +++ b/lib/deluxe/executionpolicies/Decimation.cpp @@ -0,0 +1,41 @@ +//===-- deluxe/executionpolicies/Decimation.cpp -----------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/Decimation.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for deluxe/executionpolicies/Decimation.h. +/// +//===----------------------------------------------------------------------===// + +#include "Decimation.h" + +#include + +namespace rosa { +namespace deluxe { + +Decimation::Decimation(const size_t D) + : Rate(std::max(D, 1)), Cycle(0) {} + +bool Decimation::canHandle(const AgentHandle, const DeluxeSystem &) const + noexcept { + return true; +} + +bool Decimation::shouldProcess(const std::vector &) noexcept { + return (Cycle++ % Rate) == 0; +} + +std::string Decimation::dump(void) const noexcept { + return "Decimation with rate " + std::to_string(Rate); +} + +} // End namespace deluxe +} // End namespace rosa diff --git a/lib/deluxe/executionpolicies/Decimation.h b/lib/deluxe/executionpolicies/Decimation.h new file mode 100644 index 0000000..b4d826c --- /dev/null +++ b/lib/deluxe/executionpolicies/Decimation.h @@ -0,0 +1,73 @@ +//===-- deluxe/executionpolicies/Decimation.h -------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file deluxe/executionpolicies/Decimation.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Declaration of the *execution policy* *decimation*. +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H +#define ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H + +#include "rosa/deluxe/DeluxeExecutionPolicy.h" + +namespace rosa { +namespace deluxe { + +/// Implementation of the *execution policy* *decimation*. +/// +/// \see \c rosa::deluxe::DeluxeExecutionPolicy::decimation() +class Decimation : public DeluxeExecutionPolicy { + + /// The rate of *decimation*. + const size_t Rate; + + /// Counter of triggerings. + size_t Cycle; + +public: + /// Constructor. + /// + /// \param D the rate of *decimation* + Decimation(const size_t D); + + /// Tells if \p this object can handle the deluxe *unit* referred by \p H. + /// + /// *Decimation* can handle any *units*. + /// + /// \param H reference to the *unit* to check + /// \param S the system owning the *unit* referred by \p H + /// + /// \return if \p this object can handle the *unit* referred by \p H + bool canHandle(const AgentHandle H, const DeluxeSystem &S) const + noexcept override; + + /// Tells if processing function should be executed on the current triggering. + /// + /// *Decimation* allows execution on each \c rosa::deluxe::Decimation::Rate + /// th triggering (i.e., calling of the function), which is counted + /// by \p this object in \c rosa::deluxe::Decimation::Cycle. + /// + /// \param InputChanged *ignored* + /// + /// \return if to execute processing function + bool shouldProcess(const std::vector &InputChanged) noexcept override; + + /// Dumps \p this object into textual representation. + /// + /// \return textual representation of \p this object + std::string dump(void) const noexcept override; +}; + +} // End namespace deluxe +} // End namespace rosa + +#endif // ROSA_LIB_DELUXE_EXECUTIONPOLICIES_DECIMATION_H