diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp index 67a1ac6..836e3d1 100644 --- a/apps/sa-ews1/sa-ews1.cpp +++ b/apps/sa-ews1/sa-ews1.cpp @@ -1,316 +1,318 @@ //===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS1 // //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews1/sa-ews1.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief The application SA-EWS1 implements the case study from the paper: /// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and /// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence /// DOI: 10.1007/978-3-319-58877-3_12 //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; const std::string AppName = "SA-EWS1"; /// Paths for the CSV files for simulation. /// ///@{ const std::string HRCSVPath = "HR.csv"; const std::string BRCSVPath = "BR.csv"; const std::string SpO2CSVPath = "SpO2.csv"; const std::string BPSysCSVPath = "BPSys.csv"; const std::string BodyTempCSVPath = "BodyTemp.csv"; const std::string ScoreCSVPath = "Score.csv"; ///@} /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; /// Warning levels for abstraction. enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Helper function creating a deluxe agent for pre-processing sensory values. /// /// Received values are first validated for confidence. Values which the /// validator does not mark confident are ignored. Confident values are /// abstracted into a \c WarningScore value, which is the result of the /// processing function. /// /// \note The result, \c WarningScore, is returned as \c uint32_t because /// enumeration types are not integrated into built-in types. Hence, a master /// to these agents receives its input as \c uint32_t values, and may cast them /// to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param C the deluxe context to create the agent in /// \param Name name of the new agent /// \param CC confidence validator to use /// \param A abstraction to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &C, const std::string &Name, const Confidence &CC, const Abstraction &A) { - using handler = DeluxeAgent::D; + using handler = std::function(std::pair)>; 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.second && CC(I.first)) ? result(A(I.first)) : result(); })); } int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << "app" << Color::Default << '\n' << Color::Yellow << "CSV files are read from and written to the current working directory." << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create(AppName); // // 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 HRSensor = C->createSensor("HR Sensor"); AgentHandle BRSensor = C->createSensor("BR Sensor"); AgentHandle SpO2Sensor = C->createSensor("SpO2 Sensor"); AgentHandle BPSysSensor = C->createSensor("BPSys Sensor"); AgentHandle BodyTempSensor = C->createSensor("BodyTemp Sensor"); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // Define confidence validators. // // Lower bounds are inclusive and upper bounds are exclusive. Confidence HRConfidence(0, 501); Confidence BRConfidence(0, 301); Confidence SpO2Confidence(0, 101); Confidence BPSysConfidence(0,501); Confidence BodyTempConfidence(-60, nextRepresentableFloatingPoint(50.0f)); // // Define abstractions. // RangeAbstraction HRAbstraction( {{{0, 40}, Emergency}, {{40, 51}, High}, {{51, 60}, Low}, {{60, 100}, No}, {{100, 110}, Low}, {{110, 129}, High}, {{129, 200}, Emergency}}, Emergency); RangeAbstraction BRAbstraction({{{0, 9}, High}, {{9, 14}, No}, {{14, 20}, Low}, {{20, 29}, High}, {{29, 50}, Emergency}}, Emergency); RangeAbstraction SpO2Abstraction({{{1, 85}, Emergency}, {{85, 90}, High}, {{90, 95}, Low}, {{95, 100}, No}}, Emergency); RangeAbstraction BPSysAbstraction( {{{0, 70}, Emergency}, {{70, 81}, High}, {{81, 101}, Low}, {{101, 149}, No}, {{149, 169}, Low}, {{169, 179}, High}, {{179, 200}, Emergency}}, Emergency); RangeAbstraction BodyTempAbstraction( {{{0.f, 28.f}, Emergency}, {{28.f, 32.f}, High}, {{32.f, 35.f}, Low}, {{35.f, 38.f}, No}, {{38.f, 39.5f}, High}, {{39.5f, 100.f}, Emergency}}, Emergency); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle HRAgent = createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction); AgentHandle BRAgent = createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction); AgentHandle SpO2Agent = createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction); AgentHandle BPSysAgent = createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction); AgentHandle BodyTempAgent = createLowLevelAgent( C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp 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 BodyAgent = C->createAgent( "Body Agent", - DeluxeAgent::D( + std::function( + std::pair, std::pair, + std::pair, std::pair, + std::pair)>( [](std::pair HR, std::pair BR, std::pair SpO2, std::pair BPSys, std::pair BodyTemp) -> Optional { LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" << (HR.second ? "" : "") << " HR warning score: " << HR.first << "\n" << (BR.second ? "" : "") << " BR warning score: " << BR.first << "\n" << (SpO2.second ? "" : "") << " SpO2 warning score: " << SpO2.first << "\n" << (BPSys.second ? "" : "") << " BPSys warning score: " << BPSys.first << "\n" << (BodyTemp.second ? "" : "") << " BodyTemp warning score: " << BodyTemp.first << "\n******\n"; return {HR.first + BR.first + SpO2.first + BPSys.first + BodyTemp.first}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a CSV file. // LOG_INFO("Create a logger agent."); // Create CSV writer. std::ofstream ScoreCSV(ScoreCSVPath); csv::CSVWriter ScoreWriter(ScoreCSV); // The agent writes each new input value into a CSV file and produces nothing. AgentHandle LoggerAgent = C->createAgent( "Logger Agent", - DeluxeAgent::D( + std::function(std::pair)>( [&ScoreWriter](std::pair Score) -> Optional { if (Score.second) { // The state of \p ScoreWriter is not checked, expecting good. ScoreWriter << Score.first; } 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, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. using CSVInt = csv::CSVFlatIterator; using CSVFloat = csv::CSVFlatIterator; std::ifstream HRCSV(HRCSVPath); C->registerSensorValues(HRSensor, CSVInt(HRCSV), CSVInt()); std::ifstream BRCSV(BRCSVPath); C->registerSensorValues(BRSensor, CSVInt(BRCSV), CSVInt()); std::ifstream SpO2CSV(SpO2CSVPath); C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV), CSVInt()); std::ifstream BPSysCSV(BPSysCSVPath); C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt()); std::ifstream BodyTempCSV(BodyTempCSVPath); C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV), CSVFloat()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp index d50396e..f465933 100755 --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,177 +1,178 @@ //===-- 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 +/// \date 2017-2019 /// /// \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 handler = std::function(std::pair)>; 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"); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle IntAgent = createLowLevelAgent(C, "IntAgent"); AgentHandle FloatAgent = createLowLevelAgent(C, "FloatAgent"); // // 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( + "Sum Agent", std::function(std::pair, + std::pair)>( [](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::function(std::pair)>( [](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..c73afdc 100755 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,673 +1,1119 @@ //===-- 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 /// 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 +#define DASLAVEHANDLERNAME(N) handleSlave_##N + +/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent. +/// +/// \param N name suffix to use +#define DAMASTERHANDLERNAME(N) handleMaster_##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 { \ +#define DASLAVEHANDLERDEFN(T, N) \ + void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \ saveInput(SlaveId, Value); \ } -/// Convenience macro for \c DAHANDLERDEFN with identical arguments. +/// Defines member functions for handling messages from *master* in +/// \c rosa::deluxe::DeluxeAgent. +/// +/// \see \c DeluxeAgentMasterInputHandlers +/// +/// \note No pre- and post-conditions are validated directly by these functions, +/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do +/// that. +/// +/// \param T the type of input to handle +/// \param N name suffix for the function identifier +#define DAMASTERHANDLERDEFN(T, N) \ + void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, \ + T Value) noexcept { \ + saveMasterInput(MasterId, Value); \ + } + +/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments. +/// +/// \see \c DASLAVEHANDLERDEFN +/// +/// This macro can be used instead of \c DASLAVEHANDLERDEFN 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 DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T) + +/// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments. /// -/// \see \c DAHANDLERDEFN +/// \see \c DAMASTERHANDLERDEFN /// -/// 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. +/// This macro can be used instead of \c DAMASTERHANDLERDEFN 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) +#define DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by -/// \c DAHANDLERDEFN. +/// \c DASLAVEHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super -/// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN. +/// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN. /// -/// \see \c DAHANDLERDEFN, \c THISMEMBER +/// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier -#define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N)) +#define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N)) + +/// Results in a \c THISMEMBER reference to a member function defined by +/// \c DAMASTERHANDLERDEFN. +/// +/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super +/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN. +/// +/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER +/// +/// \param N name suffix for the function identifier +#define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(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. +/// entry for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs +/// matches \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All +/// master-output-related container objects have a size matching \c +/// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types of input and +/// master-output values are consistent throughout all the input-related and +/// master-output-related containers, respectively. 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; 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; + /// The type of values \p this object processes from its *master*. + /// + /// \see \c rosa::deluxe::DeluxeAgent::master + const TypeNumber MasterInputType; + + /// Number of outputs produces by \p this object for its *slaves*. + /// + /// \note This values is equal to \c + /// rosa::deluxe::DeluxeAgent::NumberOfInputs. + /// + /// \see \c rosa::deluxe::DeluxeAgent::slave. + const size_t NumberOfMasterOutputs; + 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. + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. 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. + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. 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. + /// See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::unique_ptr InputValues; + /// Indicates whether the input value from the *master* has been changed since + /// the last trigger received from the system. + /// + /// The flag is reset to \c false upon handling a trigger and then set to \c + /// true by \c rosa::deluxe::DeluxeAgent::saveMasterInput when storig a new + /// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue. + bool MasterInputChanged; + + /// Stores the actual input value from *master*. + /// + /// \note The type of the stored value matches the type indicated by \c + /// rosa::deluxe::DeluxeAgent::MasterInputType. + const std::unique_ptr MasterInputValue; + + /// Types of output values produced by \p this object for its *slaves*. + /// + /// That is the type of values \p this object sends to its *slaves*. + /// + /// \note The position of a type in the \c std::vector indicates which + /// *slave* of \p this object the type belongs to. See also + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + const std::vector MasterOutputTypes; + /// 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 + /// The actual functions processing *slave* and *master* inputs and generating + /// optional output to *master* and *slaves* are captured in a lambda + /// expression that is in turn wrapped in a \c std::function object. The + /// lambda expression calls the master-input processing function with the + /// actual master-input data and sends its result -- if any -- to *slaves* by + /// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the + /// input processing function with the actual input data and sends its result + /// -- if any -- to *master* by calling \c + /// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c + /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored + /// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c + /// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current + /// values are processed. The function \c + /// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the /// function object. /// - /// \see \c rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunction + /// \see \c + /// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions 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. + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \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. + /// Tells the unique identifier of the *master* of \p this object, if any + /// registered. /// - /// \tparam As types to match against values in + /// \return the unique identifier of the *master* + /// + /// \pre A *master* is registered for \p this object: \code + /// Master + /// \endcode + id_t masterId(void) const noexcept; + + /// Tells whether types stored in \c rosa::TypeList \p As match the input + /// types of \p this object. + /// + /// \tparam As \c rosa::TypeList containing types to match against values in /// \c rosa::deluxe::DeluxeAgent::InputTypes /// - /// \return if types \p As... match \c rosa::TypeNumber values stored in + /// \note Instatiation of the template fails if \p As is not \c + /// rosa::TypeList. + /// + /// \return if types in \p As match \c rosa::TypeNumber values stored in /// \c rosa::deluxe::DeluxeAgent::InputTypes - template bool inputTypesMatch(void) const noexcept; + template bool inputTypesMatch(void) const noexcept; + + /// Tells whether types stored in \c rosa::TypeList \p Ts match the + /// master-output types of \p this object. + /// + /// \tparam Ts \c rosa::TypeList containing types to match against values in + /// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes + /// + /// \note Instatiation of the template fails if \p As is not \c + /// rosa::TypeList. + /// + /// \return if types in \p Ts match \c rosa::TypeNumber values stored in + /// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes + template bool masterOutputTypesMatch(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) + /// 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. + /// Invokes a processing function matching the input, output, and + /// master-output 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 Ts types of master-output values 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; + template + static std::tuple, Optional...> + invokeWithTuple(std::function, Optional...>( + std::pair...)> + F, + const std::tuple...> Args, + Seq) noexcept; + + /// Handles the master-output value at position \p Pos of \p Output. + /// + /// \p Output is a \c std::tuple resulted by a processing function and + /// contains master-output values starting at position \p Offset. The + /// function takes the master-output value for *slave* position \p Pos and + /// sends its actual value, if any, to the corresponding *slave*. + /// + /// \note A master-output of type \c rosa::unit_t indicates no actual output + /// and hence no message is generated for a position whose corresponding + /// master-output type is \c rosa::unit_t. + /// + /// \note The function provides position-based implementation for \c + /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. + /// + /// \tparam Offset index of the first master-output value in \p Output + /// \tparam Pos the position of the master-output to handle + /// \tparam Ts output types stored in \p Output + /// + /// \param Output \c std::tuple resulted by the processing function + /// + /// \pre \p Output matches the master-output types \p this object was created + /// with and \p Pos is a valid master-output index: \code + /// masterOutputTypesMatch>::Type>() && + /// Pos < NumberOfMasterOutputs + /// \endcode + template + void handleMasterOutputAtPos( + const std::tuple...> &Output) noexcept; + + /// Handles master-output values from \p Output. + /// + /// \p Output is a \c std::tuple resulted by a processing function and + /// contains master-output values starting at position \p Offset. The function + /// takes master-output values and sends each actual value to the + /// corresponding *slave*. + /// + /// \tparam Offset index of the first master-output value in \p Output + /// \tparam Ts output types stored in \p Output + /// \tparam S0 indices starting with `0` for extracting master-output values + /// from \p Output + /// + /// \param Output \c std::tuple resulted by a processing function + /// + /// \pre \p Output matches the master-output types \p this object was created + /// with and the provided sequence of indices \p S0... constitues a proper + /// sequence for extraing all master-output values from \p Output: \code + /// masterOutputTypesMatch>::Type>() && + /// sizeof...(S0) == NumberOfMasterOutputs + /// \endcode + template + void + handleMasterOutputs(const std::tuple...> &Output, + Seq) noexcept; - /// Wraps a processing function into a trigger handler. + /// Wraps processing functions 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 MT type of master-input /// \tparam T type of output + /// \tparam Ts types of master-output values /// \tparam As types of input values /// + /// \param MF function processing master-input and generating output /// \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() + /// \note A master-input type of \c rosa::unit_t indicates that \p this object + /// does not receive master-input, \p MF is never called if \p MT is \c + /// rosa::unit_t. + /// + /// \return trigger handler function based on \p F and \p MF + /// + /// \pre Template arguments \p MT, \p T, \p Ts..., and \p As... match the + /// corresponding types \p this object was created with: \code + /// MasterInputType == TypeNumberOf::Value && + /// OutputType == TypeNumberOf::Value && + /// inputTypesMatch>() && + /// masterOutputTypesMatch>() /// \endcode - template - H triggerHandlerFromProcessingFunction(D &&F) noexcept; + template + H triggerHandlerFromProcessingFunctions( + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&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. + /// The function \p F generates a \c std::tuple of values: the first value is + /// the output for the *master* and the rest is for the *slaves*. All output + /// generated by the function is optional as an agent may decide not to output + /// anything at some situation. /// + /// \todo Enforce \p F and \p MF do not potentially throw exception. + /// + /// \tparam MT type of master-input handled by \p MF /// \tparam T type of output of \p F + /// \tparam Ts type of master-output values of \p F and \p MF /// \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. + /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p + /// Ts..., and \p As... is not a built-in type. + /// + /// \note If \p MT is \c rosa::unit_t, the constructed object does not receive + /// master-input. Similarly, if any of \p Ts... is \c rosa::unit_t, the + /// constructed object does not generated master-output for the corresponding + /// *slave* position. /// /// \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 + /// \param MF function to process master-input values and generate + /// master-output with + /// \param F function to process input values and generate output and + /// master-output with + /// + /// \pre Statically, all of the type arguments \p MT, \p T, \p Ts..., and \p + /// As... is a built-in type and the number of input and master-output types + /// are equal: \code + /// TypeListSubsetOf, + /// BuiltinTypes>::Value && sizeof...(Ts) == sizeof...(As) + ///\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; + template , + BuiltinTypes>::Value && + sizeof...(Ts) == sizeof...(As)>> + DeluxeAgent( + const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) 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*. /// + /// \note Any call to \c rosa::deluxe::DeluxeAgent::registerMaster should be + /// paired with a corresponding call of \c + /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that + /// input/output types of master and slave matches. + /// /// \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; + /// Tells the type of values produced for the *slave* at a position. + /// + /// That is the type of values \p this object potentially sends to 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 produced for + /// the *slave* at position \p Pos + /// + /// \pre \p Pos is a valid index of input: \code + /// Pos < NumberOfMasterOutputs + /// \endcode + TypeNumber masterOutputType(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. /// + /// \note The type of master-input values processed by the referred *slave* is + /// validated by matching its `MasterInputType` against the corresponding + /// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes. + /// /// \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: + /// expected input type at position \p Pos and processes values of types + /// matching the produced master-output type at position \p Pos: /// \code /// Pos < NumberOfInputs && /// (!Slave || /// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind && /// static_cast(unwrapAgent(*Slave)).OutputType == - /// InputTypes[Pos]) || + /// InputTypes[Pos] && + /// (MasterOutputTypes[Pos] == TypeNumberOf::Value || + /// static_cast(unwrapAgent(*Slave)).MasterInputType + /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == - /// InputTypes[Pos])) + /// InputTypes[Pos] && + /// (MasterOutputTypes[Pos] == TypeNumberOf::Value || + /// static_cast(unwrapAgent(*Slave)).MasterInputType == + /// MasterOutputTypes[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; + /// Sends a value to a *slave* of \p this object at position \p Pos. + /// + /// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if + /// it contains a valid handle. The function does nothing otherwise. + /// + /// \tparam T type of the value to send + /// + /// \param Pos the position of the *slave* to send \p Value to + /// \param Value value to send + /// + /// \pre \p Pos is a valid *slave* position and \p T matches \c + /// rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code + /// Pos < NumberOfMasterOutputs && + /// MasterOutputTypes[Pos] == TypeNumberOf::Value + /// \endcode + template + void sendToSlave(const size_t Pos, 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 + /// Stores a new input value from the *master*. + /// + /// The function stores \p Value in \c + /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the + /// corresponding flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. + /// + /// \note Utilized by member functions of group \c + /// DeluxeAgentMasterInputHandlers. + /// + /// \tparam T type of input to store + /// + /// \param Id unique identifier of the *master* + /// \param Value the input value to store + /// + /// \pre The *master* with \p Id is registered and the input from the *master* + /// is expected to be of type \p T: \code + /// Master && masterId() == Id && MasterInputType == TypeNumberOf::Value + /// \endcode + template void saveMasterInput(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. + /// built-in type to handle messages from its *slaves*. 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 The member functions in this group are defined by \c + /// DASLAVEHANDLERDEF. /// /// \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) + DASLAVEHANDLERDEF(AtomValue) + DASLAVEHANDLERDEF(int16_t) + DASLAVEHANDLERDEF(int32_t) + DASLAVEHANDLERDEF(int64_t) + DASLAVEHANDLERDEF(int8_t) + DASLAVEHANDLERDEFN(long double, long_double) + DASLAVEHANDLERDEFN(std::string, std__string) + DASLAVEHANDLERDEF(uint16_t) + DASLAVEHANDLERDEF(uint32_t) + DASLAVEHANDLERDEF(uint64_t) + DASLAVEHANDLERDEF(uint8_t) + DASLAVEHANDLERDEF(unit_t) + DASLAVEHANDLERDEF(bool) + DASLAVEHANDLERDEF(double) + DASLAVEHANDLERDEF(float) /// @} + /// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of + /// rosa::deluxe::DeluxeAgent + /// + /// Definition of member functions handling messages from the *master* with + /// different types of input + /// + /// A *slave* generally needs to be prepared to deal with values of any + /// built-in type, except for \c rosa::unit_t, to handle messages from its + /// *master*. Each type requires a separate message handler, which are + /// implemented by these functions. The functions instantiate + /// \c rosa::deluxe::DeluxeAgent::saveMasterInput 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 + /// DAMASTERHANDLERDEF. + /// + /// \note Keep these definitions in sync with \c rosa::BuiltinTypes; but do no + /// include \c rosa::unit_t. + /// + ///@{ + + DAMASTERHANDLERDEF(AtomValue) + DAMASTERHANDLERDEF(int16_t) + DAMASTERHANDLERDEF(int32_t) + DAMASTERHANDLERDEF(int64_t) + DAMASTERHANDLERDEF(int8_t) + DAMASTERHANDLERDEFN(long double, long_double) + DAMASTERHANDLERDEFN(std::string, std__string) + DAMASTERHANDLERDEF(uint16_t) + DAMASTERHANDLERDEF(uint32_t) + DAMASTERHANDLERDEF(uint64_t) + DAMASTERHANDLERDEF(uint8_t) + DAMASTERHANDLERDEF(bool) + DAMASTERHANDLERDEF(double) + DAMASTERHANDLERDEF(float) + + /// @} }; /// Anonymous namespace with implementation for -/// \c rosa::deluxe::DeluxeAgent::inputTypesMatch, consider it private. +/// \c rosa::deluxe::DeluxeAgent::inputTypesMatch and \c +/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, 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 +/// for \c TypesMatchList. /// /// \tparam As types to match -template struct InputTypesMatchImpl; +template struct TypesMatchImpl; /// 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 { +template struct TypesMatchImpl { /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values - /// stored in \p InputTypes starting at position \p Pos. + /// stored in \p Types 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). + /// type \p A against \c rosa::TypeNumber at position \p Pos of \p Types, 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 + /// \param Types container of \c rosa::TypeNumber values to match types + /// against + /// \param Pos position in \p Types 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); + /// in \p Types starting at position \p Pos + static bool f(const std::vector &Types, size_t Pos) noexcept { + return Pos < Types.size() && TypeNumberOf::Value == Types[Pos] && + TypesMatchImpl::f(Types, 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. +template <> struct TypesMatchImpl<> { + /// Tells whether \p Pos is the number of values stored in \p Types. /// - /// In this terminal case, there is no more types to matchi because all the + /// In this terminal case, there is no more types to match 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. + /// \p Types. That is true if \p Pos points exactly to the end of \p Types. /// - /// \param InputTypes container of \c rosa::TypeNumber values to match - /// types against - /// \param Pos position in \p InputTypes to start matching at + /// \param Types container of \c rosa::TypeNumber values to match types + /// against + /// \param Pos position in \p Types 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(); + /// \return if \p Pos is the number of values stored in \p Types + static bool f(const std::vector &Types, size_t Pos) noexcept { + return Pos == Types.size(); + } +}; + +/// Template \c struct that provides an implementation for \c +/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c +/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch. +/// +/// \note Match a list of types \p As... against a \c std::vector of +/// \c rosa::TypeNumber values, \c Types, like \code +/// bool match = TypesMatchList>::f(Types); +/// \endcode +/// +/// \tparam As \c rosa::TypeList that contains types to match +template struct TypesMatchList; + +/// Template specialization implementing the feature. +/// +/// \tparam As \c rosa::TypeList that contains types to match +template struct TypesMatchList> { + /// Tells whether types \p As... match \c rosa::TypeNumber values + /// stored in \p Types. + /// + /// The function unwraps the types from \c rosa::TypeList and utilizes \c + /// TypesMatchImpl to do the check. + /// + /// \param Types container of \c rosa::TypeNumber values to match types + /// against + /// + /// \return if types \p As... match \c rosa::TypeNumber values stored + /// in \p Types + static bool f(const std::vector &Types) noexcept { + return TypesMatchImpl::f(Types, 0); } }; } // End namespace -template +template bool DeluxeAgent::inputTypesMatch(void) const noexcept { - return InputTypesMatchImpl::f(InputTypes, 0); + return TypesMatchList::f(InputTypes); +} + +template +bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept { + return TypesMatchList::f(MasterOutputTypes); } 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; + 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 { +template +std::tuple, Optional...> DeluxeAgent::invokeWithTuple( + std::function< + std::tuple, Optional...>(std::pair...)> + F, + const 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)()); +template +void DeluxeAgent::handleMasterOutputAtPos( + const std::tuple...> &Output) noexcept { + // Need to indirectly reference \c + // rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT because + // of the comma in its template argument list. + auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch< + typename TypeListDrop>::Type>; + ASSERT(inv() && (this->*MOTMFP)() && Pos < NumberOfMasterOutputs); + // Do not do anything for master-output type \c rosa::unit_t. + if (!std::is_same, Offset + Pos>::Type, + unit_t>::value) { + const auto MasterOutput = std::get(Output); + if (MasterOutput) { + sendToSlave(Pos, *MasterOutput); + } + } +} - return [ this, F ]() noexcept { +template +void DeluxeAgent::handleMasterOutputs(const std::tuple...> &Output, + Seq) noexcept { + // Need to indirectly reference \c + // rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT because + // of the comma in its template argument list. + auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch< + typename TypeListDrop>::Type>; + ASSERT(inv() && (this->*MOTMFP)() && sizeof...(S0) == NumberOfMasterOutputs); + // Handle each master-output position in a fold expression. + (handleMasterOutputAtPos(Output), ...); +} + +template +DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + // Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch + // and \c rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT + // because of the comma in their respective template argument lists. + auto ITMFP = &DeluxeAgent::inputTypesMatch>; + auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch>; + ASSERT(MasterInputType == TypeNumberOf::Value && + OutputType == TypeNumberOf::Value && (this->*ITMFP)() && + (this->*MOTMFP)()); + + return [ this, MF, F ]() noexcept { + // \note These indices work for both inputs and master-outputs. using Indices = typename GenSeq::Type; - auto Args = prepareCurrentInputs(Indices()); + + // Handle master-input. + // Do not do anything for master-input type \c rosa::unit_t. + if (MasterInputType != TypeNumberOf::Value) { + LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles master-input." + << std::endl; + const auto MasterInputArg = std::make_pair( + *static_cast(MasterInputValue->pointerTo(0)), + MasterInputChanged); + MasterInputChanged = false; + const std::tuple...> MasterOutput = MF(MasterInputArg); + handleMasterOutputs<0>(MasterOutput, Indices()); + } + + // Handle inputs. + LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles input." + << std::endl; + const auto InputArgs = prepareCurrentInputs(Indices()); std::fill(InputChanged.begin(), InputChanged.end(), false); - Optional R = invokeWithTuple(F, Args, Indices()); - if (R) { - sendToMaster(*R); + const std::tuple, Optional...> Output = + invokeWithTuple(F, InputArgs, Indices()); + const auto OutputToMaster = std::get<0>(Output); + if (OutputToMaster) { + sendToMaster(*OutputToMaster); } + handleMasterOutputs<1>(Output, Indices()); }; } -template -DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id, - const std::string &Name, MessagingSystem &S, - D &&F) noexcept +template +DeluxeAgent::DeluxeAgent( + const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&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)), + DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t), + DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t), + DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double), + DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t), + DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t), + DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t), + DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double), + DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue), + DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t), + DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t), + DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string), + DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t), + DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t), + DAMASTERHANDLERREF(bool), DAMASTERHANDLERREF(double), + DAMASTERHANDLERREF(float)), + OutputType(TypeNumberOf::Value), NumberOfInputs(sizeof...(As)), + MasterInputType(TypeNumberOf::Value), + NumberOfMasterOutputs(NumberOfInputs), InputTypes({TypeNumberOf::Value...}), InputChanged(NumberOfInputs, false), - InputValues(new TokenizedStorage()), - FP(triggerHandlerFromProcessingFunction(std::move(F))), + InputValues(new TokenizedStorage()), MasterInputChanged(false), + MasterInputValue(new TokenizedStorage()), + MasterOutputTypes({TypeNumberOf::Value...}), + FP(triggerHandlerFromProcessingFunctions(std::move(MF), 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)); } } +template +void DeluxeAgent::sendToSlave(const size_t Pos, const T &Value) noexcept { + ASSERT(inv() && Pos < NumberOfMasterOutputs && + MasterOutputTypes[Pos] == TypeNumberOf::Value); + + // There is a handle and the referred *slave* is in a valid state. + auto Slave = Slaves[Pos]; + if (Slave && *Slave) { + Slave->sendMessage(Message::create(atoms::Master::Value, Id, Value)); + } +} + 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()); } +template +void DeluxeAgent::saveMasterInput(id_t Id, T Value) noexcept { + ASSERT(inv() && Master && masterId() == Id && + MasterInputType == TypeNumberOf::Value); + + *static_cast(MasterInputValue->pointerTo(0)) = Value; + MasterInputChanged = true; + + ASSERT(inv()); +} } // End namespace deluxe } // End namespace rosa -#undef DAHANDLEREF -#undef DAHANDLEDEF -#undef DAHANDLEDEFN -#undef DAHANDLENAME +#undef DASLAVEHANDLEREF +#undef DAMASTERHANDLEREF +#undef DASLAVEHANDLEDEF +#undef DAMASTERHANDLEDEF +#undef DASLAVEHANDLEDEFN +#undef DAMASTERHANDLEDEFN +#undef DASLAVEHANDLENAME +#undef DAMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXEAGENT_HPP diff --git a/include/rosa/deluxe/DeluxeAtoms.hpp b/include/rosa/deluxe/DeluxeAtoms.hpp index a2aab57..70963b2 100755 --- a/include/rosa/deluxe/DeluxeAtoms.hpp +++ b/include/rosa/deluxe/DeluxeAtoms.hpp @@ -1,61 +1,59 @@ //===-- rosa/deluxe/DeluxeAtoms.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAtoms.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Definition of \c rosa::AtomValue values and \c rosa::AtomConstant /// types for the implementation of the *deluxe interface*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEATOMS_HPP #define ROSA_DELUXE_DELUXEATOMS_HPP #include "rosa/support/atom.hpp" namespace rosa { namespace deluxe { /// Contains some definitions used in the implementation of the *deluxe /// interface* to denote various roles and events /// /// \see \c rosa::deluxe::DeluxeContext /// /// \note Do not apply `using namespace` to this namespace as that may result in /// some identifiers in the original namespace being hidden by those of /// \c rosa::deluxe::atoms. namespace atoms { /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeSensor. /// /// \see \c rosa::Unit::Kind constexpr AtomValue SensorKind = atom("dl_sensor"); /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeAgent. /// /// \see \c rosa::Unit::Kind constexpr AtomValue AgentKind = atom("dl_agent"); /// Type alias denoting system trigger messages. using Trigger = AtomConstant; /// Type alias denoting messages from a slave. using Slave = AtomConstant; /// Type alias denoting messages from a master. -/// -/// \note This one is not used at the moment. using Master = AtomConstant; } // End namespace atoms } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXEATOMS_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index 29794a1..f641466 100755 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,294 +1,492 @@ //===-- 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*. +/// +/// \todo The classes \c rosa::deluxe::DeluxeSensor and \c +/// rosa::deluxe::DeluxeAgent share some common features in relation to their +/// *slave* role in the *deluxe interface*. But their definitions are completely +/// independent. It could be investigated how to lift their common parts into a +/// new *deluxe slave* class, which would serve as base for both, to avoid code +/// duplication. +/// +/// \todo In the master-to-slave communication, the type \c rosa::unit_t +/// indicates no master-output in the *master* and no master-input in the +/// *slave*. That works fine, but does not allow \c rosa::unit_t to be used in +/// actual master-to-slave communication. It would make sense to use \c +/// rosa::none_t as the extreme type instead. That would need some adjustment of +/// code because \c rosa::none_t is not part of \c rosa::BuiltinTypes. 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, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream }; /// 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. /// + /// The new *sensor* does not receive master-input. + /// /// \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. /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// /// \return \c rosa::AgentHandle for the new *sensor* template - AgentHandle createSensor(const std::string &Name, - DeluxeSensor::D &&F = [](void) { - return T(); - }) noexcept; + AgentHandle createSensor( + const std::string &Name, + std::function &&F = [](void) { return T(); }) noexcept; + + /// Creates a new *sensor* in the context of \p this object. + /// + /// The new *sensor* handles master-input by \p MF. + /// + /// \tparam MT type of master-input the new *sensor* handles + /// \tparam T type of data the new *sensor* operates on + /// + /// \note If \p MT is \c rosa::UnitType + /// + /// \param Name name of the new *sensor* + /// \param MF function for the new *sensors* to process master-input values with + /// \param F function for the new *sensor* to generate the next value with + /// during normal operation + /// + /// \note \p F is not used during simulation, in which case + /// \c rosa::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. + /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// + /// \return \c rosa::AgentHandle for the new *sensor* + template + AgentHandle createSensor( + const std::string &Name, std::function)> &&MF, + std::function &&F = [](void) { return T(); }) noexcept; /// Creates a new *agent* in the context of \p this object. /// + /// The new *agent* neither receives master-input nor produces master-output. + /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// /// \return \c rosa::AgentHandle for the new *agent* template - AgentHandle createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept; + AgentHandle + createAgent(const std::string &Name, + std::function(std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF but does not produce + /// master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam As types of inputs the new *agent* takes + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input values + /// with + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template + AgentHandle + createAgent(const std::string &Name, + std::function)> &&MF, + std::function(std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* does not receive master-input but produces master-output. + /// + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \param Name name of the new *agent* + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template + AgentHandle + createAgent(const std::string &Name, + std::function, Optional...>( + std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF and produces master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input values + /// with + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template + AgentHandle createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) 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 + /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other than the output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// /// \return how succesfull connecting \p Slave to \p Master at slot index /// \p Pos was /// /// \note The function may return the following /// \c rosa::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 + /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; /// Initializes \c this object and others managed by \p this object for /// setting up and performing simulation. /// /// \see \c rosa::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; }; +/// Anonymous namespace with helper features for implementing +/// \c rosa::deluxe::DeluxeContext, consider it private. +namespace { + +/// Maps any type \p T to \c rosa::unit_t. +template struct MapToUnit { using Type = unit_t; }; + +} // End namespace + template AgentHandle DeluxeContext::createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept { - AgentHandle H = System->createSensor(Name, std::move(F)); + std::function &&F) noexcept { + return createSensor(Name, + std::function)>( + [](std::pair) {}), + std::move(F)); +} + +template +AgentHandle +DeluxeContext::createSensor(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { + AgentHandle H = System->createSensor(Name, std::move(MF), 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)); +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function(std::pair...)> &&F) noexcept { + using NoMasterOutputType = + std::tuple::Type>...>; + return createAgent( + Name, + std::function)>( + [](std::pair) { return NoMasterOutputType(); }), + std::function< + std::tuple, Optional::Type>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + return std::tuple_cat(std::make_tuple(F(Args...)), + NoMasterOutputType()); + })); +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, std::function)> &&MF, + std::function(std::pair...)> &&F) noexcept { + using NoMasterOutputType = + std::tuple::Type>...>; + return createAgent( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF(Arg); + return NoMasterOutputType(); + }), + std::function< + std::tuple, Optional::Type>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + return std::tuple_cat(std::make_tuple(F(Args...)), + NoMasterOutputType()); + })); +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + using MasterOutputType = std::tuple...>; + return createAgent( + Name, + std::function)>( + [](std::pair) { return MasterOutputType(); }), + std::move(F)); +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); 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 { + std::function([=](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/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 46ca0c1..1760116 100755 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,257 +1,489 @@ //===-- 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" +/// Local helper macros to deal with built-in types. +/// +///@{ + +/// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor. +/// +/// \param N name suffix to use +#define DSMASTERHANDLERNAME(N) handleMaster_##N + +/// Defines member functions for handling messages from *master* in +/// \c rosa::deluxe::DeluxeSensor. +/// +/// \see \c DeluxeSensorMasterInputHandlers +/// +/// \note No pre- and post-conditions are validated directly by these functions, +/// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do +/// that. +/// +/// \param T the type of input to handle +/// \param N name suffix for the function identifier +#define DSMASTERHANDLERDEFN(T, N) \ + void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, \ + T Value) noexcept { \ + saveMasterInput(MasterId, Value); \ + } + +/// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments. +/// +/// \see \c DSMASTERHANDLERDEFN +/// +/// This macro can be used instead of \c DSMASTERHANDLERDEFN 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 DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T) + +/// Results in a \c THISMEMBER reference to a member function defined by +/// \c DSMASTERHANDLERDEFN. +/// +/// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super +/// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN. +/// +/// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER +/// +/// \param N name suffix for the function identifier +#define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N)) + +///@} + namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext class DeluxeSensor : public Agent { 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; + /// The type of values \p this object processes from its *master*. + /// + /// \see \c rosa::deluxe::DeluxeSensor::master + const TypeNumber MasterInputType; + private: + /// Indicates whether the input value from the *master* has been changed since + /// the last trigger received from the system. + /// + /// The flag is reset to \c false upon handling a trigger and then set to \c + /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new + /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. + bool MasterInputChanged; + + /// Stores the actual input value from *master*. + /// + /// \note The type of the stored value matches the type indicated by \c + /// rosa::deluxe::DeluxeSensor::MasterInputType. + const std::unique_ptr MasterInputValue; + /// 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 + /// \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. + /// The actual data source functions and master-input processing function are + /// captured in lambda expressions that are in turn wrapped in \c + /// std::function objects. The lambda expression calls a processing function, + /// either to handle master-input or obtain the next sensory value from data + /// source. The next sensory value is sent it to *master* by calling \c + /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c + /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current + /// value is passed to the master-input processing function. The function \c + /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper + /// function object. - /// Handles trigger during normal execution. + /// Processes master-input. + /// + /// \ingroup DeluxeSensorTriggerHandlers + /// + /// The function is called upon the sensor is trigged by the system. + const H MFP; + + /// Produces the next sensory value 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. + /// Produces the next sensory value 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; + /// Tells the unique identifier of the *master* of \p this object, if any + /// registered. + /// + /// \return the unique identifier of the *master* + /// + /// \pre A *master* is registered for \p this object: \code + /// Master + /// \endcode + id_t masterId(void) const noexcept; + + /// Wraps a master-input processing function into a trigger handler. + /// + /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers + /// + /// \tparam MT type of master-input processed by \p MF + /// + /// \param MF function that processes master-input + /// + /// \note A master-input type of \c rosa::unit_t indicates that \p this object + /// does not receive master-input, \p MF is never called if \p MT is \c + /// rosa::unit_t. + /// + /// \return trigger handler function based on \p MF + /// + /// \pre \p MT matches \c rosa::deluxe::DeluxeSensor::MasterInputType: \code + /// MasterInputType == TypeNumberOf::Value + /// \endcode + template + H triggerHandlerFromProcessingFunction( + std::function)> &&MF) noexcept; + /// Wraps a data source function into a trigger handler. /// - /// \see \c DeluxeSensorTriggerHandlers + /// \see \c rosa::deluxe::DeluxeSensor::FP, \c + /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// + /// \return trigger handler function based on \p F + /// /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeNumberOf::Value /// \endcode - template H triggerHandlerFromDataSource(D &&F) noexcept; + template + H triggerHandlerFromDataSource(std::function &&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. + /// \todo Enforce \p F and \p MF do not potentially throw exception. /// + /// \tparam MT type of master-input handled by \p MF /// \tparam T type of data to operate on /// + /// \note Instantiation fails if any of the type arguments \p T and \p MT is + /// not a built-in type. + /// + /// \note If \p MT is \c rosa::unit_t, the constructed object does not receive + /// master-input. + /// /// \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 MF function to process master-input values with /// \param F function to generate the next value with during normal operation /// - /// \pre Statically, \p T is a built-in type:\code - /// TypeListContains::Value + /// \pre Statically, \p MT and \p T are built-in types:\code + /// TypeListSubsetOf, BuiltinTypes>::Value /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode - template ::Value>> + template , BuiltinTypes>::Value>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, - MessagingSystem &S, D &&F) noexcept; + MessagingSystem &S, + std::function)> &&MF, + std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) 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*. /// + /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be + /// paired with a corresponding call of \c + /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that + /// input/output types of master and slave matches. + /// /// \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; + template + void registerSimulationDataSource(std::function &&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. + /// Handles master-input and generates the next sensory value upon trigger + /// from the system. /// - /// Executes \c rosa::deluxe::DeluxeSensor::FP or - /// \c rosa::deluxe::DeluxeSensor::SFP if set. + /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input + /// and data generating function \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; + + /// Stores a new input value from the *master*. + /// + /// The function stores \p Value in \c + /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the + /// corresponding flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. + /// + /// \note Utilized by member functions of group \c + /// DeluxeSensorMasterInputHandlers. + /// + /// \tparam T type of input to store + /// + /// \param Id unique identifier of the *master* + /// \param Value the input value to store + /// + /// \pre The *master* with \p Id is registered and the input from the *master* + /// is expected to be of type \p T: \code + /// Master && masterId() == Id && MasterInputType == TypeNumberOf::Value + /// \endcode + template void saveMasterInput(id_t Id, T Value) noexcept; + + /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of + /// rosa::deluxe::DeluxeSensor + /// + /// Definition of member functions handling messages from the *master* with + /// different types of input + /// + /// A *slave* generally needs to be prepared to deal with values of any + /// built-in type, except for \c rosa::unit_t, to handle messages from its + /// *master*. Each type requires a separate message handler, which are + /// implemented by these functions. The functions instantiate + /// \c rosa::deluxe::DeluxeSensor::saveMasterInput 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 + /// DSMASTERHANDLERDEF. + /// + /// \note Keep these definitions in sync with \c rosa::BuiltinTypes; but do no + /// include \c rosa::unit_t. + /// + ///@{ + + DSMASTERHANDLERDEF(AtomValue) + DSMASTERHANDLERDEF(int16_t) + DSMASTERHANDLERDEF(int32_t) + DSMASTERHANDLERDEF(int64_t) + DSMASTERHANDLERDEF(int8_t) + DSMASTERHANDLERDEFN(long double, long_double) + DSMASTERHANDLERDEFN(std::string, std__string) + DSMASTERHANDLERDEF(uint16_t) + DSMASTERHANDLERDEF(uint32_t) + DSMASTERHANDLERDEF(uint64_t) + DSMASTERHANDLERDEF(uint8_t) + DSMASTERHANDLERDEF(bool) + DSMASTERHANDLERDEF(double) + DSMASTERHANDLERDEF(float) + + /// @} }; +template +DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( + std::function)> &&MF) noexcept { + ASSERT(MasterInputType == TypeNumberOf::Value); + return [ this, MF ](void) noexcept { + // Do not do anything for master-input type \c rosa::unit_t. + if (MasterInputType != TypeNumberOf::Value) { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " handles master-input." + << std::endl; + const auto MasterInputArg = std::make_pair( + *static_cast(MasterInputValue->pointerTo(0)), + MasterInputChanged); + MasterInputChanged = false; + MF(MasterInputArg); + } + }; +} + template -DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(D &&F) noexcept { +DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource( + std::function &&F) noexcept { ASSERT(OutputType == TypeNumberOf::Value); - return [this, F](void) noexcept { sendToMaster(F()); }; + return [ this, F ](void) noexcept { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." + << std::endl; + sendToMaster(F()); + }; } -template +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)), + std::function)> &&MF, + std::function &&F) noexcept + : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), + DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), + DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), + DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), + DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), + DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), + DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(bool), + DSMASTERHANDLERREF(double), DSMASTERHANDLERREF(float)), OutputType(TypeNumberOf::Value), - FP(triggerHandlerFromDataSource(std::move(F))), - SFP(nullptr) { + MasterInputType(TypeNumberOf::Value), MasterInputChanged(false), + MasterInputValue(new TokenizedStorage()), + MFP(triggerHandlerFromProcessingFunction(std::move(MF))), + FP(triggerHandlerFromDataSource(std::move(F))), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE("DeluxeSensor is created."); } template -void DeluxeSensor::registerSimulationDataSource(D &&SF) noexcept { +void DeluxeSensor::registerSimulationDataSource( + std::function &&SF) noexcept { ASSERT(OutputType == TypeNumberOf::Value); SFP = triggerHandlerFromDataSource(std::move(SF)); } template void DeluxeSensor::sendToMaster(const T &Value) noexcept { ASSERT(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)); } } +template +void DeluxeSensor::saveMasterInput(id_t Id, T Value) noexcept { + ASSERT(Master && masterId() == Id && + MasterInputType == TypeNumberOf::Value); + + *static_cast(MasterInputValue->pointerTo(0)) = Value; + MasterInputChanged = true; +} + } // End namespace deluxe } // End namespace rosa +#undef DSMASTERHANDLEREF +#undef DSMASTERHANDLEDEF +#undef DSMASTERHANDLEDEFN +#undef DSMASTERHANDLENAME + #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp index 4388a4e..ed8071a 100755 --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,211 +1,233 @@ //===-- 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; 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 MT type of master-input the new \c rosa::deluxe::DeluxeSensor + /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor + /// \param MF function to process master-input values /// \param F function to generate the next value with during normal operation /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor - template + template AgentHandle createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept; + std::function)> &&MF, + std::function &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// + /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent + /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs + /// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent + /// produces /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent + /// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process + /// master-input values and generate master-output with /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process - /// input values and generate output with + /// input values and generate output and master-output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent - template - AgentHandle createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept; + template + AgentHandle createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&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 { +template +AgentHandle +DeluxeSystem::createSensor(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { - return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(F)); + return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF), + std::move(F)); }); return {DS}; } -template -AgentHandle -DeluxeSystem::createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept { - +template +AgentHandle DeluxeSystem::createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { - return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(F)); + return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF), + std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa #endif // ROSA_LIB_DELUXE_DELUXESYSTEM_HPP diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp index 78d1cd4..b32ac99 100755 --- a/lib/deluxe/DeluxeAgent.cpp +++ b/lib/deluxe/DeluxeAgent.cpp @@ -1,205 +1,235 @@ //===-- 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 namespace rosa { namespace deluxe { bool DeluxeAgent::inv(void) const noexcept { + // Check number of inputs and master-outputs. + if (NumberOfInputs != NumberOfMasterOutputs) { + return false; + } + // Check container sizes. if (!(InputTypes.size() == NumberOfInputs && InputChanged.size() == NumberOfInputs && InputValues->size() == NumberOfInputs && - Slaves.size() == NumberOfInputs)) { + MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs + && 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; + // Prepare master-output related info for the *slave*. + const TypeNumber MT = MasterOutputTypes[I]; + const bool hasMT = MT != TypeNumberOf::Value; + // \c Slave is not empty here. - // Check the `OutputType` of the registered *slave*. + // Check the `OutputType` and `MasterInputType` of the registered *slave*. const auto &A = unwrapAgent(*Slave); if (!((A.Kind == atoms::SensorKind && - static_cast(A).OutputType == T) || + static_cast(A).OutputType == T && + (!hasMT || + static_cast(A).MasterInputType == MT)) || (A.Kind == atoms::AgentKind && - static_cast(A).OutputType == T))) { + static_cast(A).OutputType == T && + (!hasMT || + static_cast(A).MasterInputType == MT)))) { 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. } +id_t DeluxeAgent::masterId(void) const noexcept { + ASSERT(inv() && Master); + return unwrapAgent(*Master).Id; +} + 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]; } +TypeNumber DeluxeAgent::masterOutputType(const size_t Pos) const noexcept { + ASSERT(inv() && Pos < NumberOfMasterOutputs); + return MasterOutputTypes[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]) || + InputTypes[Pos] && + (MasterOutputTypes[Pos] == TypeNumberOf::Value || + static_cast(unwrapAgent(*Slave)) + .MasterInputType == MasterOutputTypes[Pos])) || (unwrapAgent(*Slave).Kind == atoms::AgentKind && static_cast(unwrapAgent(*Slave)).OutputType == - InputTypes[Pos]))); + InputTypes[Pos] && + (MasterOutputTypes[Pos] == TypeNumberOf::Value || + static_cast(unwrapAgent(*Slave)) + .MasterInputType == MasterOutputTypes[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..10d9d5c 100755 --- a/lib/deluxe/DeluxeContext.cpp +++ b/lib/deluxe/DeluxeContext.cpp @@ -1,151 +1,155 @@ //===-- deluxe/DeluxeContext.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file deluxe/DeluxeContext.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \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."); } 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) { + } else if (A->inputType(Pos) != S->OutputType || + (A->masterOutputType(Pos) != TypeNumberOf::Value && + A->masterOutputType(Pos) != S->MasterInputType)) { 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) { + } else if (M->inputType(Pos) != S->OutputType || + (M->masterOutputType(Pos) != TypeNumberOf::Value && + M->masterOutputType(Pos) != S->MasterInputType)) { 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/DeluxeSensor.cpp b/lib/deluxe/DeluxeSensor.cpp index c55ef7f..1e9968e 100755 --- a/lib/deluxe/DeluxeSensor.cpp +++ b/lib/deluxe/DeluxeSensor.cpp @@ -1,63 +1,72 @@ //===-- 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" namespace rosa { namespace deluxe { +id_t DeluxeSensor::masterId(void) const noexcept { + ASSERT(Master); + return unwrapAgent(*Master).Id; +} + DeluxeSensor::~DeluxeSensor(void) noexcept { 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 = {}; } } Optional DeluxeSensor::master(void) const noexcept { return Master; } void DeluxeSensor::registerMaster(const Optional _Master) noexcept { ASSERT(!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind); Master = _Master; } void DeluxeSensor::clearSimulationDataSource(void) noexcept { SFP = nullptr; } bool DeluxeSensor::simulationDataSourceIsSet(void) const noexcept { return SFP != nullptr; } void DeluxeSensor::handleTrigger(atoms::Trigger) noexcept { + // Process master-input. + MFP(); + + // Obtain the next sensory value. // Use \c rosa::deluxe::DeluxeSensor::SFP if set, otherwise // \c rosa::deluxe::DeluxeSensor::FP. const H &F = SFP ? SFP : FP; F(); } } // End namespace deluxe } // End namespace rosa