diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp index f465933..82621ac 100755 --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,178 +1,272 @@ //===-- 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-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 = 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 + // with the default value of the last argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. + + // The first and second sensors do not receive master-input. + AgentHandle BoolSensor = C->createSensor("BoolSensor"); AgentHandle IntSensor = C->createSensor("IntSensor"); - AgentHandle FloatSensor = C->createSensor("FloatSensor"); + + // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM. + const std::string FloatSensorName = "FloatSensor"; + AgentHandle FloatSensor = C->createSensor( + FloatSensorName, [&FloatSensorName](std::pair I) { + LOG_INFO_STREAM << "\n******\n" + << FloatSensorName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + }); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); - AgentHandle IntAgent = createLowLevelAgent(C, "IntAgent"); - AgentHandle FloatAgent = createLowLevelAgent(C, "FloatAgent"); + // All agents below dump their received values to \c LOG_INFO_STREAM on each + // triggering. + + // This agent does not receive master-input and does not produce + // master-output. It results in the value it received. + const std::string BoolAgentName = "BoolAgent"; + using BoolResult = Optional; + using BoolHandler = std::function)>; + AgentHandle BoolAgent = C->createAgent( + BoolAgentName, + BoolHandler([&BoolAgentName](std::pair I) -> BoolResult { + LOG_INFO_STREAM << "\n******\n" + << BoolAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {I.first}; + })); + + // This agent receives master-input but does not produce master-output. The + // agent maintains a state in \c IntAgentOffset. The master-input handler + // updates \c IntAgentOffset according to each received (new) value from its + // master. The slave-input handler results in the sum of the received value + // and the actual value of \c IntAgentOffset. + const std::string IntAgentName = "IntAgent"; + using IntMasterHandler = std::function)>; + using IntResult = Optional; + using IntHandler = std::function)>; + uint32_t IntAgentOffset = 0; + AgentHandle IntAgent = C->createAgent( + IntAgentName, + // Master-input handler. + IntMasterHandler([&IntAgentName, + &IntAgentOffset](std::pair I) { + LOG_INFO_STREAM << "\n******\n" + << IntAgentName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + if (I.second) { + IntAgentOffset = I.first; + } + }), + // Slave-input handler. + IntHandler([&IntAgentName, + &IntAgentOffset](std::pair I) -> IntResult { + LOG_INFO_STREAM << "\n******\n" + << IntAgentName << " " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {I.first + IntAgentOffset}; + })); + + // This agent receives master-input and produces master-output. The + // master-input handler propagaates each received (new) value to its slave as + // master-output. The slave-input handler results in the value it received and + // produces no actual master-output. + const std::string FloatAgentName = "FloatAgent"; + using FloatMasterResult = std::tuple>; + using FloatMasterHandler = + std::function)>; + using FloatResult = std::tuple, Optional>; + using FloatHandler = std::function)>; + AgentHandle FloatAgent = C->createAgent( + FloatAgentName, + // Master-input handler. + FloatMasterHandler([&FloatAgentName]( + std::pair I) -> FloatMasterResult { + LOG_INFO_STREAM << "\n******\n" + << FloatAgentName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + const auto Output = + I.second ? Optional(I.first) : Optional(); + return {Output}; + }), + // Slave-input handler. + FloatHandler([&FloatAgentName](std::pair I) -> FloatResult { + LOG_INFO_STREAM << "\n******\n" + << FloatAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {{I.first}, {}}; + })); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); + C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel"); 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. + // This agent does not receive master-input but produces master-output for its + // slaves at positions `1` and `2` but not for that at position `0`. The agent + // maintains a state in \c SumAgentState. The handler increments \c + // SumAgentState upon each received (new) `true` value from its slave at + // position `0`. Whenever \c SumAgentState has been updated, it is sent to the + // slaves at positions `1` and `2`. The handler results in the sum of the + // values received from slaves at positions `1` and `2`. + using SumResult = std::tuple, Optional, + Optional, Optional>; + using SumHandler = std::function, std::pair, std::pair)>; + uint32_t SumAgentState = 0; AgentHandle SumAgent = C->createAgent( - "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}; - })); + "Sum Agent", + SumHandler([&SumAgentState](std::pair I0, + std::pair I1, + std::pair I2) -> SumResult { + LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" + << (I0.second ? "" : "") + << " bool value: " << I0.first << "\n" + << (I1.second ? "" : "") + << " int value: " << I1.first << "\n" + << (I2.second ? "" : "") + << " float value: " << I2.first << "\n******\n"; + if (I0.second && I0.first) { + ++SumAgentState; + } + const auto MasterOutput = I0.second && I0.first + ? Optional(SumAgentState) + : Optional(); + const auto Output = I1.first + I2.first; + return {{Output}, {}, {MasterOutput}, {MasterOutput}}; + })); // // 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"); + C->connectAgents(SumAgent, 0, BoolAgent, "Bool Agent Channel"); + C->connectAgents(SumAgent, 1, IntAgent, "Int Agent Channel"); + C->connectAgents(SumAgent, 2, 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. + // The agent dumps each received (new) value to \c LOG_INFO_STREAM and + // produces nothing; does not receive mater-input and does not produce + // master-output. AgentHandle LoggerAgent = C->createAgent("Logger Agent", 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 BoolValues(NumberOfSimulationCycles); + std::generate(BoolValues.begin(), BoolValues.end(), + [i = 0](void) mutable -> bool { return (++i % 4) == 0; }); + C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end()); + 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; }