diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp new file mode 100644 index 0000000..6ca7342 --- /dev/null +++ b/examples/CSVFiles/main.cpp @@ -0,0 +1,328 @@ +//===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// +// +// The RoSA Framework -- Application SA-EWS1 +// +//===----------------------------------------------------------------------===// +/// +/// \file apps/sa-ews1/sa-ews1.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2017-2019 +/// +/// \brief The application SA-EWS1 implements the case study from the paper: +/// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and +/// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence +/// DOI: 10.1007/978-3-319-58877-3_12 +//===----------------------------------------------------------------------===// + +#include "rosa/agent/Abstraction.hpp" +#include "rosa/agent/Confidence.hpp" + +#include "rosa/config/version.h" + +#include "rosa/deluxe/DeluxeContext.hpp" + +#include "rosa/support/csv/CSVReader.hpp" +#include "rosa/support/csv/CSVWriter.hpp" + +#include + +using namespace rosa; +using namespace rosa::agent; +using namespace rosa::deluxe; +using namespace rosa::terminal; + +const std::string AppName = "Test-CSVFiles"; + +/// Paths for the CSV files for simulation. +/// +///@{ +//const std::string HRCSVPath = "HR.csv"; +const std::string HRCSVPathOld = "HR-New.csv"; +const std::string HRCSVPath = "HR-New-Semicolon.csv"; +const std::string BRCSVPath = "BR.csv"; +const std::string SpO2CSVPath = "SpO2.csv"; +const std::string BPSysCSVPath = "BPSys.csv"; +const std::string BodyTempCSVPath = "BodyTemp.csv"; +const std::string ScoreCSVPath = "Score.csv"; +///@} + +/// How many cycles of simulation to perform. +const size_t NumberOfSimulationCycles = 16; + +/// Warning levels for abstraction. +enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 }; + +/// Helper function creating a deluxe agent for pre-processing sensory values. +/// +/// Received values are first validated for confidence. Values which the +/// validator does not mark confident are ignored. Confident values are +/// abstracted into a \c WarningScore value, which is the result of the +/// processing function. +/// +/// \note The result, \c WarningScore, is returned as \c uint32_t because +/// enumeration types are not integrated into built-in types. Hence, a master +/// to these agents receives its input as \c uint32_t values, and may cast them +/// to \c WarningScore explicitly. +/// +/// \tparam T type of values to receive from the sensor +/// +/// \param C the deluxe context to create the agent in +/// \param Name name of the new agent +/// \param CC confidence validator to use +/// \param A abstraction to use +/// +/// \return handle for the new agent +template +AgentHandle createLowLevelAgent(std::unique_ptr &C, + const std::string &Name, + const Confidence &CC, + const Abstraction &A) { + using handler = DeluxeAgent::D; + using result = Optional; + return C->createAgent( + Name, handler([&, Name](std::pair I) -> result { + LOG_INFO_STREAM << "\n******\n" + << Name << " " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return (I.second && CC(I.first)) ? result(A(I.first)) : result(); + })); +} + +int main(void) { + LOG_INFO_STREAM + << '\n' + << library_string() << " -- " << Color::Red << AppName << "app" + << Color::Default << '\n' + << Color::Yellow + << "CSV files are read from and written to the current working directory." + << Color::Default << '\n'; + + std::unique_ptr C = DeluxeContext::create(AppName); + + // + // Create deluxe sensors. + // + LOG_INFO("Creating sensors."); + + // All sensors are created without defining a normal generator function, but + // with the default value of the second argument. That, however, requires the + // data type to be explicitly defined. This is good for simulation only. + AgentHandle HRSensor = C->createSensor("HR Sensor"); + AgentHandle BRSensor = C->createSensor("BR Sensor"); + AgentHandle SpO2Sensor = C->createSensor("SpO2 Sensor"); + AgentHandle BPSysSensor = C->createSensor("BPSys Sensor"); + AgentHandle BodyTempSensor = C->createSensor("BodyTemp Sensor"); + + // + // Create functionalities. + // + LOG_INFO("Creating Functionalities for Agents."); + + // + // Define confidence validators. + // + // Lower bounds are inclusive and upper bounds are exclusive. + + Confidence HRConfidence(0, 501); + Confidence BRConfidence(0, 301); + Confidence SpO2Confidence(0, 101); + Confidence BPSysConfidence(0,501); + Confidence BodyTempConfidence(-60, + nextRepresentableFloatingPoint(50.0f)); + + // + // Define abstractions. + // + + RangeAbstraction HRAbstraction( + {{{0, 40}, Emergency}, + {{40, 51}, High}, + {{51, 60}, Low}, + {{60, 100}, No}, + {{100, 110}, Low}, + {{110, 129}, High}, + {{129, 200}, Emergency}}, + Emergency); + + RangeAbstraction BRAbstraction({{{0, 9}, High}, + {{9, 14}, No}, + {{14, 20}, Low}, + {{20, 29}, High}, + {{29, 50}, Emergency}}, + Emergency); + + RangeAbstraction SpO2Abstraction({{{1, 85}, Emergency}, + {{85, 90}, High}, + {{90, 95}, Low}, + {{95, 100}, No}}, + Emergency); + + RangeAbstraction BPSysAbstraction( + {{{0, 70}, Emergency}, + {{70, 81}, High}, + {{81, 101}, Low}, + {{101, 149}, No}, + {{149, 169}, Low}, + {{169, 179}, High}, + {{179, 200}, Emergency}}, + Emergency); + + RangeAbstraction BodyTempAbstraction( + {{{0.f, 28.f}, Emergency}, + {{28.f, 32.f}, High}, + {{32.f, 35.f}, Low}, + {{35.f, 38.f}, No}, + {{38.f, 39.5f}, High}, + {{39.5f, 100.f}, Emergency}}, + Emergency); + + // + // Create low-level deluxe agents with \c createLowLevelAgent. + // + LOG_INFO("Creating low-level agents."); + + AgentHandle HRAgent = + createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction); + AgentHandle BRAgent = + createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction); + AgentHandle SpO2Agent = + createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction); + AgentHandle BPSysAgent = + createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction); + AgentHandle BodyTempAgent = createLowLevelAgent( + C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction); + + // + // Connect sensors to low-level agents. + // + LOG_INFO("Connect sensors to their corresponding low-level agents."); + + C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); + C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); + C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); + C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); + C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); + + // + // Create a high-level deluxe agent. + // + LOG_INFO("Create high-level agent."); + + // The new agent logs its input values and results in the the sum of them. + AgentHandle BodyAgent = C->createAgent( + "Body Agent", + DeluxeAgent::D( + [](std::pair HR, std::pair BR, + std::pair SpO2, std::pair BPSys, + std::pair BodyTemp) -> Optional { + LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" + << (HR.second ? "" : "") + << " HR warning score: " << HR.first << "\n" + << (BR.second ? "" : "") + << " BR warning score: " << BR.first << "\n" + << (SpO2.second ? "" : "") + << " SpO2 warning score: " << SpO2.first << "\n" + << (BPSys.second ? "" : "") + << " BPSys warning score: " << BPSys.first << "\n" + << (BodyTemp.second ? "" : "") + << " BodyTemp warning score: " << BodyTemp.first + << "\n******\n"; + return {HR.first + BR.first + SpO2.first + BPSys.first + + BodyTemp.first}; + })); + + // + // Connect low-level agents to the high-level agent. + // + LOG_INFO("Connect low-level agents to the high-level agent."); + + C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); + C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); + C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); + C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); + C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel"); + + // + // For simulation output, create a logger agent writing the output of the + // high-level agent into a CSV file. + // + LOG_INFO("Create a logger agent."); + + // Create CSV writer. + std::ofstream ScoreCSV(ScoreCSVPath); + csv::CSVWriter ScoreWriter(ScoreCSV); + + // The agent writes each new input value into a CSV file and produces nothing. + AgentHandle LoggerAgent = C->createAgent( + "Logger Agent", + DeluxeAgent::D( + [&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. + //parseparse + LOG_INFO("Connect the high-level agent to the logger agent."); + + C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); + + // + // Do simulation. + // + LOG_INFO("Setting up and performing simulation."); + + // + // Initialize deluxe context for simulation. + // + + C->initializeSimulation(); + + // + // Open CSV files and register them for their corresponding sensors. + // + + // Type aliases for iterators. + using CSVInt = csv::CSVFlatIterator; + using CSVFloat = csv::CSVFlatIterator; + //std::ifstream HRCSV(HRCSVPath); + + //CSVInt testIt(HRCSV); + + + /* std::ifstream HRCSV(HRCSVPath); + C->registerSensorValues(HRSensor, CSVInt(HRCSV, 5, false, ';', '\n'), CSVInt()); */ + + std::ifstream HRCSVOld(HRCSVPathOld); + C->registerSensorValues(HRSensor, CSVInt(HRCSVOld, 2), CSVInt()); + + /* + std::ifstream BRCSV(HRCSVPath); + C->registerSensorValues(BRSensor, CSVInt(BRCSV, 4, false, ';', '\n'), CSVInt()); */ + + std::ifstream SpO2CSV(HRCSVPath); + C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV, 2, false, ';', '\n'), CSVInt()); + + + /* + std::ifstream BPSysCSV(BPSysCSVPath); + C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt());*/ + + std::ifstream BodyTempCSV(HRCSVPath); + C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV, 2, false, ';', '\n'), CSVFloat()); + + // + // Simulate. + // + + C->simulate(NumberOfSimulationCycles); + + return 0; +}