diff --git a/apps/sa-ews0/sa-ews0.cpp b/apps/sa-ews0/sa-ews0.cpp index 3cfcb30..e73a7dc 100644 --- a/apps/sa-ews0/sa-ews0.cpp +++ b/apps/sa-ews0/sa-ews0.cpp @@ -1,387 +1,388 @@ //===-- apps/sa-ews0/sa-ews0.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS0 // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews0/sa-ews0.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2020 /// /// \brief The application SA-EWS0 implements the conventional Early Warning /// Score (EWS) system. //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/config/version.h" #include "rosa/app/Application.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "cxxopts/cxxopts.hpp" #include #include using namespace rosa; using namespace rosa::agent; using namespace rosa::app; using namespace rosa::terminal; using namespace rosa::csv; using namespace rosa::iterator; const std::string AppName = "SA-EWS0"; /// Representation type of warning levels. /// \note Make sure it suits all defined enumeration values. using WarningScoreType = uint8_t; /// Warning levels for abstraction. enum WarningScore : WarningScoreType { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Helper function creating an application agent for pre-processing sensory /// values. /// /// Received 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 WarningScoreType /// because enumeration types are not integrated into built-in types. Hence, a /// master to these agents receives its input as \c WarningScoreType values, and /// may cast them to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param App the application to create the agent in /// \param Name name of the new agent /// \param A abstraction to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &App, const std::string &Name, const Abstraction &A) { using result = Optional; using handler = std::function)>; return App->createAgent( Name, handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; return {A(I.first)}; })); } /// Helper function to print an error message in red color to the terminal and /// exit from the application. /// /// \note The function never returns as it calles `exit()`. /// /// \param Error error message /// \param ExitCode exit code to return from the application void logErrorAndExit(const std::string &Error, const int ExitCode) { LOG_ERROR_STREAM << Color::Red << Error << Color::Default << std::endl; exit(ExitCode); } int main(int argc, char *argv[]) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << " app" << Color::Default << '\n'; /// Paths for the CSV files for simulation. /// ///@{ std::string DataCSVPath; std::string ScoreCSVPath; ///@} /// Whether CSV files have header row at the beginning. bool CSVHeader = false; /// Delimiter character in CSV files. char CSVDelimiter = ','; /// How many cycles of simulation to perform. size_t NumberOfSimulationCycles = 16; // Handle command-line arguments. try { cxxopts::Options Options(argv[0], library_string() + " -- " + AppName); Options.add_options()("i,input", "Path for the CSV file providing input data", cxxopts::value(DataCSVPath), "file") ("o,output", "Path for the CSV file to write output scores", cxxopts::value(ScoreCSVPath), "file") ("header", "CSV input file has header row", cxxopts::value(CSVHeader)->default_value("false")) ("delimiter", "CSV delimiter character", cxxopts::value(CSVDelimiter)->default_value(","), "char") ("c,cycles", "Number of simulation cycles to perform", cxxopts::value(NumberOfSimulationCycles)->default_value("16"), "int") ("h,help", "Print usage"); auto Args = Options.parse(argc, argv); if (Args.count("help")) { LOG_INFO_STREAM << '\n' << Options.help() << std::endl; exit(0); } if (Args.count("input") == 0) { throw std::invalid_argument("Argument --input must be defined."); } if (Args.count("output") == 0) { throw std::invalid_argument("Argument --output must be defined."); } } catch (const cxxopts::OptionException &e) { logErrorAndExit(e.what(), 1); } catch (const std::invalid_argument &e) { logErrorAndExit(e.what(), 1); } std::unique_ptr App = Application::create(AppName); // // Relevant types and definitions. // using HRType = int32_t; using BRType = int32_t; using SpO2Type = int32_t; using BPSysType = int32_t; using BodyTempType = float; // // 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 = App->createSensor("HR Sensor"); AgentHandle BRSensor = App->createSensor("BR Sensor"); AgentHandle SpO2Sensor = App->createSensor("SpO2 Sensor"); AgentHandle BPSysSensor = App->createSensor("BPSys Sensor"); AgentHandle BodyTempSensor = App->createSensor("BodyTemp Sensor"); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // 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(App, "HR Agent", HRAbstraction); AgentHandle BRAgent = createLowLevelAgent(App, "BR Agent", BRAbstraction); AgentHandle SpO2Agent = createLowLevelAgent(App, "SpO2 Agent", SpO2Abstraction); AgentHandle BPSysAgent = createLowLevelAgent(App, "BPSys Agent", BPSysAbstraction); AgentHandle BodyTempAgent = createLowLevelAgent(App, "BodyTemp Agent", BodyTempAbstraction); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); App->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); App->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); App->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); App->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); App->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 = App->createAgent( "Body Agent", 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"; + LOG_INFO_STREAM + << "\n*******\nBody Agent trigged with values:\n" + << (HR.second ? "" : "") + << " HR warning score: " << PRINTABLE(HR.first) << "\n" + << (BR.second ? "" : "") + << " BR warning score: " << PRINTABLE(BR.first) << "\n" + << (SpO2.second ? "" : "") + << " SpO2 warning score: " << PRINTABLE(SpO2.first) << "\n" + << (BPSys.second ? "" : "") + << " BPSys warning score: " << PRINTABLE(BPSys.first) << "\n" + << (BodyTemp.second ? "" : "") + << " BodyTemp warning score: " << PRINTABLE(BodyTemp.first) + << "\n******\n"; const std::array Values{ HR.first, BR.first, SpO2.first, BPSys.first, BodyTemp.first}; const WarningScoreType ews = std::reduce(Values.begin(), Values.end(), (WarningScoreType)0); return {ews}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); App->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); App->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); App->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); App->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); App->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, CSVDelimiter); // The agent writes each new input value into a CSV file and produces nothing. AgentHandle LoggerAgent = App->createAgent( "Logger Agent", 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."); App->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // App->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. // Type aliases for iterators. using CSVDataIterator = CSVIterator; const auto CSVHeaderInfo = CSVHeader ? HeaderInformation::HasHeader : HeaderInformation::HasNoHeader; std::ifstream DataCSV(DataCSVPath); auto [HRRange, BRRange, SpO2Range, BPSysRange, BodyTempRange] = splitTupleIterator( CSVDataIterator(DataCSV, 0, CSVHeaderInfo, CSVDelimiter), CSVDataIterator()); App->registerSensorValues(HRSensor, std::move(begin(HRRange)), end(HRRange)); App->registerSensorValues(BRSensor, std::move(begin(BRRange)), end(BRRange)); App->registerSensorValues(SpO2Sensor, std::move(begin(SpO2Range)), end(SpO2Range)); App->registerSensorValues(BPSysSensor, std::move(begin(BPSysRange)), end(BPSysRange)); App->registerSensorValues(BodyTempSensor, std::move(begin(BodyTempRange)), end(BodyTempRange)); // // Simulate. // App->simulate(NumberOfSimulationCycles); return 0; } diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp index 851459c..e5aeae6 100644 --- a/apps/sa-ews1/sa-ews1.cpp +++ b/apps/sa-ews1/sa-ews1.cpp @@ -1,620 +1,622 @@ //===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS1 // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews1/sa-ews1.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// Maximilian Goetzinger (maxgot@utu.fi) /// /// \date 2017-2020 /// /// \brief The application SA-EWS1 implements the case study from the paper: /// M. Götzinger, A. Anzanpour, I. Azimi, N. TaheriNejad, and A. M. Rahmani: /// Enhancing the Self-Aware Early Warning Score System Through Fuzzified Data /// Reliability Assessment. DOI: 10.1007/978-3-319-98551-0_1 //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/CrossCombinator.h" #include "rosa/agent/ReliabilityConfidenceCombination.h" #include "rosa/config/version.h" #include "rosa/app/Application.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "cxxopts/cxxopts.hpp" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::app; using namespace rosa::terminal; using namespace rosa::csv; using namespace rosa::iterator; const std::string AppName = "SA-EWS1"; /// Representation type of warning levels. /// \note Make sure it suits all defined enumeration values. using WarningScoreType = uint8_t; /// Warning levels for abstraction. enum WarningScore : WarningScoreType { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Vector with all possible warning levels. std::vector warningScores = {No, Low, High, Emergency}; /// Type used to represent reliability values. using ReliabilityType = double; /// The result type of low-level functions. using WarningValue = AppTuple; /// Helper function creating an application sensor and setting its execution /// policy for decimation. /// /// \note The sensors are created without defining a normal generator function, /// which is suitable for simulation only. /// /// \tparam T type of values for the sensor to generate /// /// \param App the application to create the sensor in /// \param Name name of the new sensor /// \param Decimation the decimation parameter /// /// \return handle for the new sensor template AgentHandle createSensor(std::unique_ptr &App, const std::string &Name, const size_t Decimation) { AgentHandle Sensor = App->createSensor(Name); App->setExecutionPolicy(Sensor, AppExecutionPolicy::decimation(Decimation)); return Sensor; } /// Helper function creating an application agent for pre-processing sensory /// values and setting its execution policy for decimation. /// /// Received values are assessed for reliability and abstracted into a \c /// WarningScore. The result of the processing function is a pair of assessed /// reliability and abstracted values. /// /// \note The result, \c WarningScore, is returned as \c WarningScoreType /// because enumeration types are not integrated into built-in types. Hence, a /// master to these agents receives its input as \c WarningScoreType values, and /// may cast them to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param App the application to create the agent in /// \param Name name of the new agent /// \param Decimation the decimation parameter /// \param A abstraction to use /// \param R reliability/confidence combination to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent( std::unique_ptr &App, const std::string &Name, const size_t Decimation, const Abstraction &A, ReliabilityAndConfidenceCombination &R) { using result = Optional; using input = AppTuple; using handler = std::function)>; AgentHandle Agent = App->createAgent( Name, handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") - << " value: " << std::get<0>(I.first) << "\n******\n"; + << " value: " << PRINTABLE(std::get<0>(I.first)) + << "\n******\n"; const auto SensorValue = std::get<0>(I.first); const WarningScoreType Score = A(SensorValue); const ReliabilityType InputReliability = R.getInputReliability(SensorValue); return {WarningValue(Score, InputReliability)}; })); App->setExecutionPolicy(Agent, AppExecutionPolicy::decimation(Decimation)); return Agent; } /// Helper function to print an error message in red color to the terminal and /// exit from the application. /// /// \note The function never returns as it calles `exit()`. /// /// \param Error error message /// \param ExitCode exit code to return from the application void logErrorAndExit(const std::string &Error, const int ExitCode) { LOG_ERROR_STREAM << Color::Red << Error << Color::Default << std::endl; exit(ExitCode); } int main(int argc, char *argv[]) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << " app" << Color::Default << '\n'; /// Paths for the CSV files for simulation. /// ///@{ std::string DataCSVPath; std::string ScoreCSVPath; ///@} /// Whether CSV files have header row at the beginning. bool CSVHeader = false; /// Delimiter character in CSV files. char CSVDelimiter = ','; /// Decimation of sensors and agents. size_t Decimation = 1; /// How many cycles of simulation to perform. size_t NumberOfSimulationCycles = 16; // Handle command-line arguments. try { cxxopts::Options Options(argv[0], library_string() + " -- " + AppName); Options.add_options()("i,input", "Path for the CSV file providing input data", cxxopts::value(DataCSVPath), "file") ("o,output", "Path for the CSV file to write output scores", cxxopts::value(ScoreCSVPath), "file") ("header", "CSV files have header row", cxxopts::value(CSVHeader)->default_value("false")) ("delimiter", "CSV delimiter character", cxxopts::value(CSVDelimiter)->default_value(","), "char") ("d,decimation", "Decimation of sensors and agents", cxxopts::value(Decimation)->default_value("1"), "int") ("c,cycles", "Number of simulation cycles to perform", cxxopts::value(NumberOfSimulationCycles)->default_value("16"), "int") ("h,help", "Print usage"); auto Args = Options.parse(argc, argv); if (Args.count("help")) { LOG_INFO_STREAM << '\n' << Options.help() << std::endl; exit(0); } if (Args.count("input") == 0) { throw std::invalid_argument("Argument --input must be defined."); } if (Args.count("output") == 0) { throw std::invalid_argument("Argument --output must be defined."); } } catch (const cxxopts::OptionException &e) { logErrorAndExit(e.what(), 1); } catch (const std::invalid_argument &e) { logErrorAndExit(e.what(), 1); } std::unique_ptr App = Application::create(AppName); // // Relevant types and definitions. // using HRType = int32_t; using HRParFun = PartialFunction; using HRLinFun = LinearFunction; using BRType = int32_t; using BRParFun = PartialFunction; using BRLinFun = LinearFunction; using SpO2Type = int32_t; using SpO2ParFun = PartialFunction; using SpO2LinFun = LinearFunction; using BPSysType = int32_t; using BPSysParFun = PartialFunction; using BPSysLinFun = LinearFunction; using BodyTempType = float; using BodyTempParFun = PartialFunction; using BodyTempLinFun = LinearFunction; // // Create application 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 = createSensor(App, "HR Sensor", Decimation); AgentHandle BRSensor = createSensor(App, "BR Sensor", Decimation); AgentHandle SpO2Sensor = createSensor(App, "SpO2 Sensor", Decimation); AgentHandle BPSysSensor = createSensor(App, "BPSys Sensor", Decimation); AgentHandle BodyTempSensor = createSensor(App, "BodyTemp Sensor", Decimation); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // 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); // // Define reliabilities. // ReliabilityAndConfidenceCombination HRReliability; HRReliability.setTimeStep(1); std::shared_ptr> HRAbsRel( new HRParFun({{{0, 200}, std::make_shared(1, 0)}, {{200, 300}, std::make_shared(200, 1, 300, 0)}}, 0)); HRReliability.setAbsoluteReliabilityFunction(HRAbsRel); std::shared_ptr> HRRelSlope(new HRParFun( {{{-200, -100}, std::make_shared(-200, 0, -100, 1)}, {{-100, 100}, std::make_shared(1, 0)}, {{100, 200}, std::make_shared(100, 1, 200, 0)}}, 0)); HRReliability.setReliabilitySlopeFunction(HRRelSlope); ReliabilityAndConfidenceCombination BRReliability; BRReliability.setTimeStep(1); std::shared_ptr> BRAbsRel( new BRParFun({{{0, 40}, std::make_shared(1, 0)}, {{40, 60}, std::make_shared(40, 1, 60, 0)}}, 0)); BRReliability.setAbsoluteReliabilityFunction(BRAbsRel); std::shared_ptr> BRRelSlope( new BRParFun({{{-30, -20}, std::make_shared(-30, 0, -20, 1)}, {{-20, 20}, std::make_shared(1, 0)}, {{20, 30}, std::make_shared(20, 1, 30, 0)}}, 0)); BRReliability.setReliabilitySlopeFunction(BRRelSlope); ReliabilityAndConfidenceCombination SpO2Reliability; SpO2Reliability.setTimeStep(1); std::shared_ptr> SpO2AbsRel( new SpO2ParFun( { {{0, 100}, std::make_shared(1, 0)}, }, 0)); SpO2Reliability.setAbsoluteReliabilityFunction(SpO2AbsRel); std::shared_ptr> SpO2RelSlope( new SpO2ParFun({{{-8, -5}, std::make_shared(-8, 0, -5, 1)}, {{-5, 5}, std::make_shared(1, 0)}, {{5, 8}, std::make_shared(5, 1, 8, 0)}}, 0)); SpO2Reliability.setReliabilitySlopeFunction(SpO2RelSlope); ReliabilityAndConfidenceCombination BPSysReliability; BPSysReliability.setTimeStep(1); std::shared_ptr> BPSysAbsRel( new BPSysParFun( {{{0, 260}, std::make_shared(1, 0)}, {{260, 400}, std::make_shared(260, 1, 400, 0)}}, 0)); BPSysReliability.setAbsoluteReliabilityFunction(BPSysAbsRel); std::shared_ptr> BPSysRelSlope( new BPSysParFun( {{{-100, -50}, std::make_shared(-100, 0, -50, 1)}, {{-50, 50}, std::make_shared(1, 0)}, {{50, 100}, std::make_shared(50, 1, 100, 0)}}, 0)); BPSysReliability.setReliabilitySlopeFunction(BPSysRelSlope); ReliabilityAndConfidenceCombination BodyTempReliability; BodyTempReliability.setTimeStep(1); std::shared_ptr> BodyTempAbsRel( new BodyTempParFun( {{{-70.f, -50.f}, std::make_shared(-70.f, 0, -50.f, 1)}, {{-50.f, 40.f}, std::make_shared(1, 0)}, {{40.f, 60.f}, std::make_shared(40.f, 1, 60.f, 0)}}, 0)); BodyTempReliability.setAbsoluteReliabilityFunction(BodyTempAbsRel); std::shared_ptr> BodyTempRelSlope( new BodyTempParFun( {{{-0.1f, -0.05f}, std::make_shared(-0.1f, 0, -0.05f, 1)}, {{-0.05f, 0.05f}, std::make_shared(1, 0)}, {{0.05f, 0.1f}, std::make_shared(0.05f, 1, 0.1f, 0)}}, 0)); BodyTempReliability.setReliabilitySlopeFunction(BodyTempRelSlope); // // Create low-level application agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle HRAgent = createLowLevelAgent(App, "HR Agent", Decimation, HRAbstraction, HRReliability); AgentHandle BRAgent = createLowLevelAgent(App, "BR Agent", Decimation, BRAbstraction, BRReliability); AgentHandle SpO2Agent = createLowLevelAgent(App, "SpO2 Agent", Decimation, SpO2Abstraction, SpO2Reliability); AgentHandle BPSysAgent = createLowLevelAgent( App, "BPSys Agent", Decimation, BPSysAbstraction, BPSysReliability); AgentHandle BodyTempAgent = createLowLevelAgent(App, "BodyTemp Agent", Decimation, BodyTempAbstraction, BodyTempReliability); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); App->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); App->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); App->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); App->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); App->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); // // Create a high-level application agent. // LOG_INFO("Create high-level agent."); // Slave positions on BodyAgent. enum SlaveIndex : rosa::id_t { HRIdx = 0, BRIdx = 1, SpO2Idx = 2, BPSysIdx = 3, BodyTempIdx = 4 }; CrossCombinator BodyCrossCombinator; BodyCrossCombinator.setCrossLikelinessParameter(1.5); using WarningLikelinessFun = LikelinessFunction; std::shared_ptr BRCrossLikelinessFun( new WarningLikelinessFun(0.6)); BodyCrossCombinator.addCrossLikelinessProfile(HRIdx, BRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, HRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, SpO2Idx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, BPSysIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, BodyTempIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(SpO2Idx, BRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BPSysIdx, BRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BodyTempIdx, BRIdx, BRCrossLikelinessFun); std::shared_ptr HRBPCrossLikelinessFun( new WarningLikelinessFun(2.5)); BodyCrossCombinator.addCrossLikelinessProfile(HRIdx, BPSysIdx, HRBPCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BPSysIdx, HRIdx, HRBPCrossLikelinessFun); // The new agent logs its input values and results in a pair of the sum of // received warning scores and their cross-reliability. AgentHandle BodyAgent = App->createAgent( "Body Agent", 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 result: " << HR.first << "\n" + << " HR result: " << PRINTABLE(HR.first) << "\n" << (BR.second ? "" : "") - << " BR result: " << BR.first << "\n" + << " BR result: " << PRINTABLE(BR.first) << "\n" << (SpO2.second ? "" : "") - << " SpO2 result: " << SpO2.first << "\n" + << " SpO2 result: " << PRINTABLE(SpO2.first) << "\n" << (BPSys.second ? "" : "") - << " BPSys result: " << BPSys.first << "\n" + << " BPSys result: " << PRINTABLE(BPSys.first) + << "\n" << (BodyTemp.second ? "" : "") - << " BodyTemp result: " << BodyTemp.first + << " BodyTemp result: " << PRINTABLE(BodyTemp.first) << "\n******\n"; using ValueType = std::tuple; const std::vector SlaveValues{ {HRIdx, std::get<0>(HR.first), std::get<1>(HR.first)}, {BRIdx, std::get<0>(BR.first), std::get<1>(BR.first)}, {SpO2Idx, std::get<0>(SpO2.first), std::get<1>(SpO2.first)}, {BPSysIdx, std::get<0>(BPSys.first), std::get<1>(BPSys.first)}, {BodyTempIdx, std::get<0>(BodyTemp.first), std::get<1>(BodyTemp.first)}}; const ReliabilityType crossReliability = BodyCrossCombinator.getOutputLikeliness(SlaveValues); struct ScoreSum { void operator()(const ValueType &V) { ews += std::get<1>(V); } WarningScoreType ews{0}; }; const ScoreSum scoreSum = std::for_each( SlaveValues.cbegin(), SlaveValues.cend(), ScoreSum()); return {WarningValue(scoreSum.ews, crossReliability)}; })); App->setExecutionPolicy(BodyAgent, AppExecutionPolicy::decimation(Decimation)); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); App->connectAgents(BodyAgent, HRIdx, HRAgent, "HR Agent Channel"); App->connectAgents(BodyAgent, BRIdx, BRAgent, "BR Agent Channel"); App->connectAgents(BodyAgent, SpO2Idx, SpO2Agent, "SpO2 Agent Channel"); App->connectAgents(BodyAgent, BPSysIdx, BPSysAgent, "BPSys Agent Channel"); App->connectAgents(BodyAgent, BodyTempIdx, 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::CSVTupleWriter ScoreWriter(ScoreCSV, CSVDelimiter); if (CSVHeader) { ScoreWriter.writeHeader({"EWS", "Reliability"}); } // The agent writes each new input value into a CSV file and produces nothing. // \note The execution of the logger is not subject to decimation. using logger_result = AppTuple; AgentHandle LoggerAgent = App->createAgent( "Logger Agent", std::function(std::pair)>( [&ScoreWriter]( std::pair Score) -> Optional { // 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."); App->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize application for simulation. // App->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. using CSVDataIterator = CSVIterator; const auto CSVHeaderInfo = CSVHeader ? HeaderInformation::HasHeader : HeaderInformation::HasNoHeader; std::ifstream DataCSV(DataCSVPath); auto [HRRange, BRRange, SpO2Range, BPSysRange, BodyTempRange] = splitTupleIterator( CSVDataIterator(DataCSV, 0, CSVHeaderInfo, CSVDelimiter), CSVDataIterator()); App->registerSensorValues(HRSensor, std::move(begin(HRRange)), end(HRRange)); App->registerSensorValues(BRSensor, std::move(begin(BRRange)), end(BRRange)); App->registerSensorValues(SpO2Sensor, std::move(begin(SpO2Range)), end(SpO2Range)); App->registerSensorValues(BPSysSensor, std::move(begin(BPSysRange)), end(BPSysRange)); App->registerSensorValues(BodyTempSensor, std::move(begin(BodyTempRange)), end(BodyTempRange)); // // Simulate. // App->simulate(NumberOfSimulationCycles); return 0; } diff --git a/apps/sa-ews2/sa-ews2.cpp b/apps/sa-ews2/sa-ews2.cpp index 1dac18f..6cd706f 100644 --- a/apps/sa-ews2/sa-ews2.cpp +++ b/apps/sa-ews2/sa-ews2.cpp @@ -1,837 +1,841 @@ //===-- apps/sa-ews2/sa-ews2.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS2 // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews2/sa-ews2.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// Maximilian Goetzinger (maxgot@utu.fi) /// /// \date 2020 /// /// \brief The application SA-EWS2 implements the case study from the paper: /// M. Götzinger, A. Anzanpour, I. Azimi, N. TaheriNejad, A. Jantsch, /// A. M. Rahmani, and P. Liljeberg: Confidence-Enhanced Early Warning Score /// Based on Fuzzy Logic. DOI: 10.1007/s11036-019-01324-5 //===----------------------------------------------------------------------===// #include "rosa/agent/RangeConfidence.hpp" #include "rosa/agent/CrossCombinator.h" #include "rosa/agent/ReliabilityConfidenceCombination.h" #include "rosa/config/version.h" #include "rosa/app/Application.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "cxxopts/cxxopts.hpp" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::app; using namespace rosa::terminal; using namespace rosa::csv; using namespace rosa::iterator; const std::string AppName = "SA-EWS2"; /// Representation type of warning levels. /// \note Make sure it suits all defined enumeration values. using WarningScoreType = uint8_t; /// Warning levels for abstraction. /// /// \note Conversion functions \c feedbackFromSymbol(), \c feedbackToSymbol(), /// and the master input handler generated by \c createLowLevelAgent() expect /// enumeration values to constitute a consecutive range of non-negative /// integers starting from \c 0. enum WarningScore : WarningScoreType { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Vector with all possible warning levels. std::vector warningScores = {No, Low, High, Emergency}; /// Type used to represent reliability values. using ReliabilityType = double; /// The result type of low-level functions. using WarningValue = AppTuple; /// The helper struct used by the Likeliness Combinator to provide feedback. using EWSSymbol = Symbol; /// All feedback calculated by Likelienss Combinator for one slave. using EWSFeedback = std::vector; /// The feedback provided by master to a slave agent. /// The tuple contains the reliability feedback for each warning level in order. using FeedbackTuple = AppTuple; /// Converts feedback calculated by Likeliness Combinator into a tuple that can /// be sent between agents. /// /// \param F calculated standard feedback /// /// \return feedback tuple FeedbackTuple feedbackFromSymbol(const EWSFeedback &F) { // Fill a map with feedback for warning levels. std::map M; std::for_each(F.cbegin(), F.cend(), [&M](const EWSSymbol &S) { M.insert({S.Identifier, S.Likeliness}); }); // Expect feedback for all warning levels. ASSERT(M.size() == warningScores.size()); return {M[No], M[Low], M[High], M[Emergency]}; } /// Converts feedback tuple to standard format used by the Likeliness Combinator. /// /// \param F feedback tuple /// /// \return feedback in standard format EWSFeedback feedbackToSymbol(const FeedbackTuple &F) { return {{No, std::get(F)}, {Low, std::get(F)}, {High, std::get(F)}, {Emergency, std::get(F)}}; } /// Helper function creating an application sensor and setting its execution /// policy for decimation. /// /// \note The sensors are created without defining a normal generator function, /// which is suitable for simulation only. /// /// \tparam T type of values for the sensor to generate /// /// \param App the application to create the sensor in /// \param Name name of the new sensor /// \param Decimation the decimation parameter /// /// \return handle for the new sensor template AgentHandle createSensor(std::unique_ptr &App, const std::string &Name, const size_t Decimation) { AgentHandle Sensor = App->createSensor(Name); App->setExecutionPolicy(Sensor, AppExecutionPolicy::decimation(Decimation)); return Sensor; } /// Helper function creating an application agent for pre-processing sensory /// values and setting its execution policy for decimation. /// /// Received values are assessed for reliability and abstracted into a \c /// WarningScore. The result of the processing function is a pair of assessed /// reliability and abstracted values. /// /// The agent also accepts feedback from its master, which is taken into account /// for the pre-processing of later sensory values. /// /// \note The result, \c WarningScore, is returned as \c WarningScoreType /// because enumeration types are not integrated into built-in types. Hence, a /// master to these agents receives its input as \c WarningScoreType values, and /// may cast them to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param App the application to create the agent in /// \param Name name of the new agent /// \param Decimation the decimation parameter /// \param R reliability/confidence combination to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &App, const std::string &Name, const size_t Decimation, ReliabilityAndConfidenceCombination &R) { using masterhandler = std::function)>; using result = Optional; using input = AppTuple; using handler = std::function)>; AgentHandle Agent = App->createAgent( Name, masterhandler([&, Name](std::pair I) { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") - << " master feedback: [" << std::get(I.first) - << ", " << std::get(I.first) << ", " - << std::get(I.first) << ", " - << std::get(I.first) << "]\n******\n"; + << " master feedback: [" + << PRINTABLE(std::get(I.first)) << ", " + << PRINTABLE(std::get(I.first)) << ", " + << PRINTABLE(std::get(I.first)) << ", " + << PRINTABLE(std::get(I.first)) + << "]\n******\n"; const auto Feedback = feedbackToSymbol(I.first); R.feedback(Feedback); }), handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") - << " value: " << std::get<0>(I.first) << "\n******\n"; + << " value: " << PRINTABLE(std::get<0>(I.first)) + << "\n******\n"; const auto SensorValue = std::get<0>(I.first); const auto AbstractedValue = R.bestSymbol(SensorValue); return {WarningValue(AbstractedValue.Identifier, AbstractedValue.Likeliness)}; })); App->setExecutionPolicy(Agent, AppExecutionPolicy::decimation(Decimation)); return Agent; } /// Helper function to print an error message in red color to the terminal and /// exit from the application. /// /// \note The function never returns as it calles `exit()`. /// /// \param Error error message /// \param ExitCode exit code to return from the application void logErrorAndExit(const std::string &Error, const int ExitCode) { LOG_ERROR_STREAM << Color::Red << Error << Color::Default << std::endl; exit(ExitCode); } int main(int argc, char *argv[]) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << " app" << Color::Default << '\n'; /// Paths for the CSV files for simulation. /// ///@{ std::string DataCSVPath; std::string ScoreCSVPath; ///@} /// Whether CSV files have header row at the beginning. bool CSVHeader = false; /// Delimiter character in CSV files. char CSVDelimiter = ','; /// Decimation of sensors and agents. size_t Decimation = 1; /// How many cycles of simulation to perform. size_t NumberOfSimulationCycles = 128; // Handle command-line arguments. try { cxxopts::Options Options(argv[0], library_string() + " -- " + AppName); Options.add_options()("i,input", "Path for the CSV file providing input data", cxxopts::value(DataCSVPath), "file") ("o,output", "Path for the CSV file to write output scores", cxxopts::value(ScoreCSVPath), "file") ("header", "CSV files have header row", cxxopts::value(CSVHeader)->default_value("false")) ("delimiter", "CSV delimiter character", cxxopts::value(CSVDelimiter)->default_value(","), "char") ("d,decimation", "Decimation of sensors and agents", cxxopts::value(Decimation)->default_value("1"), "int") ("c,cycles", "Number of simulation cycles to perform", cxxopts::value(NumberOfSimulationCycles)->default_value("16"), "int") ("h,help", "Print usage"); auto Args = Options.parse(argc, argv); if (Args.count("help")) { LOG_INFO_STREAM << '\n' << Options.help() << std::endl; exit(0); } if (Args.count("input") == 0) { throw std::invalid_argument("Argument --input must be defined."); } if (Args.count("output") == 0) { throw std::invalid_argument("Argument --output must be defined."); } } catch (const cxxopts::OptionException &e) { logErrorAndExit(e.what(), 1); } catch (const std::invalid_argument &e) { logErrorAndExit(e.what(), 1); } std::unique_ptr App = Application::create(AppName); // // Relevant types and definitions. // using HRType = float; using HRConfidence = RangeConfidence; using HRParFun = PartialFunction; using HRLinFun = LinearFunction; using BRType = float; using BRConfidence = RangeConfidence; using BRParFun = PartialFunction; using BRLinFun = LinearFunction; using SpO2Type = int32_t; using SpO2Confidence = RangeConfidence; using SpO2ParFun = PartialFunction; using SpO2LinFun = LinearFunction; using BPSysType = float; using BPSysConfidence = RangeConfidence; using BPSysParFun = PartialFunction; using BPSysLinFun = LinearFunction; using BodyTempType = float; using BodyTempConfidence = RangeConfidence; using BodyTempParFun = PartialFunction; using BodyTempLinFun = LinearFunction; // // Create application 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 = createSensor(App, "HR Sensor", Decimation); AgentHandle BRSensor = createSensor(App, "BR Sensor", Decimation); AgentHandle SpO2Sensor = createSensor(App, "SpO2 Sensor", Decimation); AgentHandle BPSysSensor = createSensor(App, "BPSys Sensor", Decimation); AgentHandle BodyTempSensor = createSensor(App, "BodyTemp Sensor", Decimation); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // Define reliabilities and confindences. // const size_t TimeStep = 1; const size_t HistoryLength = 32; using TimeParFun = PartialFunction; using TimeLinFun = LinearFunction; std::shared_ptr> TimeConfidence( new TimeParFun({{{std::numeric_limits::lowest(), 0}, std::make_shared(1, 0)}, {{0, HistoryLength}, std::make_shared(0, 1, HistoryLength, 0)}}, 0)); ReliabilityAndConfidenceCombination HRReliability; HRReliability.setIdentifiers(warningScores); HRReliability.setHistoryLength(HistoryLength); HRReliability.setTimeStep(TimeStep); HRReliability.setTimeFunctionForLikelinessFunction(TimeConfidence); std::shared_ptr HRConf(new HRConfidence( {{No, HRParFun({{{56.5f, 62.5f}, std::make_shared(56.5f, 0, 62.5f, 1)}, {{62.5f, 97.5f}, std::make_shared(1, 0)}, {{97.5f, 103.5f}, std::make_shared(97.5f, 1, 103.5f, 0)}}, 0)}, {Low, HRParFun( {{{47.5f, 53.5f}, std::make_shared(47.5f, 0, 53.5f, 1)}, {{53.5f, 56.5f}, std::make_shared(1, 0)}, {{56.5f, 62.5f}, std::make_shared(56.5f, 1, 62.5f, 0)}, {{97.5f, 103.5f}, std::make_shared(97.5f, 0, 103.5f, 1)}, {{103.5f, 107.5f}, std::make_shared(1, 0)}, {{107.5f, 113.5f}, std::make_shared(107.5f, 1, 113.5f, 0)}}, 0)}, {High, HRParFun( {{{36.5f, 42.5f}, std::make_shared(36.5f, 0, 42.5f, 1)}, {{42.5f, 47.5f}, std::make_shared(1, 0)}, {{47.5f, 53.5f}, std::make_shared(47.5f, 1, 53.5f, 0)}, {{107.5f, 113.5f}, std::make_shared(107.5f, 0, 113.5f, 1)}, {{113.5f, 126.5f}, std::make_shared(1, 0)}, {{126.5f, 132.5f}, std::make_shared(126.5f, 1, 132.5f, 0)}}, 0)}, {Emergency, HRParFun( {{{36.5f, 42.5f}, std::make_shared(36.5f, 1, 42.5f, 0)}, {{42.5f, 126.5f}, std::make_shared(0, 0)}, {{126.5f, 132.5f}, std::make_shared(126.5f, 0, 132.5f, 1)}}, 1)}})); HRReliability.setConfidenceFunction(HRConf); std::shared_ptr> HRAbsRel( new HRParFun({{{0.f, 200.f}, std::make_shared(1, 0)}, {{200.f, 300.f}, std::make_shared(200.f, 1, 300.f, 0)}}, 0)); HRReliability.setAbsoluteReliabilityFunction(HRAbsRel); std::shared_ptr> HRRelSlope(new HRParFun( {{{-200.f, -100.f}, std::make_shared(-200.f, 0, -100.f, 1)}, {{-100.f, 100.f}, std::make_shared(1, 0)}, {{100.f, 200.f}, std::make_shared(100.f, 1, 200.f, 0)}}, 0)); HRReliability.setReliabilitySlopeFunction(HRRelSlope); ReliabilityAndConfidenceCombination BRReliability; BRReliability.setIdentifiers(warningScores); BRReliability.setHistoryLength(HistoryLength); BRReliability.setTimeStep(TimeStep); BRReliability.setTimeFunctionForLikelinessFunction(TimeConfidence); std::shared_ptr BRConf(new BRConfidence( {{No, BRParFun( {{{7.5f, 9.5f}, std::make_shared(7.5f, 0, 9.5f, 1)}, {{9.5f, 13.5f}, std::make_shared(1, 0)}, {{13.5f, 15.5f}, std::make_shared(12.5f, 1, 15.5f, 0)}}, 0)}, {Low, BRParFun( {{{13.5f, 15.5f}, std::make_shared(13.5f, 0, 15.5f, 1)}, {{15.5f, 19.5f}, std::make_shared(1, 0)}, {{19.5f, 21.5f}, std::make_shared(19.5f, 1, 21.5f, 0)}}, 0)}, {High, BRParFun( {{{std::numeric_limits::lowest(), 7.5f}, std::make_shared(1, 0)}, {{7.5f, 9.5f}, std::make_shared(7.5f, 1, 9.5f, 0)}, {{19.5f, 21.5f}, std::make_shared(19.5f, 0, 21.5f, 1)}, {{21.5f, 28.5f}, std::make_shared(1, 0)}, {{28.5f, 30.5f}, std::make_shared(28.5f, 1, 30.5f, 0)}}, 0)}, {Emergency, BRParFun({{{std::numeric_limits::lowest(), 28.5f}, std::make_shared(0, 0)}, {{28.5f, 30.5f}, std::make_shared(28.5f, 0, 30.5f, 1)}}, 1)}})); BRReliability.setConfidenceFunction(BRConf); std::shared_ptr> BRAbsRel( new BRParFun({{{0.f, 40.f}, std::make_shared(1, 0)}, {{40.f, 60.f}, std::make_shared(40.f, 1, 60.f, 0)}}, 0)); BRReliability.setAbsoluteReliabilityFunction(BRAbsRel); std::shared_ptr> BRRelSlope( new BRParFun({{{-30.f, -20.f}, std::make_shared(-30.f, 0, -20.f, 1)}, {{-20.f, 20.f}, std::make_shared(1, 0)}, {{20.f, 30.f}, std::make_shared(20.f, 1, 30.f, 0)}}, 0)); BRReliability.setReliabilitySlopeFunction(BRRelSlope); ReliabilityAndConfidenceCombination SpO2Reliability; SpO2Reliability.setIdentifiers(warningScores); SpO2Reliability.setHistoryLength(HistoryLength); SpO2Reliability.setTimeStep(TimeStep); SpO2Reliability.setTimeFunctionForLikelinessFunction(TimeConfidence); std::shared_ptr SpO2Conf(new SpO2Confidence( {{No, SpO2ParFun({{{93, 96}, std::make_shared(93, 0, 96, 1)}, {{96, std::numeric_limits::max()}, std::make_shared(1, 0)}}, 0)}, {Low, SpO2ParFun({{{88, 91}, std::make_shared(88, 0, 91, 1)}, {{91, 93}, std::make_shared(1, 0)}, {{93, 96}, std::make_shared(93, 1, 96, 0)}}, 0)}, {High, SpO2ParFun({{{83, 86}, std::make_shared(83, 0, 86, 1)}, {{86, 88}, std::make_shared(1, 0)}, {{88, 91}, std::make_shared(88, 1, 91, 0)}}, 0)}, {Emergency, SpO2ParFun({{{83, 86}, std::make_shared(83, 1, 86, 0)}, {{86, std::numeric_limits::max()}, std::make_shared(0, 0)}}, 1)}})); SpO2Reliability.setConfidenceFunction(SpO2Conf); std::shared_ptr> SpO2AbsRel( new SpO2ParFun( { {{0, 100}, std::make_shared(1, 0)}, }, 0)); SpO2Reliability.setAbsoluteReliabilityFunction(SpO2AbsRel); std::shared_ptr> SpO2RelSlope( new SpO2ParFun({{{-8, -5}, std::make_shared(-8, 0, -5, 1)}, {{-5, 5}, std::make_shared(1, 0)}, {{5, 8}, std::make_shared(5, 1, 8, 0)}}, 0)); SpO2Reliability.setReliabilitySlopeFunction(SpO2RelSlope); ReliabilityAndConfidenceCombination BPSysReliability; BPSysReliability.setIdentifiers(warningScores); BPSysReliability.setHistoryLength(HistoryLength); BPSysReliability.setTimeStep(TimeStep); BPSysReliability.setTimeFunctionForLikelinessFunction(TimeConfidence); std::shared_ptr BPSysConf(new BPSysConfidence( {{No, BPSysParFun({{{97.5f, 103.5f}, std::make_shared(97.5f, 0, 103.5f, 1)}, {{103.5f, 146.5f}, std::make_shared(1, 0)}, {{146.5f, 152.5f}, std::make_shared(146.5f, 1, 152.5f, 0)}}, 0)}, {Low, BPSysParFun({{{77.5f, 83.5f}, std::make_shared(77.5f, 0, 83.5f, 1)}, {{83.5f, 97.5f}, std::make_shared(1, 0)}, {{97.5f, 103.5f}, std::make_shared(97.5f, 1, 103.5f, 0)}, {{146.5f, 152.5f}, std::make_shared(146.5f, 0, 152.5f, 1)}, {{152.5f, 166.5f}, std::make_shared(1, 0)}, {{166.5f, 172.5f}, std::make_shared(166.5f, 1, 172.5f, 0)}}, 0)}, {High, BPSysParFun({{{66.5f, 72.5f}, std::make_shared(66.5f, 0, 72.5f, 1)}, {{72.5f, 77.5f}, std::make_shared(1, 0)}, {{77.5f, 83.5f}, std::make_shared(77.5f, 1, 83.5f, 0)}, {{166.5f, 172.5f}, std::make_shared(166.5f, 0, 172.5f, 1)}, {{172.5f, 176.5f}, std::make_shared(1, 0)}, {{176.5f, 182.5f}, std::make_shared(176.5f, 1, 182.5f, 0)}}, 0)}, {Emergency, BPSysParFun({{{66.5f, 72.5f}, std::make_shared(66.5f, 1, 72.5f, 0)}, {{72.5f, 176.5f}, std::make_shared(0, 0)}, {{176.5f, 182.5f}, std::make_shared(176.5f, 0, 182.5f, 1)}}, 1)}})); BPSysReliability.setConfidenceFunction(BPSysConf); std::shared_ptr> BPSysAbsRel( new BPSysParFun( {{{0.f, 260.f}, std::make_shared(1, 0)}, {{260.f, 400.f}, std::make_shared(260.f, 1, 400.f, 0)}}, 0)); BPSysReliability.setAbsoluteReliabilityFunction(BPSysAbsRel); std::shared_ptr> BPSysRelSlope( new BPSysParFun( {{{-100.f, -50.f}, std::make_shared(-100.f, 0, -50.f, 1)}, {{-50.f, 50.f}, std::make_shared(1, 0)}, {{50.f, 100.f}, std::make_shared(50.f, 1, 100.f, 0)}}, 0)); BPSysReliability.setReliabilitySlopeFunction(BPSysRelSlope); ReliabilityAndConfidenceCombination BodyTempReliability; BodyTempReliability.setIdentifiers(warningScores); BodyTempReliability.setHistoryLength(HistoryLength); BodyTempReliability.setTimeStep(TimeStep); BodyTempReliability.setTimeFunctionForLikelinessFunction(TimeConfidence); std::shared_ptr BodyTempConf(new BodyTempConfidence( {{No, BodyTempParFun( {{{34.55f, 35.55f}, std::make_shared(34.55f, 0, 35.55f, 1)}, {{35.55f, 37.55f}, std::make_shared(1, 0)}, {{37.55f, 38.55f}, std::make_shared(37.55f, 1, 38.55f, 0)}}, 0)}, {Low, BodyTempParFun({}, 0)}, {High, BodyTempParFun( {{{std::numeric_limits::lowest(), 34.55f}, std::make_shared(1, 0)}, {{34.55f, 35.55f}, std::make_shared(34.55f, 1, 35.55f, 0)}, {{37.55f, 38.55f}, std::make_shared(37.55f, 0, 38.55f, 1)}, {{38.55f, 39.05f}, std::make_shared(1, 0)}, {{39.05f, 40.05f}, std::make_shared(39.05f, 1, 40.05f, 0)}}, 0)}, {Emergency, BodyTempParFun( {{{std::numeric_limits::lowest(), 39.05f}, std::make_shared(0, 0)}, {{39.05f, 40.05f}, std::make_shared(39.05f, 0, 40.05f, 1)}}, 1)}})); BodyTempReliability.setConfidenceFunction(BodyTempConf); std::shared_ptr> BodyTempAbsRel( new BodyTempParFun( {{{-70.f, -50.f}, std::make_shared(-70.f, 0, -50.f, 1)}, {{-50.f, 40.f}, std::make_shared(1, 0)}, {{40.f, 60.f}, std::make_shared(40.f, 1, 60.f, 0)}}, 0)); BodyTempReliability.setAbsoluteReliabilityFunction(BodyTempAbsRel); std::shared_ptr> BodyTempRelSlope( new BodyTempParFun( {{{-0.1f, -0.05f}, std::make_shared(-0.1f, 0, -0.05f, 1)}, {{-0.05f, 0.05f}, std::make_shared(1, 0)}, {{0.05f, 0.1f}, std::make_shared(0.05f, 1, 0.1f, 0)}}, 0)); BodyTempReliability.setReliabilitySlopeFunction(BodyTempRelSlope); // // Create low-level application agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle HRAgent = createLowLevelAgent(App, "HR Agent", Decimation, HRReliability); AgentHandle BRAgent = createLowLevelAgent(App, "BR Agent", Decimation, BRReliability); AgentHandle SpO2Agent = createLowLevelAgent(App, "SpO2 Agent", Decimation, SpO2Reliability); AgentHandle BPSysAgent = createLowLevelAgent(App, "BPSys Agent", Decimation, BPSysReliability); AgentHandle BodyTempAgent = createLowLevelAgent( App, "BodyTemp Agent", Decimation, BodyTempReliability); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); App->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); App->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); App->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); App->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); App->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); // // Create a high-level application agent. // LOG_INFO("Create high-level agent."); // Slave positions on BodyAgent. enum SlaveIndex : rosa::id_t { HRIdx = 0, BRIdx = 1, SpO2Idx = 2, BPSysIdx = 3, BodyTempIdx = 4 }; const ReliabilityType CrossLikelinessParameter = 1.5; CrossCombinator BodyCrossCombinator; BodyCrossCombinator.setCrossLikelinessParameter(CrossLikelinessParameter); using WarningLikelinessFun = LikelinessFunction; std::shared_ptr BRCrossLikelinessFun( new WarningLikelinessFun(0.6)); BodyCrossCombinator.addCrossLikelinessProfile(HRIdx, BRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, HRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, SpO2Idx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, BPSysIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BRIdx, BodyTempIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(SpO2Idx, BRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BPSysIdx, BRIdx, BRCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BodyTempIdx, BRIdx, BRCrossLikelinessFun); std::shared_ptr HRBPCrossLikelinessFun( new WarningLikelinessFun(2.5)); BodyCrossCombinator.addCrossLikelinessProfile(HRIdx, BPSysIdx, HRBPCrossLikelinessFun); BodyCrossCombinator.addCrossLikelinessProfile(BPSysIdx, HRIdx, HRBPCrossLikelinessFun); // The new agent logs its input values, results in a pair of the sum of // received warning scores and their cross-reliability as well as calculated // cross-likeliness feedback for each of its slaves. using BodyAgentInput = std::pair; using BodyAgentOutput = Optional; using BodyAgentFeedback = Optional; using BodyAgentResult = std::tuple; using BodyAgentHandler = std::function; AgentHandle BodyAgent = App->createAgent( "Body Agent", BodyAgentHandler([&](BodyAgentInput HR, BodyAgentInput BR, BodyAgentInput SpO2, BodyAgentInput BPSys, BodyAgentInput BodyTemp) -> BodyAgentResult { LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" << (HR.second ? "" : "") - << " HR result: " << HR.first << "\n" + << " HR result: " << PRINTABLE(HR.first) << "\n" << (BR.second ? "" : "") - << " BR result: " << BR.first << "\n" + << " BR result: " << PRINTABLE(BR.first) << "\n" << (SpO2.second ? "" : "") - << " SpO2 result: " << SpO2.first << "\n" + << " SpO2 result: " << PRINTABLE(SpO2.first) << "\n" << (BPSys.second ? "" : "") - << " BPSys result: " << BPSys.first << "\n" + << " BPSys result: " << PRINTABLE(BPSys.first) + << "\n" << (BodyTemp.second ? "" : "") - << " BodyTemp result: " << BodyTemp.first + << " BodyTemp result: " << PRINTABLE(BodyTemp.first) << "\n******\n"; using ValueType = std::tuple; const std::vector SlaveValues{ {HRIdx, std::get<0>(HR.first), std::get<1>(HR.first)}, {BRIdx, std::get<0>(BR.first), std::get<1>(BR.first)}, {SpO2Idx, std::get<0>(SpO2.first), std::get<1>(SpO2.first)}, {BPSysIdx, std::get<0>(BPSys.first), std::get<1>(BPSys.first)}, {BodyTempIdx, std::get<0>(BodyTemp.first), std::get<1>(BodyTemp.first)}}; const auto [crossReliability, feedbackValues] = BodyCrossCombinator(SlaveValues); struct ScoreSum { void operator()(const ValueType &V) { ews += std::get<1>(V); } WarningScoreType ews{0}; }; const ScoreSum scoreSum = std::for_each( SlaveValues.cbegin(), SlaveValues.cend(), ScoreSum()); return {{WarningValue(scoreSum.ews, crossReliability)}, {feedbackFromSymbol(feedbackValues.at(HRIdx))}, {feedbackFromSymbol(feedbackValues.at(BRIdx))}, {feedbackFromSymbol(feedbackValues.at(SpO2Idx))}, {feedbackFromSymbol(feedbackValues.at(BPSysIdx))}, {feedbackFromSymbol(feedbackValues.at(BodyTempIdx))}}; })); App->setExecutionPolicy(BodyAgent, AppExecutionPolicy::decimation(Decimation)); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); App->connectAgents(BodyAgent, HRIdx, HRAgent, "HR Agent Channel"); App->connectAgents(BodyAgent, BRIdx, BRAgent, "BR Agent Channel"); App->connectAgents(BodyAgent, SpO2Idx, SpO2Agent, "SpO2 Agent Channel"); App->connectAgents(BodyAgent, BPSysIdx, BPSysAgent, "BPSys Agent Channel"); App->connectAgents(BodyAgent, BodyTempIdx, 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::CSVTupleWriter ScoreWriter(ScoreCSV, CSVDelimiter); if (CSVHeader) { ScoreWriter.writeHeader({"EWS", "Reliability"}); } // The agent writes each new input value into a CSV file and produces nothing. // \note The execution of the logger is not subject to decimation. using logger_result = AppTuple; AgentHandle LoggerAgent = App->createAgent( "Logger Agent", std::function(std::pair)>( [&ScoreWriter]( std::pair Score) -> Optional { // 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."); App->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize application for simulation. // App->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. using CSVDataIterator = CSVIterator; const auto CSVHeaderInfo = CSVHeader ? HeaderInformation::HasHeader : HeaderInformation::HasNoHeader; std::ifstream DataCSV(DataCSVPath); auto [HRRange, BRRange, SpO2Range, BPSysRange, BodyTempRange] = splitTupleIterator( CSVDataIterator(DataCSV, 0, CSVHeaderInfo, CSVDelimiter), CSVDataIterator()); App->registerSensorValues(HRSensor, std::move(begin(HRRange)), end(HRRange)); App->registerSensorValues(BRSensor, std::move(begin(BRRange)), end(BRRange)); App->registerSensorValues(SpO2Sensor, std::move(begin(SpO2Range)), end(SpO2Range)); App->registerSensorValues(BPSysSensor, std::move(begin(BPSysRange)), end(BPSysRange)); App->registerSensorValues(BodyTempSensor, std::move(begin(BodyTempRange)), end(BodyTempRange)); // // Simulate. // App->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/agent-functionalities/Reliability-functionality-agent-context/Reliability-agents.cpp b/examples/agent-functionalities/Reliability-functionality-agent-context/Reliability-agents.cpp index 220505c..177d127 100644 --- a/examples/agent-functionalities/Reliability-functionality-agent-context/Reliability-agents.cpp +++ b/examples/agent-functionalities/Reliability-functionality-agent-context/Reliability-agents.cpp @@ -1,340 +1,343 @@ //===- examples/agent-functionalities/Reliability-functionality.cpp *C++-*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/agent-functionalities/Reliability-functionality.cpp /// /// \author Daniel Schnoell (daniel.schnoell@tuwien.ac.at ) /// -/// \date 2019 +/// \date 2019-2020 /// /// \brief A simple example on defining Relianility Functionalities inside a /// master slave context. /// \note This is not finished. and is currently inactive /// //===----------------------------------------------------------------------===// #define Reliability_trace_level 5 #include "rosa/config/version.h" #include "rosa/support/log.h" #include "rosa/agent/CrossReliability.h" #include "rosa/agent/RangeConfidence.hpp" #include "rosa/agent/Reliability.h" #include "rosa/app/Application.hpp" #include #include typedef double SensorValueType; typedef long StateType; typedef double ReliabilityType; #include "./helper.h" // just stuff from Rel-func to increase readability using namespace rosa::agent; using namespace rosa; using namespace rosa::app; using namespace rosa::terminal; #define NumberOfSimulationCycles 10 // ------------------------------------------------------------------------------- // Bunch of recusive templates to simplify usage // ------------------------------------------------------------------------------- template struct conversion; template class A, typename... TypesB, template class B> struct conversion, B> { using type = typename conversion, std::tuple>::type; }; template class A, typename... TypesB, template class B> struct conversion<0, A, B> { using type = AppTuple; }; template using unrolled_data_type = typename conversion, std::tuple<>>::type; //-------------------------------------------------------------------------------- template struct __convert_to_vector { void operator()(std::vector> &feedback, unrolled_data_type I) { __convert_to_vector()(feedback, I); feedback.push_back({std::get(I), std::get(I)}); } }; template struct __convert_to_vector { void operator()(std::vector> &feedback, unrolled_data_type I) { feedback.push_back({std::get<0>(I), std::get<1>(I)}); } }; template void convert_to_vector( std::vector> &feedback, unrolled_data_type I) { __convert_to_vector()(feedback, I); } //---------------------------------------------------------------------------- // template struct unrole_vector {}; // // template v, typename... vT, // template class T, typename... TT, typename... types> // struct unrole_vector,T, types...> { // void operator()(c vec, types... A) { // unrole_vector, T, auto>()(vec, // vec.at(size).score, // vec.at(size).score); // } //}; // --------------------------------------------------------------------------- // main // --------------------------------------------------------------------------- int main(void) { const std::size_t number_of_states = 3; std::unique_ptr App = Application::create("App"); //---------------------- Sensors ------------------------------------- //-------------------------------------------------------------------- const std::string SensorName1 = "Sensor1"; const std::string SensorName2 = "Sensor2"; const std::string SensorName3 = "Sensor3"; AgentHandle Sensor1 = App->createSensor( SensorName1, [&SensorName1](std::pair I) { LOG_INFO_STREAM << "\n******\n" << SensorName1 << " sensor-input " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; }); AgentHandle Sensor2 = App->createSensor( SensorName2, [&SensorName2](std::pair I) { LOG_INFO_STREAM << "\n******\n" << SensorName2 << " sensor-input " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; }); AgentHandle Sensor3 = App->createSensor( SensorName3, [&SensorName3](std::pair I) { LOG_INFO_STREAM << "\n******\n" << SensorName3 << " sensor-input " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; }); //------------------------- lowlevel agents -------------------------------- //-------------------------------------------------------------------------- const std::string LowLevelAgentName1 = "LowLevelAgent1"; const std::string LowLevelAgentName2 = "LowLevelAgent2"; const std::string LowLevelAgentName3 = "LowLevelAgent3"; using conf = unrolled_data_type< number_of_states, StateType, ReliabilityType>; // this is the confidence expressed as one tuple it // uses the format // (first.state,first.rel,second.sate...) using LowLevelAgentMasterResult = void; // no return using LowLevelReturnFromMaster = std::pair; using FloatMasterHandler = std::function; using FloatResult = Optional>; using FloatHandler = std::function, bool>)>; auto lowlevel1 = create_lowlevel_func(); auto lowlevel2 = create_lowlevel_func(); auto lowlevel3 = create_lowlevel_func(); AgentHandle SlaveAgent1 = App->createAgent( LowLevelAgentName1, // Master-input handler. FloatMasterHandler( [&LowLevelAgentName1, lowlevel1](LowLevelReturnFromMaster I) -> LowLevelAgentMasterResult { LOG_INFO_STREAM << "inside: " << LowLevelAgentName1 << "feedback\n"; if (I.second) { std::vector> feedback; convert_to_vector(feedback, I.first); lowlevel1->feedback(feedback); } }), // Slave-input handler. FloatHandler( [&LowLevelAgentName1, lowlevel1]( std::pair, bool> I) -> FloatResult { - LOG_INFO_STREAM - << "\n******\n" - << LowLevelAgentName1 << " " << (I.second ? "" : "") - << " value: " << std::get<0>(I.first) << "\n******\n"; + LOG_INFO_STREAM << "\n******\n" + << LowLevelAgentName1 << " " + << (I.second ? "" : "") + << " value: " << PRINTABLE(std::get<0>(I.first)) + << "\n******\n"; auto tmp = lowlevel1->operator()(std::get<0>(I.first)); AppTuple ret(tmp.score, tmp.Likeliness); return {ret}; })); AgentHandle SlaveAgent2 = App->createAgent( LowLevelAgentName2, // Master-input handler. FloatMasterHandler( [&LowLevelAgentName2, lowlevel2](LowLevelReturnFromMaster I) -> LowLevelAgentMasterResult { LOG_INFO_STREAM << "inside: " << LowLevelAgentName2 << "feedback\n"; if (I.second) { std::vector> feedback; convert_to_vector(feedback, I.first); lowlevel2->feedback(feedback); } }), // Slave-input handler. FloatHandler( [&LowLevelAgentName2, lowlevel2]( std::pair, bool> I) -> FloatResult { - LOG_INFO_STREAM - << "\n******\n" - << LowLevelAgentName2 << " " << (I.second ? "" : "") - << " value: " << std::get<0>(I.first) << "\n******\n"; + LOG_INFO_STREAM << "\n******\n" + << LowLevelAgentName2 << " " + << (I.second ? "" : "") + << " value: " << PRINTABLE(std::get<0>(I.first)) + << "\n******\n"; auto tmp = lowlevel2->operator()(std::get<0>(I.first)); AppTuple ret(tmp.score, tmp.Likeliness); return {ret}; })); AgentHandle SlaveAgent3 = App->createAgent( LowLevelAgentName3, // Master-input handler. FloatMasterHandler( [&LowLevelAgentName3, lowlevel3](LowLevelReturnFromMaster I) -> LowLevelAgentMasterResult { LOG_INFO_STREAM << "inside: " << LowLevelAgentName3 << "feedback\n"; if (I.second) { std::vector> feedback; convert_to_vector(feedback, I.first); lowlevel3->feedback(feedback); } }), // Slave-input handler. FloatHandler( [&LowLevelAgentName3, lowlevel3]( std::pair, bool> I) -> FloatResult { - LOG_INFO_STREAM - << "\n******\n" - << LowLevelAgentName3 << " " << (I.second ? "" : "") - << " value: " << std::get<0>(I.first) << "\n******\n"; + LOG_INFO_STREAM << "\n******\n" + << LowLevelAgentName3 << " " + << (I.second ? "" : "") + << " value: " << PRINTABLE(std::get<0>(I.first)) + << "\n******\n"; auto tmp = lowlevel3->operator()(std::get<0>(I.first)); AppTuple ret(tmp.score, tmp.Likeliness); return {ret}; })); //------------------------- high level rel --------------------------------- //-------------------------------------------------------------------------- auto highlevel = create_highlevel_func(); using conf1 = Optional>; // this is the confidence expressed as one tuple it // uses the format // (first.state,first.rel,second.sate...) using MasterResult = std::tuple>, conf1, conf1, conf1>; using SlaveOutputs = AppTuple; using MasterHandler = std::function, std::pair, std::pair)>; AgentHandle MasterAgent = App->createAgent( "Master Agent", MasterHandler([&](std::pair I0, std::pair I1, std::pair I2) -> MasterResult { ReliabilityForHighLevelAgents::InputType input; input.push_back({0, std::get<0>(I0.first), std::get<1>(I0.first)}); input.push_back({1, std::get<0>(I1.first), std::get<1>(I1.first)}); input.push_back({2, std::get<0>(I2.first), std::get<1>(I2.first)}); auto out = highlevel->operator()(input); AppTuple rel(out.CrossLikeliness); conf c[3]; for (std::size_t at = 0; at < 3; at++) { std::get<0>(c[at]) = out.Likeliness.at(at).at(0).score; std::get<1>(c[at]) = out.Likeliness.at(at).at(0).Likeliness; std::get<2>(c[at]) = out.Likeliness.at(at).at(1).score; std::get<3>(c[at]) = out.Likeliness.at(at).at(1).Likeliness; std::get<4>(c[at]) = out.Likeliness.at(at).at(2).score; std::get<5>(c[at]) = out.Likeliness.at(at).at(2).Likeliness; } return {{rel}, {c[0]}, {c[1]}, {c[2]}}; })); // ------------------------------------------------------------------------- // Sensors and connecting // ------------------------------------------------------------------------- std::vector FloatValues(NumberOfSimulationCycles); std::generate(FloatValues.begin(), FloatValues.end(), [f = 0.5f](void) mutable { f += 0.3f; return std::floor(f) + 0.5f; }); App->registerSensorValues(Sensor1, FloatValues.begin(), FloatValues.end()); App->registerSensorValues(Sensor2, FloatValues.begin(), FloatValues.end()); App->registerSensorValues(Sensor3, FloatValues.begin(), FloatValues.end()); // Connection App->connectSensor(SlaveAgent1, 0, Sensor1, "Sensor 1"); App->connectSensor(SlaveAgent2, 0, Sensor2, "Sensor 2"); App->connectSensor(SlaveAgent3, 0, Sensor3, "Sensor 3"); App->connectAgents(MasterAgent, 0, SlaveAgent1, "Slave1"); App->connectAgents(MasterAgent, 1, SlaveAgent2, "Slave2"); App->connectAgents(MasterAgent, 2, SlaveAgent3, "Slave3"); // Logger AgentHandle LoggerAgent = App->createAgent( "Logger Agent", std::function>( std::pair, bool>)>( [](std::pair, bool> Sum) -> Optional> { if (Sum.second) { LOG_INFO_STREAM - << "Result: Rel: " << std::get<0>(Sum.first) - << "\n"; + << "Result: Rel: " + << PRINTABLE(std::get<0>(Sum.first)) << "\n"; } return {}; })); App->connectAgents(LoggerAgent, 0, MasterAgent, "Sum Agent Channel"); // ------------------------------------------------------------------------------- // Simulate // ------------------------------------------------------------------------------- App->simulate(NumberOfSimulationCycles); delete highlevel; delete lowlevel1; delete lowlevel2; } diff --git a/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp b/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp index d6e5ede..57cfb51 100644 --- a/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp +++ b/examples/agent-functionalities/basic-agent-functionalities/agent-functionalities.cpp @@ -1,194 +1,194 @@ //===-- examples/agent-functionalities/agent-functionalities.cpp *-- C++-*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/agent-functionalities/agent-functionalities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2020 /// /// \brief A simple example on defining \c rosa::Agent instances using /// \c rosa::agent::Functionality object as components. /// //===----------------------------------------------------------------------===// // Make sure M_PI is available, needed for _WIN32 #define _USE_MATH_DEFINES #include #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/RangeConfidence.hpp" #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::terminal; // We use pi as float rather than double, which M_PI is. constexpr float Pi = (float) M_PI; /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; private: enum class Categories { Bad, Normal, Good }; static const std::map CategoryNames; StaticLengthHistory H; Confidence C; RangeAbstraction A; PartialFunction L; RangeConfidence RCL; RangeConfidence RCS; public: void handler(Tick, uint8_t V) noexcept { // Record \p V to the \c rosa::agent::History, then print state info. H << V; ASSERT(H.entry() == V); // Sanity check. LOG_INFO_STREAM << "\nNext value: " << PRINTABLE(V) << ", confidence: " << C(H) << ", category: " << CategoryNames.at(A(H.entry())) - << ", partial: " << int(L(H.entry())) + << ", partial: " << PRINTABLE(L(H.entry())) << ", range-confidence-linear: "; std::map ResLin = RCL(H.entry()); for (auto Con : ResLin) { LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second << ","; } LOG_INFO_STREAM << " range-confidence-sine: "; std::map ResSine = RCS(H.entry()); for (auto Con : ResSine) { LOG_INFO_STREAM << " " << CategoryNames.at(Con.first) << " " << Con.second << ","; } LOG_INFO_STREAM << '\n'; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, THISMEMBER(handler)), H(), C(5, 20, 1), A({{{(uint8_t)10, (uint8_t)14}, Categories::Normal}, {{(uint8_t)15, (uint8_t)17}, Categories::Good}, {{(uint8_t)18, (uint8_t)19}, Categories::Normal}}, Categories::Bad), L({{{0, 2}, std::make_shared>(0, 1)}, {{2, 4}, std::make_shared>(2, 0)}, {{4, 6}, std::make_shared>(6, -1)}}, 0), RCL({ {Categories::Bad, PartialFunction({ {{0.f, 3.f}, std::make_shared> (0.f, 1.f/3)}, {{3.f, 6.f}, std::make_shared> (1.f, 0.f)}, {{6.f, 9.f}, std::make_shared> (3.f, -1.f/3)}, },0)}, {Categories::Normal, PartialFunction({ {{6.f, 9.f}, std::make_shared> (-2.f, 1.f/3)}, {{9.f, 12.f}, std::make_shared> (1.f, 0.f)}, {{12.f, 15.f}, std::make_shared> (5.f, -1.f/3)}, },0)}, {Categories::Good, PartialFunction({ {{12.f, 15.f}, std::make_shared> (-4.f, 1.f/3)}, {{15.f, 18.f}, std::make_shared> (1.f, 0.f)}, {{18.f, 21.f}, std::make_shared> (7.f, -1.f/3)}, },0)} }), RCS({ {Categories::Bad, PartialFunction({ {{0.f, 3.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2, 0.5f)}, {{3.f, 6.f}, std::make_shared>(1.f, 0.f)}, {{6.f, 9.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)}, {Categories::Normal, PartialFunction({ {{6.f, 9.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2, 0.5f)}, {{9.f, 12.f}, std::make_shared>(1.f, 0.f)}, {{12.f, 15.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)}, {Categories::Good, PartialFunction({ {{12.f, 15.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2, 0.5f)}, {{15.f, 18.f}, std::make_shared>(1.f, 0.f)}, {{18.f, 21.f}, std::make_shared> (Pi/3, 0.5f, -Pi/2 + 3, 0.5f)}, },0)} }, true){} }; const std::map MyAgent::CategoryNames{ {Categories::Bad, "Bad"}, {Categories::Normal, "Normal"}, {Categories::Good, "Good"}}; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "agent-functionalities example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); AgentHandle A = SystemTester::createMyAgent(SP, "MyAgent"); std::vector Vs{0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 15, 14, 15, 16, 19, 20, 21}; for (auto I = Vs.begin(); I != Vs.end(); ++I) { A.send(MyAgent::Tick::Value, *I); } SystemTester::destroyMyAgent(SP, A); return 0; } diff --git a/examples/application-interface/application-interface.cpp b/examples/application-interface/application-interface.cpp index 8d71d95..b2e20b3 100644 --- a/examples/application-interface/application-interface.cpp +++ b/examples/application-interface/application-interface.cpp @@ -1,345 +1,346 @@ //===-- examples/application-interface/application-interface.cpp *- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/application-interface/application-interface.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2020 /// /// \brief A simple example on the \c rosa::app::Application and related /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/app/Application.hpp" #include #include #include using namespace rosa; using namespace rosa::app; using namespace rosa::terminal; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << "application-interface example" << Color::Default << '\n'; std::unique_ptr App = Application::create("App"); // // Create application sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // 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 = App->createSensor("BoolSensor"); AgentHandle IntSensor = App->createSensor("IntSensor"); // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM. const std::string FloatSensorName = "FloatSensor"; AgentHandle FloatSensor = App->createSensor( FloatSensorName, [&FloatSensorName](std::pair I) { LOG_INFO_STREAM << "\n******\n" << FloatSensorName << " master-input " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; }); // This sensor do not receive master-input but produces tuples. using TupleType = AppTuple; AgentHandle TupleSensor = App->createSensor("TupleSensor"); // // Check and set execution policy for sensors. // LOG_INFO("Execution policies for sensors."); LOG_INFO(std::to_string(*App->getExecutionPolicy(IntSensor))); App->setExecutionPolicy(IntSensor, AppExecutionPolicy::decimation(2)); App->setExecutionPolicy(FloatSensor, AppExecutionPolicy::decimation(2)); LOG_INFO(std::to_string(*App->getExecutionPolicy(IntSensor))); // // Create low-level application agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); // 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 = App->createAgent( BoolAgentName, BoolHandler([&BoolAgentName](std::pair I) -> BoolResult { LOG_INFO_STREAM << "\n******\n" << BoolAgentName << " " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(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 = App->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"; + << " value: " << PRINTABLE(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"; + << " value: " << PRINTABLE(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 = App->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"; + << " value: " << PRINTABLE(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"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; return {{I.first}, {}}; })); // This agent does not receive master-input and does not produce // master-output. It results in the sum of the values it receives in a tuple. const std::string TupleAgentName = "TupleAgent"; using TupleSumResult = Optional>; using TupleHandler = std::function)>; AgentHandle TupleAgent = App->createAgent( TupleAgentName, TupleHandler( [&TupleAgentName](std::pair I) -> TupleSumResult { LOG_INFO_STREAM << "\n******\n" << TupleAgentName << " " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; + << " value: " << PRINTABLE(I.first) << "\n******\n"; return {std::get<0>(I.first) + std::get<1>(I.first)}; })); // // Set execution policies for low-level agents. // LOG_INFO("Setting Execution policies for low-level agents."); App->setExecutionPolicy(IntAgent, AppExecutionPolicy::awaitAll({0})); App->setExecutionPolicy(FloatAgent, AppExecutionPolicy::awaitAll({0})); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); App->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel"); App->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel"); App->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel"); App->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel"); // // Create a high-level application agent. // LOG_INFO("Create high-level agent."); using SingleDoubleOutputType = Optional>; using SingleUInt32OutputType = Optional>; using NoOutputType = Optional; // 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`, `2`, and `3`. using SumResult = std::tuple; using SumHandler = std::function, bool>, std::pair, bool>, std::pair, bool>, std::pair, bool>)>; uint32_t SumAgentState = 0; AgentHandle SumAgent = App->createAgent( "Sum Agent", SumHandler( [&SumAgentState](std::pair, bool> I0, std::pair, bool> I1, std::pair, bool> I2, std::pair, bool> I3) -> SumResult { const auto V0 = std::get<0>(I0.first); const auto V1 = std::get<0>(I1.first); const auto V2 = std::get<0>(I2.first); const auto V3 = std::get<0>(I3.first); LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" << (I0.second ? "" : "") - << " bool value: " << V0 << "\n" + << " bool value: " << PRINTABLE(V0) << "\n" << (I1.second ? "" : "") - << " int value: " << V1 << "\n" + << " int value: " << PRINTABLE(V1) << "\n" << (I2.second ? "" : "") - << " float value: " << V2 << "\n" + << " float value: " << PRINTABLE(V2) << "\n" << (I3.second ? "" : "") - << " double value: " << V3 << "\n******\n"; + << " double value: " << PRINTABLE(V3) + << "\n******\n"; if (I0.second && V0) { ++SumAgentState; } const SingleUInt32OutputType MasterOutput = I0.second && V0 ? SingleUInt32OutputType(AppTuple(SumAgentState)) : SingleUInt32OutputType(); const AppTuple Output = {V1 + V2 + V3}; return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); App->connectAgents(SumAgent, 0, BoolAgent, "Bool Agent Channel"); App->connectAgents(SumAgent, 1, IntAgent, "Int Agent Channel"); App->connectAgents(SumAgent, 2, FloatAgent, "Float Agent Channel"); App->connectAgents(SumAgent, 3, TupleAgent, "Tuple 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 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 = App->createAgent( "Logger Agent", std::function(std::pair)>( [](std::pair Sum) -> Optional { if (Sum.second) { - LOG_INFO_STREAM << "Result: " << Sum.first - << "\n"; + LOG_INFO_STREAM + << "Result: " << PRINTABLE(Sum.first) << "\n"; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); App->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize application for simulation. // App->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; }); App->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end()); std::vector IntValues(NumberOfSimulationCycles); std::generate(IntValues.begin(), IntValues.end(), [i = 0](void) mutable { return ++i; }); App->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; }); App->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end()); std::vector TupleValues(NumberOfSimulationCycles); std::generate(TupleValues.begin(), TupleValues.end(), [f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType { f1 += f2; f2 -= f1; return {f1, f2}; }); App->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end()); // // Simulate. // App->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/csvfiles/csvfiles.cpp b/examples/csvfiles/csvfiles.cpp index 9f0e337..943eaed 100644 --- a/examples/csvfiles/csvfiles.cpp +++ b/examples/csvfiles/csvfiles.cpp @@ -1,358 +1,357 @@ //===-- examples/csvfiles/csvfiles.cpp ------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/csvfiles/csvfiles.cpp /// /// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2019-2020 /// /// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and /// \c rosa::writer classes. Focus is on the tuple impementations. /// //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include // includes for an complete example to read and write // with sensors and agents. #include "rosa/app/Application.hpp" #include "rosa/config/version.h" // includes to test the basic functionality // to read and write tuples. #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "rosa/support/writer/split_tuple_writer.hpp" /// the name of the example const std::string ExampleName = "csvfiles"; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 10; /// Paths for the CSV files for simulation. /// input csv files const std::string csvPath = "../examples/CSVFiles/"; const std::string csvFileWithHeader = csvPath + "HR-New.csv"; const std::string csvFileNoHeader = csvPath + "HR.csv"; const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv"; /// output csv files const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv"; const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv"; using namespace rosa; /// /// This function tests the basic CSVIterator capablities, and shows you /// how you could work with this class. /// void testtupleCSVReader(void){ //different streams to get the csv data out of the files //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_2(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_3(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_4(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_5(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_6(csvFileWithHeader); //file contains no header an valid data entries, delimter = ',' std::ifstream file2(csvFileNoHeader); //file contains header and valid data entries, delimter = ';' std::ifstream file3(csvFileHeaderSemi); csv::CSVIterator it(file_header_data); it.setDelimiter(','); (void)++it; (void)++it; //if you iterate over the end of file, the last values //of the file will remain in the data structure but no //error occurs. (void)++it; (void)++it; //------------------------------------------------------------------- // a possiblity to get the data out of the iterator std::tuple value = *it; // // Show the value of one iterator // - LOG_INFO( "Values are: "); - LOG_INFO(std::get<0>(value) ); - LOG_INFO(std::get<1>(value) ); - + LOG_INFO("Values are: "); + LOG_INFO(PRINTABLE(std::get<0>(value))); + LOG_INFO(PRINTABLE(std::get<1>(value))); //-------------------------------------------------------------------- //testing differnet parameters to the constructor //uncomment to see that it is not possible to iterate over an vector in the tuple. //rosa::csv::CSVIterator> it2(file, 1); //try to skip a valid number of lines after the header csv::CSVIterator it2_0(file_header_data_2, 1); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the programm //csv::CSVIterator it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the program //csv::CSVIterator it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines of a file without header csv::CSVIterator it2_3(file2, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but with different delimiter csv::CSVIterator it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';'); // if you skip more lines than valid, you generate an infinte loop //csv::CSVIterator it3(file_header_data_5, 500); //if you don't need data from all columns just select the number of columns you //need. You get the data back from the first column (index 0) to the fourth column //all values from the fifth column are ignored. csv::CSVIterator it4(file_header_data_6); } /// /// This function tests the basic CSVTupleWriter capablities, and shows you /// how you could work with this class. /// void testtupleCSVWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteHea); csv::CSVTupleWriter wri(file_header_out); // // Create test tuples // std::tuple values(5, 8.3f, "hallo"); std::tuple values2(3, 8.3f, "end"); // // Create test header lines for the test tuples // std::array header{ {"zero column", "first column", "second column"}}; std::array headerWrong{ {"zero column", "first column", "second column", "third column"}}; std::array headerWrongShort{ {"zero column", "first column"}}; //if you uncomment this line than it would be possible for you to write the header into the stream //in the next line. //wri.write(values); wri.writeHeader(header); wri.write(values); wri.write(values); // it is not possible to write an additional header into the stream. wri.writeHeader(header); wri.write(values); wri << values; wri << values2; //uncomment this line to see, that you can't write a header with the too many elements. //wri.writeHeader(headerWrong); //uncomment this line to see, that you can't write a header with the too few elements. //wri.writeHeader(headerWrongShort); } /// /// This function tests the basic splitTupleIterator capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVIterator in separate parts. /// void testsplitTupleIterator(void) { // // Create application // std::unique_ptr App = app::Application::create(ExampleName); // // Create application 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. // Three different sensors were created, this is just a random number taken. AgentHandle Elem0Sensor = App->createSensor("Element1 Sensor"); AgentHandle Elem1Sensor = App->createSensor("Element2 Sensor"); AgentHandle Elem2Sensor = App->createSensor("Element3 Sensor"); // // Initialize application for simulation. // App->initializeSimulation(); // Type aliases for iterators using Iterator = rosa::csv::CSVIterator; using IteratorValue = std::tuple; static_assert( std::is_same::value, "Iterator must provide tuples"); // // Open CSV file and register the columns to the corresponding sensors. // std::ifstream TestCSV(csvFileWithHeader); // // Test data looks like: // Element1, Element2, Element3, Element4, Element5 -- is the header line // 3, 5, 8, 9.5, 17 -- first line of values // 100, -8, 30, 18.8, 29 -- other line of values // were also in the file // 5, 20, -100, -200.1, -30 -- if you have less number // of values than simulation rounds all values // -- beyond your last value // will be zero. // get element iterator ranges auto[Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator()); // dissect a range into begin and end iterators by structred bindings auto[Elem0Begin, Elem0End] = Elem0Range; // deissect a range with functions auto Elem1Begin = iterator::begin(Elem1Range); auto Elem1End = iterator::end(Elem1Range); App->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End); App->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End); App->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)), iterator::end(Elem2Range)); // // Simulate. // App->simulate(NumberOfSimulationCycles); } /// /// This function tests the basic splitTupleWriter capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVWriter in separate parts. /// void testsplitTupleWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteNoHeaSplit); csv::CSVTupleWriter wri(file_header_out); // if you omit, the type definition in the template, than auto generated types were used, // and they may not fit to the used CSVTupleWriter. wri << std::make_tuple(1000, 50.6f, "tuple_created"); auto [T0Writer, T1Writer, T2Writer] = writer::splitTupleWriter(std::move(wri), writer::IncompleteTuplePolicy::Ignore); //writing elements in sequential order into the writer classes, but you can write the values into the writers in //a random order. T0Writer << (500); T1Writer << (3.0); T2Writer << "splitted writter"; T2Writer << "splitting is cool"; T0Writer << (-30); T1Writer << (-0.004f); // you can also write more often values into a writer and later into the other writers // all data will be processed correctly into the right order. T0Writer << (1); T0Writer << (2); T1Writer << (-0.4f); T0Writer << (3); T2Writer << "again"; T0Writer << (4); T1Writer << (-0.1f); T1Writer << (-0.2f); T2Writer << "and"; T1Writer << (-0.3f); T2Writer << "splitting"; T2Writer << "once again"; // again writing data of one tuple entry to the different writers in a random fashion. T1Writer << (-0.004f); T2Writer << "splitting is cool"; T0Writer << (-30); } int main(void) { LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red << ExampleName << " example" << terminal::Color::Default << '\n'; // // Testing CSVWriter. // LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: "); testtupleCSVWriter(); // // Testing CSVReader. // LOG_INFO("Testing CSVReader CSVTupleIterator implementation: "); testtupleCSVReader(); // // Testing SplitTupleIterator. // LOG_INFO("Testing SplitTupleIterator: "); testsplitTupleIterator(); // // Testing SplitTupleWriter. // LOG_INFO("Testing SplitTupleWriter: "); testsplitTupleWriter(); // // info that user knows programm has finished. // LOG_INFO("All tests finished."); return 0; } diff --git a/examples/messaging-system/messaging-system.cpp b/examples/messaging-system/messaging-system.cpp index 868cf25..72a7f9b 100644 --- a/examples/messaging-system/messaging-system.cpp +++ b/examples/messaging-system/messaging-system.cpp @@ -1,142 +1,144 @@ //===-- examples/messaging-system/messaging-system.cpp ----------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/messaging-system/messaging-system.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2020 /// /// \brief A simple example on the \c rosa::MessagingSystem and \c rosa::Agent /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/Agent.hpp" #include "rosa/core/MessagingSystem.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; /// A dummy wrapper for testing \c rosa::MessagingSystem. /// /// \note Since we test \c rosa::MessagingSystem directly here, we need to get /// access to its protected members. That we do by imitating to be a decent /// subclass of \c rosa::MessagingSystem, while calling protected member /// functions on an object of a type from which we actually don't inherit. struct SystemTester : protected MessagingSystem { template static AgentHandle createMyAgent(MessagingSystem *S, const std::string &Name, Funs &&... Fs) { return ((SystemTester *)S)->createAgent(Name, std::move(Fs)...); } static void destroyMyAgent(MessagingSystem *S, const AgentHandle &H) { ((SystemTester *)S)->destroyUnit(unwrapAgent(H)); } }; /// A special \c rosa::Agent subclass with its own state. class MyAgent : public Agent { public: using Tick = AtomConstant; using Report = AtomConstant; private: size_t Counter; public: void handler(Tick) noexcept { - LOG_INFO_STREAM << "MyAgent Tick count: " << ++Counter << '\n'; + ++Counter; + LOG_INFO_STREAM << "MyAgent Tick count: " << PRINTABLE(Counter) << '\n'; } MyAgent(const AtomValue Kind, const rosa::id_t Id, const std::string &Name, MessagingSystem &S) : Agent(Kind, Id, Name, S, Invoker::F([this](Report) noexcept { - LOG_INFO_STREAM << "MyAgent count: " << Counter << '\n'; + LOG_INFO_STREAM << "MyAgent count: " << PRINTABLE(Counter) + << '\n'; }), THISMEMBER(handler)), Counter(0) {} }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging-system example" << Color::Default << '\n'; std::unique_ptr S = MessagingSystem::createSystem("Sys"); MessagingSystem *SP = S.get(); LOG_INFO("\n\n** Stateless Agents\n"); AgentHandle Agent1 = SystemTester::createMyAgent( SP, "Agent1", Invoker::F([](const std::string &M) noexcept { LOG_INFO("Agent1: " + M); })); using Print = AtomConstant; using Forward = AtomConstant; AgentHandle Agent2 = SystemTester::createMyAgent( SP, "Agent2", Invoker::F([](Print, uint8_t N) noexcept { LOG_INFO("Agent2: " + std::to_string(N)); }), Invoker::F([&Agent1](Forward, uint8_t N) noexcept { if (Agent1) { Agent1.send(std::to_string(N)); } else { LOG_INFO("Agent2 cannot forward: Agent1 is not valid"); } })); LOG_INFO_STREAM << "\nAgent1 is valid: " << bool(Agent1) << "\nAgent2 is valid: " << bool(Agent2) << '\n'; LOG_INFO("Sending a print-message to Agent2..."); SP->send(Agent2, Print::Value, 42); LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); LOG_INFO("Sending an unexpected message to Agent2..."); SP->send(Agent2, unit); SystemTester::destroyMyAgent(SP, Agent1); LOG_INFO_STREAM << "\nAgent1 is valid: " << bool(Agent1) << "\nAgent2 is valid: " << bool(Agent2) << '\n'; LOG_INFO("Sending a forward-message to Agent2..."); SP->send(Agent2, Forward::Value, 42); SystemTester::destroyMyAgent(SP, Agent2); LOG_INFO("\n\n** Stateful Agents\n"); AgentHandle Agent3 = SystemTester::createMyAgent(SP, "Agent3"); for (size_t I = 0; I < 2; ++I) { LOG_INFO("Sending report-message to Agent3..."); Agent3.send(MyAgent::Report::Value); LOG_INFO("Sending tick-message to Agent3..."); Agent3.send(MyAgent::Tick::Value); } SystemTester::destroyMyAgent(SP, Agent3); LOG_INFO("\n"); return 0; } diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index ed453d6..64ede04 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,133 +1,133 @@ //===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/messaging/messaging.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief An example showcasing features related to the \c rosa::Message class. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << '\n'; auto &Log = LOG_INFO_STREAM << '\n'; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << "\n Size: " << Msg.Size << "\n Pos 0 is uint8_t: " << Msg.isTypeAt(0) << "\n Pos 1 is uint16_t: " << Msg.isTypeAt(1) << "\n Pos 2 is uint32_t: " << Msg.isTypeAt(1) << "\n Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << "\n Value at pos 1: " << Msg.valueAt(1) << "\n\n"; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyMatcher::doesStronglyMatch(Msg); auto Vs = MyMatcher::extractedValues(Msg); Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " - << std::get<1>(Vs) << ")'\n\n"; + << PRINTABLE(std::get<1>(Vs)) << ")'\n\n"; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n"; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; auto PAMsg = Message::create(A); auto &AMsg = *PAMsg; Log << "Checking on a 'Message with TypeList>':" << "\n Size: " << AMsg.Size << "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << "\n Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << "\n\n"; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << "\n value: '(" << std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" << "\n\n"; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << "\n\n"; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with " "TypeList>'.\n"; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << "\n with 'Message with TypeList'" << "\n does Message match Invoker: " << I.match(Msg) << "\n invoking..."; I(Msg); Log << "\n with 'Message with TypeList>'..." << "\n does Message match Invoker: " << I.match(AMsg) << "\n invoking..."; I(AMsg); Log << "\n\n"; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'\n"; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'\n"; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << "\n 'Message with TypeList'" << "\n can handle: " << Handler.canHandle(Msg) << "\n handling..."; Handler(Msg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(AMsg) << "\n handling..."; Handler(AMsg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(ANMsg) << "\n handling..."; Handler(ANMsg); Log << "\n\n"; Log << "Terminating, destroying automatic variables.\n"; return 0; } diff --git a/include/rosa/agent/ReliabilityConfidenceCombination.h b/include/rosa/agent/ReliabilityConfidenceCombination.h index ec0175f..0cfaf87 100644 --- a/include/rosa/agent/ReliabilityConfidenceCombination.h +++ b/include/rosa/agent/ReliabilityConfidenceCombination.h @@ -1,771 +1,775 @@ //===-- rosa/agent/ReliabilityConfidenceCombination.h -----------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/agent/ReliabilityConfidenceCombination.h /// /// \author Daniel Schnoell (daniel.schnoell@tuwien.ac.at) /// -/// \date 2019 +/// \date 2019-2020 /// /// \brief Definition of *ReliabilityConfidenceCombination* *functionality*. /// /// \note based on Maximilian Goetzinger (maxgot@utu.fi) code in /// CAM_Dirty_include SA-EWS2_Version... inside Agent.cpp /// /// \note By defining and setting Reliability_trace_level it is possible to /// change the level to which it should be traced. \note All classes throw /// runtime errors if not all things are set /// /// \note should the Reliability be capped? /// /// //===----------------------------------------------------------------------===// #ifndef ROSA_AGENT_ReliabilityConfidenceCombination_H #define ROSA_AGENT_ReliabilityConfidenceCombination_H #include "rosa/core/forward_declarations.h" // needed for id_t #include "rosa/support/log.h" #include "rosa/agent/FunctionAbstractions.hpp" #include "rosa/agent/Functionality.h" #include "rosa/agent/RangeConfidence.hpp" #include #include #include #include /// 0 everything /// 1 vectors /// 2 outputs #define trace_everything 0 #define trace_vectors 1 #define trace_outputs 2 #ifndef Reliability_trace_level #define Reliability_trace_level 0 #endif #define trace_end "\n\n\n" namespace rosa { namespace agent { /// This is a struct with a few methods that make Likeliness Combinator /// more readable \tparam IdentifierType The Data-type of the Identifiers /// \tparam LikelinessType The Data-type of the Likeliness \note this /// should/will be changed into a std::pair because it isn't needed anymore template struct Symbol { /// making both Template Arguments readable to make a few things easier using _IdentifierType = IdentifierType; /// making both Template Arguments readable to make a few things easier using _LikelinessType = LikelinessType; /// The actual place where the data is stored IdentifierType Identifier; /// The actual place where the data is stored LikelinessType Likeliness; Symbol(IdentifierType _Identifier, LikelinessType _Likeliness) : Identifier(_Identifier), Likeliness(_Likeliness) {} Symbol() = default; /// Pushes the Data in a Human readable form /// \param out The stream where it is written to /// \param c The struct itself friend std::ostream &operator<<(std::ostream &out, const Symbol &c) { - out << "Identifier: " << c.Identifier << "\t Likeliness: " << c.Likeliness - << " "; + out << "Identifier: " << PRINTABLE(c.Identifier) + << "\t Likeliness: " << PRINTABLE(c.Likeliness) << " "; return out; } /// needed or it throws an clang diagnosic error using map = std::map; // needed or it throws an // clang diagnosic error /// Filles the vector with the data inside the map /// \param me The vector to be filled /// \param data The data wich is to be pushed into the vector friend std::vector &operator<<(std::vector &me, map &&data) { for (auto tmp : data) { me.push_back(Symbol(tmp.first, tmp.second)); #if Reliability_trace_level <= trace_everything LOG_TRACE_STREAM << "\n" << Symbol(tmp.first, tmp.second) << trace_end; #endif } return me; } /// This is to push the data inside a vector in a human readable way into the /// ostream \param out The ostream \param c The vector which is read friend std::ostream &operator<<(std::ostream &out, const std::vector &c) { std::size_t index = 0; for (Symbol data : c) { - out << index << " : " << data << "\n"; + out << PRINTABLE(index) << " : " << data << "\n"; index++; } return out; } }; /// This is the combinator for Reliability and confidences it takes the /// Value, its "History" and feedback from \c /// CrossCombinator to calculate different Reliabilities. /// \tparam ValueType Data-type of the Value ( Typically /// double or float) \tparam IdentifierType Data-type of the Identifier ( /// Typically long or int) /// \tparam ReliabilityType Data-type of the Reliability ( /// Typically double or float) /// /// \note more information about how it calculates /// the Reliabilities it should be considered feedback is a sort of Confidence /// \verbatim ///---------------------------------------------------------------------------------- /// /// /// ->AbsoluteReliabilityFunction---> getInputReliability() /// | | /// | V /// Sensor Value ---| ReliabilityAndConfidenceCombinator -> next line /// | A | /// | | V /// ->ConfidenceFunction /// getPossibleIdentifiers() /// ///----------------------------------------------------------------------------------- /// /// feedback /// | /// V /// FeedbackSymbols /// | -> History -------| /// V | V /// here -> LikelinessFeedbackCombinator /// ------>LikelinessHistoryCombinator->next line /// | | /// V V /// getpossibleIdentifiersWithMasterFeedback() SymbolsWithHistory() /// ///---------------------------------------------------------------------------------- /// /// here -> sort -> most likely -> bestSymbol() /// ///--------------------------------------------------------------------------------- /// \endverbatim /// the mentioned methods are early outs so if two ore more of them are run in /// the same step they will be interpreted as different time steps ///
 /// Default values for Combinators:
 ///	AbsoluteAndSlopeReliabilityCombinationMethod		=
 /// combinationMin;
 ///	ReliabilityAndConfidenceCombinator=ReliabilityAndConfidenceCombinatorMin;
 /// LikelinessFeedbackCombinator		=
 /// LikelinessFeedbackCombinatorAverage; LikelinessHistoryCombinator
 /// = LikelinessHistoryCombinatorMax;
 ///	
/// To understand the place where the combinator methods come into play a list /// for each early exit and which Methods are used. /// ///
 /// \c getInputReliability():
 ///		-AbsoluteAndSlopeReliabilityCombinationMethod
 /// \c getPossibleIdentifiers():
 ///		-AbsoluteAndSlopeReliabilityCombinationMethod
 ///		-ReliabilityAndConfidenceCombinator
 /// \c getpossibleIdentifiersWithMasterFeedback():
 ///		-AbsoluteAndSlopeReliabilityCombinationMethod
 ///		-ReliabilityAndConfidenceCombinator
 ///		-LikelinessFeedbackCombinator
 /// \c SymbolsWithHistory():
 ///		-AbsoluteAndSlopeReliabilityCombinationMethod
 ///		-ReliabilityAndConfidenceCombinator
 ///		-LikelinessFeedbackCombinator
 ///		-LikelinessHistoryCombinator
 /// \c bestSymbol():
 ///		-AbsoluteAndSlopeReliabilityCombinationMethod
 ///		-ReliabilityAndConfidenceCombinator
 ///		-LikelinessFeedbackCombinator
 ///		-LikelinessHistoryCombinator
 /// 
template class ReliabilityAndConfidenceCombination { public: static_assert(std::is_arithmetic::value, "LowLevel: ValueType has to an arithmetic type\n"); static_assert(std::is_arithmetic::value, "LowLevel: ReliabilityType has to an arithmetic type\n"); /// Typedef to shorten the writing. /// \c Symbol using Symbol = Symbol; /// Calculates the input Reliability by combining Reliability of the Sensor /// and the Slope Reliability \param SensorValue The sensor Value \note to set /// the combination method \c /// setAbsoluteAndSlopeReliabilityCombinationMethod() ReliabilityType getInputReliability(const ValueType &SensorValue) noexcept { ReliabilityType inputReliability = getReliability(SensorValue, previousSensorValue, timeStep); previousSensorValue = SensorValue; PreviousSensorValueExists = true; return inputReliability; } /// Calculates the possible Identifiers /// \param SensorValue the Sensor Value /// \brief it combines the input reliability and the confidence of the Sensor. /// The use combination method can be set using \c /// setReliabilityAndConfidenceCombinator() std::vector getPossibleIdentifiers(const ValueType &SensorValue) noexcept { std::vector possibleIdentifiers; ReliabilityType inputReliability = getInputReliability(SensorValue); #if Reliability_trace_level <= trace_vectors - LOG_TRACE_STREAM << "\ninput Rel: " << inputReliability << trace_end; + LOG_TRACE_STREAM << "\ninput Rel: " << PRINTABLE(inputReliability) + << trace_end; #endif possibleIdentifiers << ConfidenceFunction->operator()(SensorValue); possibleIdentifiers = ReliabilityAndConfidenceCombinator( possibleIdentifiers, inputReliability); return possibleIdentifiers; } /// return the Possible Values with the feedback in mind /// \param SensorValue The sensor Value /// \brief it combines the input reliability and the confidence of the Sensor. /// The combines them with LikelinessFeedbackCombinator and returns the /// result. std::vector getpossibleIdentifiersWithMasterFeedback( const ValueType &SensorValue) noexcept { std::vector possibleIdentifiers; ReliabilityType inputReliability = getInputReliability(SensorValue); #if Reliability_trace_level <= trace_vectors - LOG_TRACE_STREAM << "\ninput Rel: " << inputReliability << trace_end; + LOG_TRACE_STREAM << "\ninput Rel: " << PRINTABLE(inputReliability) + << trace_end; #endif possibleIdentifiers << ConfidenceFunction->operator()(SensorValue); possibleIdentifiers = ReliabilityAndConfidenceCombinator( possibleIdentifiers, inputReliability); possibleIdentifiers = LikelinessFeedbackCombinator(possibleIdentifiers, FeedbackSymbols); return possibleIdentifiers; } /// returns all possible Identifiers and Reliabilities with the History in /// mind \param SensorValue the Sensor value how this is done is described at /// the class. std::vector SymbolsWithHistory(const ValueType &SensorValue) noexcept { std::vector ActuallPossibleIdentifiers; std::vector possibleIdentifiers; ReliabilityType inputReliability = getInputReliability(SensorValue); #if Reliability_trace_level <= trace_vectors - LOG_TRACE_STREAM << "\ninput Rel: " << inputReliability << trace_end; + LOG_TRACE_STREAM << "\ninput Rel: " << PRINTABLE(inputReliability) + << trace_end; #endif possibleIdentifiers << ConfidenceFunction->operator()(SensorValue); possibleIdentifiers = ReliabilityAndConfidenceCombinator( possibleIdentifiers, inputReliability); possibleIdentifiers = LikelinessFeedbackCombinator(possibleIdentifiers, FeedbackSymbols); saveInHistory(possibleIdentifiers); #if Reliability_trace_level <= trace_vectors LOG_TRACE_STREAM << "\nActuallPossibleIdentifiers:\n" << possibleIdentifiers << trace_end; LOG_TRACE_STREAM << "\npossibleIdentifiers:\n" << possibleIdentifiers << trace_end; #endif possibleIdentifiers.clear(); return SymbolsFromHistory(); } /// Calculates the Reliability /// \param SensorValue The current Values of the Sensor /// /// \return Reliability and Identifier of the current SensorValue /// Symbol bestSymbol(const ValueType &SensorValue) noexcept { #if Reliability_trace_level <= trace_outputs - LOG_TRACE_STREAM << "\nTrace level is set to: " << Reliability_trace_level - << "\n" + LOG_TRACE_STREAM << "\nTrace level is set to: " + << PRINTABLE(Reliability_trace_level) << "\n" << "Will trace: " << ((Reliability_trace_level == trace_outputs) ? "outputs" : (Reliability_trace_level == trace_vectors) ? "vectors" : (Reliability_trace_level == trace_everything) ? "everything" : "undefined") << trace_end; #endif std::vector ActuallPossibleIdentifiers; std::vector possibleIdentifiers; ReliabilityType inputReliability = getInputReliability(SensorValue); #if Reliability_trace_level <= trace_vectors - LOG_TRACE_STREAM << "\ninput Rel: " << inputReliability << trace_end; + LOG_TRACE_STREAM << "\ninput Rel: " << PRINTABLE(inputReliability) + << trace_end; #endif possibleIdentifiers << ConfidenceFunction->operator()(SensorValue); possibleIdentifiers = ReliabilityAndConfidenceCombinator( possibleIdentifiers, inputReliability); possibleIdentifiers = LikelinessFeedbackCombinator(possibleIdentifiers, FeedbackSymbols); saveInHistory(possibleIdentifiers); #if Reliability_trace_level <= trace_vectors LOG_TRACE_STREAM << "\nActuallPossibleIdentifiers:\n" << possibleIdentifiers << trace_end; LOG_TRACE_STREAM << "\npossibleIdentifiers:\n" << possibleIdentifiers << trace_end; #endif possibleIdentifiers.clear(); possibleIdentifiers = SymbolsFromHistory(); std::sort( possibleIdentifiers.begin(), possibleIdentifiers.end(), [](Symbol A, Symbol B) -> bool { return A.Likeliness > B.Likeliness; }); #if Reliability_trace_level <= trace_outputs LOG_TRACE_STREAM << "\noutput lowlevel: " << possibleIdentifiers.at(0) << trace_end; #endif return possibleIdentifiers.at(0); } /// feedback for this functionality most commonly it comes from a Master Agent /// \param _FeedbackSymbols The Identifiers + Reliability for the feedback /// \brief This input kind of resembles a confidence but not /// directly it more or less says: compared to the other Identifiers inside /// the System these are the Identifiers with the Reliability that you have. void feedback( const std::vector &_FeedbackSymbols) noexcept // it is being copied internally anyway { FeedbackSymbols = _FeedbackSymbols; } // // ----------------------Reliability and Confidence Function setters---------- // /// This is the setter for Confidence Function /// \param _ConfidenceFunction A pointer to the Functional for the /// \c Confidence of the Sensor value void setConfidenceFunction( std::shared_ptr< RangeConfidence> &_ConfidenceFunction) noexcept { ConfidenceFunction = _ConfidenceFunction; } /// This is the setter for AbsoluteReliabilityFunction /// \param _AbsoluteReliabilityFunction A pointer to the Functional for the /// AbsoluteReliabilityFunction \brief The AbsoluteReliabilityFunction takes /// the current Sensor value and return the AbsoluteReliabilityFunction of the /// value. void setAbsoluteReliabilityFunction( std::shared_ptr> &_AbsoluteReliabilityFunction) noexcept { AbsoluteReliabilityFunction = _AbsoluteReliabilityFunction; } /// This is the setter for ReliabilitySlopeFunction Function /// \param _ReliabilitySlopeFunction A pointer to the Functional for the /// ReliabilitySlopeFunction /// \brief The ReliabilitySlopeFunction takes the difference of the current /// Sensor Value to the last one and tells you how likely the change is. void setReliabilitySlopeFunction( std::shared_ptr> &_ReliabilitySlopeFunction) noexcept { ReliabilitySlopeFunction = _ReliabilitySlopeFunction; } /// This is the setter for TimeFunctionForLikeliness Function /// \param _TimeFunctionForLikeliness A pointer to the Functional for the /// TimeFunctionForLikeliness \brief The time function takes the position in /// the History with greater equals older and return a Reliability of how /// "relevant" it is. void setTimeFunctionForLikelinessFunction( std::shared_ptr> &_TimeFunctionForLikeliness) noexcept { TimeFunctionForLikeliness = _TimeFunctionForLikeliness; } /// This is the setter for all possible Identifiers /// \param _Identifiers A vector containing all Identifiers /// \brief This exists even though \c Identifier Type is an arithmetic Type /// because the Identifiers do not need to be "next" to each other ( ex. /// Identifiers={ 1 7 24 }) void setIdentifiers(const std::vector &_Identifiers) noexcept { this->Identifiers = _Identifiers; } /// This sets the Maximum length of the History /// \param length The length void setHistoryLength(const std::size_t &length) noexcept { this->HistoryMaxSize = length; } /// This sets the Value set Counter /// \param _timeStep the new Value /// \note This might actually be only an artifact. It is only used to get the /// reliability from the \c ReliabilitySlopeFunction [ /// ReliabilitySlopeFunction->operator()( (lastValue - actualValue) / /// (ValueType)timeStep) ] void setTimeStep(const unsigned int &_timeStep) noexcept { this->timeStep = _timeStep; } // // ----------------combinator setters----------------------------------------- // /// This sets the combination method used by the History /// \param Meth the method which should be used. predefined inside the \c /// predefinedMethods struct LikelinessHistoryCombinator() void setLikelinessHistoryCombinator( const std::function &Meth) noexcept { LikelinessHistoryCombinator = Meth; } /// sets the predefined method for the combination of the possible Identifiers /// and the master /// \param Meth the method which should be used. predefined inside the \c /// predefinedMethods struct LikelinessFeedbackCombinator() void setLikelinessFeedbackCombinator( const std::function( std::vector, std::vector)> &Meth) noexcept { LikelinessFeedbackCombinator = Meth; } /// Sets the used combination method for Possible Identifiers /// \param Meth the method which should be used. predefined inside the \c /// predefinedMethods struct setReliabilityAndConfidenceCombinator() void setReliabilityAndConfidenceCombinator( const std::function( std::vector, ReliabilityType)> &Meth) noexcept { ReliabilityAndConfidenceCombinator = Meth; } /// sets the input reliability combinator method /// \param method the method which should be used. predefined inside the \c /// predefinedMethods struct combination() void setAbsoluteAndSlopeReliabilityCombinationMethod( const std::function &&method) noexcept { AbsoluteAndSlopeReliabilityCombinationMethod = method; } // // ----------------predefined combinators------------------------------------ // /// This struct is a pseudo name space to have easier access to all predefined /// methods while still not overcrowding the class it self struct predefinedMethods { /// predefined Method static ReliabilityType LikelinessHistoryCombinatorMin(ReliabilityType A, ReliabilityType B) noexcept { return std::min(A, B); } /// predefined Method static ReliabilityType LikelinessHistoryCombinatorMax(ReliabilityType A, ReliabilityType B) noexcept { return std::max(A, B); } /// predefined Method static ReliabilityType LikelinessHistoryCombinatorMult(ReliabilityType A, ReliabilityType B) noexcept { return A * B; } /// predefined Method static ReliabilityType LikelinessHistoryCombinatorAverage(ReliabilityType A, ReliabilityType B) noexcept { return (A + B) / 2; } /// predefined method static std::vector LikelinessFeedbackCombinatorAverage(std::vector A, std::vector B) noexcept { for (auto &tmp_me : A) for (auto &tmp_other : B) { if (tmp_me.Identifier == tmp_other.Identifier) { tmp_me.Likeliness = (tmp_me.Likeliness + tmp_other.Likeliness) / 2; } } return A; } /// predefined method static std::vector LikelinessFeedbackCombinatorMin(std::vector A, std::vector B) noexcept { for (auto &tmp_me : A) for (auto &tmp_other : B) { if (tmp_me.Identifier == tmp_other.Identifier) { tmp_me.Likeliness = std::min(tmp_me.Likeliness + tmp_other.Likeliness); } } return A; } /// predefined method static std::vector LikelinessFeedbackCombinatorMax(std::vector A, std::vector B) noexcept { for (auto &tmp_me : A) for (auto &tmp_other : B) { if (tmp_me.Identifier == tmp_other.Identifier) { tmp_me.Likeliness = std::max(tmp_me.Likeliness + tmp_other.Likeliness); } } return A; } /// predefined method static std::vector LikelinessFeedbackCombinatorMult(std::vector A, std::vector B) noexcept { for (auto &tmp_me : A) for (auto &tmp_other : B) { if (tmp_me.Identifier == tmp_other.Identifier) { tmp_me.Likeliness = tmp_me.Likeliness * tmp_other.Likeliness; } } return A; } /// Predefined combination method for possible Identifiers static std::vector ReliabilityAndConfidenceCombinatorMin(std::vector A, ReliabilityType B) noexcept { for (auto tmp : A) tmp.Likeliness = std::min(tmp.Likeliness, B); return A; } /// Predefined combination method for possible Identifiers static std::vector ReliabilityAndConfidenceCombinatorMax(std::vector A, ReliabilityType B) noexcept { for (auto tmp : A) tmp.Likeliness = std::max(tmp.Likeliness, B); return A; } /// Predefined combination method for possible Identifiers static std::vector ReliabilityAndConfidenceCombinatorAverage(std::vector A, ReliabilityType B) noexcept { for (auto tmp : A) tmp.Likeliness = (tmp.Likeliness + B) / 2; return A; } /// Predefined combination method for possible Identifiers static std::vector ReliabilityAndConfidenceCombinatorMult(std::vector A, ReliabilityType B) noexcept { for (auto tmp : A) tmp.Likeliness = tmp.Likeliness * B / 2; return A; } /// The predefined min combinator method static ReliabilityType combinationMin(ReliabilityType A, ReliabilityType B) noexcept { return std::min(A, B); } /// The predefined max combinator method static ReliabilityType combinationMax(ReliabilityType A, ReliabilityType B) noexcept { return std::max(A, B); } /// The predefined average combinator method static ReliabilityType combinationAverage(ReliabilityType A, ReliabilityType B) noexcept { return (A + B) / 2; } /// The predefined average combinator method static ReliabilityType combinationMult(ReliabilityType A, ReliabilityType B) noexcept { return A * B; } }; // ---------------------------------------------------------------- // Stored Values // ---------------------------------------------------------------- private: std::vector> History; std::size_t HistoryMaxSize; std::vector FeedbackSymbols; ValueType previousSensorValue; unsigned int timeStep; std::vector Identifiers; bool PreviousSensorValueExists = false; std::shared_ptr> ConfidenceFunction; std::shared_ptr> AbsoluteReliabilityFunction; std::shared_ptr> ReliabilitySlopeFunction; std::shared_ptr> TimeFunctionForLikeliness; // combination functions std::function AbsoluteAndSlopeReliabilityCombinationMethod = predefinedMethods::combinationMin; std::function(std::vector, ReliabilityType)> ReliabilityAndConfidenceCombinator = predefinedMethods::ReliabilityAndConfidenceCombinatorMin; std::function(std::vector, std::vector)> LikelinessFeedbackCombinator = predefinedMethods::LikelinessFeedbackCombinatorAverage; std::function LikelinessHistoryCombinator = predefinedMethods::LikelinessHistoryCombinatorMax; // --------------------------------------------------------------------------- // needed Functions // --------------------------------------------------------------------------- /// returns the Reliability /// \param actualValue The Value of the Sensor /// \param lastValue of the Sensor this is stored in the class /// \param _timeStep It has an effect on the difference of the current /// and last value This might not be needed anymore /// \brief it returns the combination the \c Reliability function and \c /// ReliabilitySlopeFunction if the previous value exists. if it doesn't it /// only returns the \c Reliability function value. ReliabilityType getReliability(const ValueType &actualValue, const ValueType &lastValue, const unsigned int &_timeStep) noexcept { ReliabilityType relAbs = AbsoluteReliabilityFunction->operator()(actualValue); if (PreviousSensorValueExists) { ReliabilityType relSlo = ReliabilitySlopeFunction->operator()( (lastValue - actualValue) / static_cast(_timeStep)); // return AbsoluteAndSlopeReliabilityCombinationMethod(relAbs, relSlo); } else return relAbs; } /// adapts the possible Identifiers by checking the History and combines those /// values. /// \brief combines the historic values with the \c TimeFunctionForLikeliness /// function and returns the maximum Reliability for all Identifiers. std::vector SymbolsFromHistory() noexcept { // iterate through all history entries std::size_t posInHistory = 0; std::vector symbolFromHistory; // History Symbols for (auto step = History.begin(); step < History.end(); step++, posInHistory++) { // iterate through all possible Identifiers of each history entry for (Symbol &symbol : *step) { IdentifierType historyIdentifier = symbol.Identifier; ReliabilityType historyConf = symbol.Likeliness; historyConf = historyConf * TimeFunctionForLikeliness->operator()(posInHistory); bool foundIdentifier = false; for (Symbol &pS : symbolFromHistory) { if (pS.Identifier == historyIdentifier) { pS.Likeliness = LikelinessHistoryCombinator(pS.Likeliness, historyConf); foundIdentifier = true; } } if (foundIdentifier == false) { Symbol possibleIdentifier; // Symbol possibleIdentifier.Identifier = historyIdentifier; possibleIdentifier.Likeliness = historyConf; symbolFromHistory.push_back(possibleIdentifier); } } } return symbolFromHistory; } /// saves the Identifiers in the History /// \brief It checks the incoming Identifiers if any have a Reliability /// greater than 0.5 all of them get saved inside the History and then the /// History get shortened to the maximal length. It only saves the Value if /// the History is empty. /// /// \param actualPossibleIdentifiers The Identifiers which should be saved /// /// \note Does the History really make sense if the values are to small it /// only stores something if it's empty and not if it isn't completely filled void saveInHistory(const std::vector &actualPossibleIdentifiers) noexcept { // Symbols // check if the reliability of at least one possible Identifier is high // enough bool atLeastOneRelIsHigh = false; for (Symbol pS : actualPossibleIdentifiers) { if (pS.Likeliness > 0.5) { atLeastOneRelIsHigh = true; } } // save possible Identifiers if at least one possible Identifier is high // enough (or if the history is empty) if (History.size() < 1 || atLeastOneRelIsHigh == true) { History.insert(History.begin(), actualPossibleIdentifiers); // if history size is higher than allowed, save oldest element while (History.size() > HistoryMaxSize) { // delete possibleIdentifierHistory.back(); History.pop_back(); } } } }; } // namespace agent } // namespace rosa #endif // !ROSA_AGENT_ReliabilityConfidenceCombination_H diff --git a/include/rosa/app/AppSensor.hpp b/include/rosa/app/AppSensor.hpp index 14417ed..14359d1 100644 --- a/include/rosa/app/AppSensor.hpp +++ b/include/rosa/app/AppSensor.hpp @@ -1,671 +1,671 @@ //===-- rosa/app/AppSensor.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/app/AppSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2020 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the /// *application interface*. /// /// \see \c rosa::app::Application /// //===----------------------------------------------------------------------===// #ifndef ROSA_APP_APPSENSOR_HPP #define ROSA_APP_APPSENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/app/AppAtoms.hpp" #include "rosa/app/AppExecutionPolicy.h" #include "rosa/app/AppTuple.hpp" #include "rosa/support/diagnostics.h" /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::app::AppSensor. /// /// \param N name suffix to use #define ASMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *master* in /// \c rosa::app::AppSensor. /// /// \see \c AppSensorMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::app::AppSensor::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define ASMASTERHANDLERDEFN(T, N) \ void ASMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ saveMasterInput(MasterId, Pos, Value); \ } /// Convenience macro for \c ASMASTERHANDLERDEFN with identical arguments. /// /// \see \c ASMASTERHANDLERDEFN /// /// This macro can be used instead of \c ASMASTERHANDLERDEFN 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 ASMASTERHANDLERDEF(T) ASMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c ASMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::app::AppSensor to initialize super /// class \c rosa::Agent with member function defined by \c ASMASTERHANDLERDEFN. /// /// \see \c ASMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define ASMASTERHANDLERREF(N) THISMEMBER(ASMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace app { /// Specialization of \c rosa::Agent for *sensor* role of the *application /// interface*. /// /// \see \c rosa::app::Application /// /// \invariant There is a compatible *execution policy* set; the actual value in /// \c rosa::app::AppSensor::MasterInputNextPos is valid with respect to /// the corresponding types. /// /// \see Definition of \c rosa::app::AppSensor::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 AppSensor : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::app::AppSensor /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; /// The \c rosa::app::AppExecutionPolicy that controls the execution of /// \c this object. std::unique_ptr ExecutionPolicy; public: /// The type of values produced by \p this object. /// /// That is the types of values \p this object sends to its *master* in a /// \c rosa::app::AppTuple. /// /// \see \c rosa::app::AppSensor::master const Token OutputType; /// The type of values \p this object processes from its *master*. /// /// That is the types of values \p this object receives from its *master* in a /// \c rosa::app::AppTuple. /// /// \see \c rosa::app::AppSensor::master const Token MasterInputType; private: /// Indicates which element of the master-input is expected from the *master*. /// /// The *master* is supposed to send one \c rosa::app::AppTuple value /// element by element in their order of definition. This member field tells /// the element at which position should be received next. /// /// \p this object is supposed to be triggered only when a complete /// master-input has been received, that is the field should hold the value /// `0`. /// /// \see \c rosa::app::AppSensor::handleTrigger /// \c rosa::app::AppSensor::saveMasterInput token_size_t MasterInputNextPos; /// 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::app::AppSensor::saveMasterInput when storig a new /// input value in \c rosa::app::AppSensor::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// /// \note The type of the stored value matches the types indicated by \c /// rosa::app::AppSensor::MasterInputType. const std::unique_ptr MasterInputValue; /// Alias for function objects used as trigger handler for /// \c rosa::app::AppSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c AppSensorTriggerHandlers using H = std::function; /// \defgroup AppSensorTriggerHandlers Trigger handlers of /// rosa::app::AppSensor /// /// \brief Trigger handler functions of \c rosa::app::AppSensor /// /// 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::app::AppSensor::sendToMaster. Also, the flag \c /// rosa::app::AppSensor::MasterInputChanged is reset when the current /// value is passed to the master-input processing function. The function \c /// rosa::app::AppSensor::handleTrigger needs only to call the proper /// function object. /// Processes master-input. /// /// \ingroup AppSensorTriggerHandlers /// /// The function is called upon the sensor is trigged by the system. const H MFP; /// Produces the next sensory value during normal execution. /// /// \ingroup AppSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::app::AppSensor::SFP, which is /// used instead of \c rosa::app::AppSensor::FP. const H FP; /// Produces the next sensory value during simulation. /// /// \ingroup AppSensorTriggerHandlers /// /// 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::app::AppSensor 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::app::AppSensor::MFP and \c AppSensorTriggerHandlers /// /// \tparam Ts types of elements of master-input processed by \p MF /// \tparam S0 indices for accessing master-input values /// /// \param MF function that processes master-input /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \note A master-input type of \c rosa::app::EmptyAppTuple indicates /// that \p this object does not receive master-input, \p MF is never called /// if \p Ts is empty. /// /// \return trigger handler function based on \p MF /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts... match \c /// rosa::app::AppSensor::MasterInputType: \code /// MasterInputType == AppTuple::TT /// \endcode template H triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept; /// Wraps a data source function into a trigger handler. /// /// \see \c rosa::app::AppSensor::FP, \c /// rosa::app::AppSensor::SFP, and \c AppSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// \param inSimulation if F is a data source for Simulation /// /// \return trigger handler function based on \p F /// /// \pre Statically, the type agument \p T is an instance of \c /// rosa::app::AppTuple: \code /// IsAppTuple::Value /// \endcode Dynamically, \p T matches \c /// rosa::app::AppSensor::OutputType: \code /// OutputType == T::TT /// \endcode template H triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *application interface*. /// /// \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 MT and \p T is /// not an instance of \c rosa::app::AppTuple or \p T is \c /// rosa::app::EmptyAppTuple. /// /// \note If \p MT is \c rosa::app::EmptyAppTuple, 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 MT and \p T are instances of \c /// rosa::app::AppTuple and \p T contains at least one element:\code /// TypeListAllAppTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::app::atoms::SensorKind: /// \code /// Kind == rosa::app::atoms::SensorKind /// \endcode /// /// \see \c rosa::app::AppTuple template >::Value && (T::Length > 0)>> AppSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept; /// Destroys \p this object. ~AppSensor(void) noexcept; /// Returns the current execution policy of \p this object. /// /// \see \c rosa::app::AppExecutionPolicy /// /// \note The returned reference is valid only as long as \c /// rosa::app::AppSensor::setExecutionPolicy() is not called and \p this /// object is not destroyed. /// /// \return \c rosa::app::AppSensor::ExecutionPolicy const AppExecutionPolicy &executionPolicy(void) const noexcept; /// Sets the current execution policy of \p this object to \p EP. /// /// \see \c rosa::app::AppExecutionPolicy /// /// \note \p EP is set only if it can handle \p this object. /// /// \param EP the new execution policy for \p this object /// /// \return if \p EP was successfully set for \p this object. bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; /// The *master* of \p this object, if any. /// /// \see \c rosa::app::AppSensor::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::app::AppSensor::registerMaster should be /// paired with a corresponding call of \c /// rosa::app::AppAgent::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::app::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::app::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::app::AppSensor::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::app::AppSensor::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::app::AppSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// /// \pre \p Ts... match \c rosa::app::AppSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void registerSimulationDataSource( std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::app::AppSensor::Master if it /// contains a valid handle for a \c rosa::app::AppAgent. The function /// does nothing otherwise. /// /// The elements from \p Value are sent one by one in separate messages to the /// *master*. /// /// \tparam Ts types of the elements in \p Value /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts match \c /// rosa::app::AppSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void sendToMaster(const AppTuple &Value, Seq) noexcept; /// Handles master-input and generates the next sensory value upon trigger /// from the system. /// /// Executes \c rosa::app::AppSensor::MFP for processing master-input /// and data generating function \c rosa::app::AppSensor::FP or \c /// rosa::app::AppSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. /// /// \pre Master-input is supposed to be completely received upon triggering: /// \code /// MasterInputNextPos == 0 /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from the *master*. /// /// The function stores \p Value at position \p Pos in \c /// rosa::app::AppSensor::MasterInputValue and also sets the /// flag \c rosa::app::AppSensor::MasterInputChanged. The function also /// takes care of checking and updating \c /// rosa::app::AppSensor::MasterInputNextPos: increments its value and /// resets it to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// AppSensorMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* /// \param Pos position of the value in the \c rosa::app::AppTuple /// \param Value the input value to store /// /// \pre The *master* with \p Id is registered, \p Pos is the expected /// position of master-input, and the input from the *master* at position \p /// Pos is expected to be of type \p T: \code /// Master && masterId() == Id && Pos == MasterInputNextPos && /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode template void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; /// \defgroup AppSensorMasterInputHandlers Master-input handlers of /// rosa::app::AppSensor /// /// 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 to handle messages from its *master*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::app::AppSensor::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 /// ASMASTERHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ ASMASTERHANDLERDEF(AtomValue) ASMASTERHANDLERDEF(int16_t) ASMASTERHANDLERDEF(int32_t) ASMASTERHANDLERDEF(int64_t) ASMASTERHANDLERDEF(int8_t) ASMASTERHANDLERDEFN(long double, long_double) ASMASTERHANDLERDEFN(std::string, std__string) ASMASTERHANDLERDEF(uint16_t) ASMASTERHANDLERDEF(uint32_t) ASMASTERHANDLERDEF(uint64_t) ASMASTERHANDLERDEF(uint8_t) ASMASTERHANDLERDEF(unit_t) ASMASTERHANDLERDEF(bool) ASMASTERHANDLERDEF(double) ASMASTERHANDLERDEF(float) /// @} }; template AppSensor::H AppSensor::triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept { using MT = AppTuple; STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(MasterInputType == MT::TT); // NOTE: Clang 6 warns about unused lambda captures; we suppress that // warning (those variables need to be captured). ROSA_DISABLE_WARNING_PUSH; ROSA_DISABLE_WARNING_UNUSED_LAMBDA_CAPTURE; return [ this, MF ](void) noexcept { // Do not do anything for master-input type \c // rosa::app::EmptyAppTuple. if constexpr (!std::is_same::value) { LOG_TRACE_STREAM << "AppSensor " << FullName << " handles master-input." << std::endl; // The assert must hold if \p this object was successfully constructed. STATIC_ASSERT( (true && ... && (static_cast(static_cast(S0)) == S0)), "Unexpected error"); const auto MasterInputArg = std::make_pair( // Get all elements of the tuple in a fold expression. AppTuple(*static_cast( MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; MF(MasterInputArg); } }; ROSA_DISABLE_WARNING_POP; } template AppSensor::H AppSensor::triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept { STATIC_ASSERT(IsAppTuple::Value, "not tuple type argument"); ASSERT(OutputType == T::TT); return [ this, F, inSimulation ](void) noexcept { // Get value and send it to master only if \p ExecutionPolicy allows it. if (ExecutionPolicy->shouldProcess({})) { LOG_TRACE_STREAM << "AppSensor " << Name << " obtains next value." << std::endl; sendToMaster(F(), seq_t()); } else { LOG_TRACE_STREAM << "AppSensor " << Name << " skips next value." << std::endl; if (inSimulation) { // But read input value in Simulation anyway as input values are // provided for the highest execution frequency for simulation (void)F(); } } }; } template AppSensor::AppSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), ASMASTERHANDLERREF(AtomValue), ASMASTERHANDLERREF(int16_t), ASMASTERHANDLERREF(int32_t), ASMASTERHANDLERREF(int64_t), ASMASTERHANDLERREF(int8_t), ASMASTERHANDLERREF(long_double), ASMASTERHANDLERREF(std__string), ASMASTERHANDLERREF(uint16_t), ASMASTERHANDLERREF(uint32_t), ASMASTERHANDLERREF(uint64_t), ASMASTERHANDLERREF(uint8_t), ASMASTERHANDLERREF(unit_t), ASMASTERHANDLERREF(bool), ASMASTERHANDLERREF(double), ASMASTERHANDLERREF(float)), ExecutionPolicy(AppExecutionPolicy::decimation(1)), OutputType(T::TT), MasterInputType(MT::TT), MasterInputNextPos(0), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapAppTuple::Type>::Type()), MFP(triggerHandlerFromProcessingFunction(std::move(MF), seq_t())), FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE_STREAM << "AppSensor " << FullName << " is created." << std::endl; ASSERT(inv()); } template void AppSensor::registerSimulationDataSource( std::function(void)> &&SF) noexcept { ASSERT(OutputType == TypeToken::Value); SFP = triggerHandlerFromDataSource(std::move(SF), true); ASSERT(inv()); } template void AppSensor::sendToMaster(const AppTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(OutputType == TypeToken::Value); // The assert must hold if \p this object was successfully constructed. STATIC_ASSERT((true && ... && (static_cast(static_cast(S0)) == S0)), "Unexpected error"); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array Indices{{S0...}}; LOG_TRACE_STREAM << "AppSensor " << FullName << "(" << Id << ") sends to master(" << static_cast(Master && *Master) << "): " << Value << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { // Handle each element of the tuple in a fold expression. (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], std::get(Value))), ...); } ASSERT(inv()); } template void AppSensor::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); LOG_TRACE_STREAM << "AppSensor " << FullName << "(" << Id << ") saves value from master: (" << static_cast(Pos) - << ") " << Value << std::endl; + << ") " << PRINTABLE(Value) << std::endl; // Save value. *static_cast(MasterInputValue->pointerTo(Pos)) = Value; // Update position of next value. if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { MasterInputNextPos = 0; } // Set flag. MasterInputChanged = true; } } // End namespace app } // End namespace rosa #undef ASMASTERHANDLEREF #undef ASMASTERHANDLEDEF #undef ASMASTERHANDLEDEFN #undef ASMASTERHANDLENAME #endif // ROSA_APP_APPSENSOR_HPP diff --git a/include/rosa/app/Application.hpp b/include/rosa/app/Application.hpp index 08afd6b..e7a80ee 100644 --- a/include/rosa/app/Application.hpp +++ b/include/rosa/app/Application.hpp @@ -1,931 +1,931 @@ //===-- rosa/app/Application.hpp --------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/app/Application.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2020 /// /// \brief Public interface for the *application interface* for working with /// agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_APP_APPLICATION_HPP #define ROSA_APP_APPLICATION_HPP #include "rosa/app/AppSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::app::Application::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::app::Application::ErrorCode value to log and /// return #define APPRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace app { /// Defines the *application interface*. /// /// \todo The classes \c rosa::app::AppSensor and \c /// rosa::app::AppAgent share some common features in relation to their /// *slave* role in the *application interface*. But their definitions are /// completely independent. It could be investigated how to lift their common /// parts into a new *application slave* class, which would serve as base for /// both, to avoid code duplication. class Application { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::app::Application::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set AppUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, NotUnit, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream, UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::app::Application. /// /// \param Name name of the underlying \c rosa::AppSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::app::Application with a new, empty \c rosa::AppSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem Application(const std::string &Name) noexcept; public: /// Destroys \p this object. ~Application(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; private: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T /// is not an instance of \c rosa::app::AppTuple or \p T is \c /// rosa::app::EmptyAppTuple. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::app::Application::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::app::AppSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::app::AppSensor::AppSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template < typename MT, typename T, typename = std::enable_if>::Value && !std::is_same::value>> AgentHandle createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept; public: /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* does not receive master-input. /// /// \tparam T type of data the new *sensor* operates on /// /// \note Instantiation fails if type argument \p T is neither a built-in type /// nor an instance of \c rosa::app::AppTuple with at least one element. /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::app::Application::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::app::AppSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::app::AppSensor::AppSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template ::Value || (IsAppTuple::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function &&F = [](void) { return T(); }) noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// The new *sensor* handles master-input by \p MF. /// /// \tparam MT type of master-input the new *sensor* handles /// \tparam T type of data the new *sensor* operates on /// /// \note The type arguments \p MT and \p T must be either all built-in types /// or all instances of \c rosa::app::AppTuple. Moreover, \p T cannot be /// \c rosa::app::EmptyAppTuple. Instantiation fails if these conditions /// do not hold. /// /// \param Name name of the new *sensor* /// \param MF function for the new *sensors* to process master-input /// values with \param F function for the new *sensor* to generate /// the next value with during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::app::Application::registerSensorValues is used to /// register an alternative simulation data source with \c /// rosa::app::AppSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \see \c rosa::app::AppSensor::AppSensor. /// /// \return \c rosa::AgentHandle for the new *sensor* template , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value)>> AgentHandle createSensor( const std::string &Name, std::function)> &&MF, std::function &&F = [](void) { return T(); }) noexcept; private: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p /// Ts..., and \p As... is not an instance of \c rosa::app::AppTuple or /// any of \p T and \p As... is \c rosa::app::EmptyAppTuple. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with \param F function for the new *agent* to process /// input values and generate output with /// /// \see \c rosa::app::AppAgent::AppAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template >::Value && !std::is_same::value && (true && ... && (!std::is_same::value))>> AgentHandle createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; public: /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* neither receives master-input nor produces /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p T and \p As... must be either all built-in /// types or all instances of \c rosa::app::AppTuple. Moreover, none of /// them can be \c rosa::app::EmptyAppTuple. Instantiation fails if /// these conditions do not hold. /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::app::AppAgent::AppAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF but does not /// produce master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p MT, \p T, and \p As... must be either all /// built-in types or all instances of \c rosa::app::AppTuple. Moreover, /// none of \p T and \p As... can be \c rosa::app::EmptyAppTuple. /// Instantiation fails if these conditions do not hold. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \see \c rosa::app::AppAgent::AppAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* does not receive master-input but produces /// master-output. /// /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p T, \p Ts, and \p As... must be either all /// built-in types or all instances of \c rosa::app::AppTuple. Moreover, /// none of \p T and \p As... can be \c rosa::app::EmptyAppTuple. /// Instantiation fails if these conditions do not hold. /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \note \p F does not produce master-output for a given position if the /// corresponding type is \c rosa::app::EmptyAppTuple. It is not /// possible to disable master-output at any position by using built-in types. /// /// \see \c rosa::app::AppAgent::AppAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template < typename T, typename... Ts, typename... As, typename = std::enable_if_t< TypeListSubsetOf, BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// The new *agent* receives master-input by \p MF and produces /// master-output. /// /// \tparam MT type of master-input the new *agent* handles /// \tparam T type of data the new *agent* outputs /// \tparam Ts types of master-output the new *agent* produces /// \tparam As types of inputs the new *agent* takes /// /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either /// all built-in types or all instances of \c rosa::app::AppTuple. /// Moreover, none of \p T and \p As... can be \c /// rosa::app::EmptyAppTuple. Instantiation fails if these conditions /// do not hold. /// /// \param Name name of the new *agent* /// \param MF function for the new *agent* to process master-input /// values with /// \param F function for the new *agent* to process input values and /// generate output with /// /// \note \p F does not produce master-output for a given position if the /// corresponding type is \c rosa::app::EmptyAppTuple. It is not /// possible to disable master-output at any position by using built-in types. /// /// \see \c rosa::app::AppAgent::AppAgent. /// /// \return \c rosa::AgentHandle for the new *agent* template , BuiltinTypes>::Value || (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value)))>> AgentHandle createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept; /// Returns the current execution policy of the referred \p Unit /// /// \see \c rosa::app::AppExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \note The returned reference is valid only as long as \c /// rosa::app::Application::setExecutionPolicy() is not called with the /// *unit* referred by \p Unit and the *unit* is not destroyed. /// /// \param Unit the *unit* whose execution policy is to be obtained /// /// \return the \c rosa::app::AppExecutionPolicy from \p Unit if \p Unit /// is valid Optional getExecutionPolicy(AgentHandle Unit) const noexcept; /// Sets the current execution policy of the referred \p Unit to \p /// ExecutionPolicy. /// /// \see \c rosa::app::AppExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \param Unit the *unit* whose execution policy is to be set /// \param ExecutionPolicy the new execution policy for \p Unit /// /// \return how successful setting \p ExecutionPolicy for \p Unit was /// /// \note The function may return the following /// \c rosa::app::Application::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotUnit` | Referred \p Unit is not valid /// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit ErrorCode setExecutionPolicy( AgentHandle Unit, std::unique_ptr &&ExecutionPolicy) noexcept; /// Connects a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// /// \return how successfull connecting \p Sensor to \p Agent at slot /// index \p Pos was /// /// \note The function may return the following /// \c rosa::app::Application::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::app::AppAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::app::AppSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is /// other thanthe output type of \p Sensor or expected master-input of \p /// Sensor is other than master-output at position \p Pos of \p Agent if any /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* /// registered `AlreadyHasMaster` | \p Sensor already has a *master* /// registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// /// \return how succesfull connecting \p Slave to \p Master at slot /// index \p Pos was /// /// \note The function may return the following /// \c rosa::app::Application::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c /// rosa::app::AppAgent `WrongPosition` | \p Pos is not a valid input /// position of \p Master `TypeMismatch` | Expected input type at position /// \p Pos of \p Master is other than the output type of \p Slave or expected /// master-input of \p Slave is other than master-output at position \p Pos of /// \p Master if any `AlreadyHasSlave` | \p Master at position \p Pos already /// has a *slave* registered `AlreadyHasMaster` | \p Slave already has a /// *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; /// Initializes \c this object and others managed by \p this object /// for setting up and performing simulation. /// /// \see \c rosa::app::Application::registerSensorValues, /// \c rosa::app::Application::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; public: /// Registers a stream providing values for a *sensor* during /// simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor /// \tparam T type that can be matched to values \p Sensor is operating on, /// always use default! /// /// \note Instantiation fails if type argument \p T is neither a built-in type /// nor a tuple (i.e., can be converted to \c rosa::app::AppTuple). /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values /// \param Default value to be used when input stream is depleted /// during simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::app::Application::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `TypeMismatch` | \p T does not match the type of values /// generated by \p Sensor /// `NotSensor` | Referred \p Sensor is not \c /// rosa::app::AppSensor /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set template ::Value || IsTuple::Value>> ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// /// The function performs \p NumCycles cycle of simulation. In each /// cycle, all the *agents* and *sensors* registered in \c /// rosa::app::Application::AppUnits are trigged for /// execution. /// /// \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::app::Application, consider it private. namespace { /// Maps any type \p T to \c rosa::app::EmptyAppTuple. template struct MapToEmptyAppTuple { using Type = EmptyAppTuple; }; /// Convenience template alias for \c MapToEmptyAppTuple. template using empty_app_t = typename MapToEmptyAppTuple::Type; /// Converts a \c std::tuple of \c rosa::Optional built-in types into a /// corresponding \c std::tuple of \c rosa::Optional with each actual value /// wrapped in \c rosa::app::AppTuple. /// /// \tparam Ts types of the values /// \tparam S0 indices for accessing values in \p Values /// /// \param Values the \c std::tuple of \c rosa::Optional with built-in values /// /// \note The second argument provides indices statically as template arguments /// \p S0..., so its actual value is ignored. /// /// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values /// with each actual value wrapped in \c rosa::app::AppTuple /// /// \pre Statically, all type arguments \p Ts... are built-in types and the /// provided indices \p S0... match the length of \p Ts...: \code /// TypeListSubsetOf, BuiltinTypes>::Value && /// sizeof...(Ts) == sizeof...(S0) /// \endcode template std::tuple>...> wrapBuiltinInAppTuple(const std::tuple...> &Values, Seq) noexcept { STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), "not built-in types"); STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); return std::make_tuple( std::get(Values) ? Optional>(make_app_tuple(*std::get(Values))) : Optional>()...); } } // End namespace template AgentHandle Application::createSensorImpl(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); AppUnits.emplace(H); return H; } template AgentHandle Application::createSensor(const std::string &Name, std::function &&F) noexcept { auto EmptyMF = std::function)>( [](std::pair) {}); if constexpr (TypeListContains::Value) { using OutputType = AppTuple; return createSensorImpl( Name, std::move(EmptyMF), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (IsAppTuple::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); } else { ASSERT(false && "Unexpected type argument"); } } template AgentHandle Application::createSensor(const std::string &Name, std::function)> &&MF, std::function &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = AppTuple; using OutputType = AppTuple; return createSensorImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); }), std::function( [F{std::move(F)}](void) { return OutputType(F()); })); } else if constexpr (TypeListAllAppTuple>::Value && !std::is_same::value) { return createSensorImpl(Name, std::move(MF), std::move(F)); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgentImpl( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); AppUnits.emplace(H); return H; } template AgentHandle Application::createAgent( const std::string &Name, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; auto EmptyMF = std::function)>( [](std::pair) { return NoMasterOutputType(); }); if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using OutputType = AppTuple; return createAgentImpl( Name, std::move(EmptyMF), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInAppTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::move(EmptyMF), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgent( const std::string &Name, std::function)> &&MF, std::function(std::pair...)> &&F) noexcept { using NoMasterOutputType = std::tuple>...>; if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = AppTuple; using OutputType = AppTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF({std::get<0>(Arg.first), Arg.second}); return NoMasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return std::tuple_cat( wrapBuiltinInAppTuple(std::tuple(Result), seq_t<1>()), NoMasterOutputType()); })); } else if constexpr (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { MF(Arg); return NoMasterOutputType(); }), std::function, Optional>...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Result = F(Args...); return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgent(const std::string &Name, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterOutputType = std::tuple>...>; using OutputType = AppTuple; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInAppTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllAppTuple>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [](std::pair) { return MasterOutputType(); }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template AgentHandle Application::createAgent( const std::string &Name, std::function...>(std::pair)> &&MF, std::function, Optional...>( std::pair...)> &&F) noexcept { if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { using MasterInputType = AppTuple; using MasterOutputType = std::tuple>...>; using OutputType = AppTuple; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Result = MF({std::get<0>(Arg.first), Arg.second}); return wrapBuiltinInAppTuple(Result, seq_t()); }), std::function< std::tuple, Optional>...>( std::pair, bool>...)>( [F{std::move(F)}](std::pair, bool>... Args) { const auto Result = F({std::get<0>(Args.first), Args.second}...); return wrapBuiltinInAppTuple(Result, seq_t<1 + sizeof...(Ts)>()); })); } else if constexpr (TypeListAllAppTuple< TypeList>::Value && !std::is_same::value && (true && ... && (!std::is_same::value))) { using MasterOutputType = std::tuple...>; return createAgentImpl( Name, std::function)>( [MF{std::move(MF)}](std::pair Arg) { const auto Output = MF(Arg); return Output; }), std::function, Optional...>( std::pair...)>( [F{std::move(F)}](std::pair... Args) { const auto Output = F(Args...); return Output; })); } else { ASSERT(false && "Unexpected type arguments"); } } template Application::ErrorCode Application::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); // Make sure preconditions are met. if (!System->isAppSensor(Sensor)) { APPRETERROR(ErrorCode::NotSensor); } auto S = System->getAppSensor(Sensor); ASSERT(S); // Sanity check. if (S->simulationDataSourceIsSet()) { APPRETERROR(ErrorCode::AlreadyHasValueStream); } if constexpr (TypeListContains::Value) { if (S->OutputType != TypeToken::Value) { APPRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource(std::function(void)>([= ](void) mutable noexcept->AppTuple { if (Start != End) { LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << *Start << '\n'; + << "': " << PRINTABLE(*Start) << '\n'; return make_app_tuple(*Start++); } else { LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Default << '\n'; + << S->FullName << "': " << PRINTABLE(Default) << '\n'; return make_app_tuple(Default); } })); } else if constexpr (IsTuple::Value) { using TT = matching_app_tuple_t; if (std::is_same::value || S->OutputType != TT::TT) { APPRETERROR(ErrorCode::TypeMismatch); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource( std::function([=](void) mutable noexcept->TT { if (Start != End) { const TT AV(*Start++); LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << AV << '\n'; + << "': " << PRINTABLE(AV) << '\n'; return AV; } else { static const TT AD(Default); LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << AD << '\n'; + << S->FullName << "': " << PRINTABLE(AD) << '\n'; return AD; } })); } else { ASSERT(false && "Unexpected type argument"); } return ErrorCode::NoError; } } // End namespace app } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_APP_APPLICATION_CPP #undef APPRETERROR #endif #endif // ROSA_APP_APPLICATION_HPP