diff --git a/apps/ews/CMakeLists.txt b/apps/ews/CMakeLists.txt new file mode 100644 index 0000000..1880726 --- /dev/null +++ b/apps/ews/CMakeLists.txt @@ -0,0 +1,4 @@ +ROSA_add_app(ews ews.cpp) +ROSA_add_library_dependencies(ews ROSAConfig) +ROSA_add_library_dependencies(ews ROSAApp) +ROSA_add_library_dependencies(ews ROSAAgent) diff --git a/apps/sa-ews0/sa-ews0.cpp b/apps/ews/ews.cpp similarity index 97% rename from apps/sa-ews0/sa-ews0.cpp rename to apps/ews/ews.cpp index e73a7dc..4214a04 100644 --- a/apps/sa-ews0/sa-ews0.cpp +++ b/apps/ews/ews.cpp @@ -1,388 +1,388 @@ -//===-- apps/sa-ews0/sa-ews0.cpp --------------------------------*- C++ -*-===// +//===-- apps/ews/ews.cpp ----------------------------------------*- C++ -*-===// // -// The RoSA Framework -- Application SA-EWS0 +// The RoSA Framework -- Application EWS // // 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 +/// \file apps/ews/ews.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2020 /// -/// \brief The application SA-EWS0 implements the conventional Early Warning +/// \brief The application EWS 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"; +const std::string AppName = "EWS"; /// 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: " << 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: " << 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-ews0/CMakeLists.txt b/apps/sa-ews0/CMakeLists.txt deleted file mode 100644 index 8d01082..0000000 --- a/apps/sa-ews0/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -ROSA_add_app(sa-ews0 sa-ews0.cpp) -ROSA_add_library_dependencies(sa-ews0 ROSAConfig) -ROSA_add_library_dependencies(sa-ews0 ROSAApp) -ROSA_add_library_dependencies(sa-ews0 ROSAAgent) diff --git a/docs/CommandGuide/sa-ews0.rst b/docs/CommandGuide/ews.rst similarity index 77% rename from docs/CommandGuide/sa-ews0.rst rename to docs/CommandGuide/ews.rst index c2d4d81..c0cc619 100644 --- a/docs/CommandGuide/sa-ews0.rst +++ b/docs/CommandGuide/ews.rst @@ -1,10 +1,10 @@ -SA-EWS0 - Conventional Early Warning Score (EWS) System -======================================================= +EWS - Conventional Early Warning Score (EWS) System +=================================================== The application implements a conventional EWS system. The application takes its input from a multi-column CSV file and generates its output to a multi-column CSV file. Input and output files are to be defined as command line arguments. A full list of available arguments is printed to the `INFO` stream when `--help` is used. Sample data can be used from the SA-EWS1 and SA-EWS2 applications. diff --git a/docs/CommandGuide/index.rst b/docs/CommandGuide/index.rst index 3cfe91b..cfbed9d 100755 --- a/docs/CommandGuide/index.rst +++ b/docs/CommandGuide/index.rst @@ -1,21 +1,21 @@ =========================== RoSA Applications and Tools =========================== Overview ======== Below is documentation for applications and tools .. toctree:: :caption: Applications :maxdepth: 1 - sa-ews0 + ews sa-ews1 sa-ews2 ccam .. toctree:: :caption: Tools :maxdepth: 1