Page MenuHomePhorge

ews.cpp
No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None
//===-- apps/ews/ews.cpp ----------------------------------------*- C++ -*-===//
//
// 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/ews/ews.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2020
///
/// \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 <fstream>
#include <numeric>
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 = "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 <typename T>
AgentHandle createLowLevelAgent(std::unique_ptr<Application> &App,
const std::string &Name,
const Abstraction<T, WarningScore> &A) {
using result = Optional<WarningScoreType>;
using handler = std::function<result(std::pair<T, bool>)>;
return App->createAgent(
Name, handler([&, Name](std::pair<T, bool> I) -> result {
LOG_INFO_STREAM << "\n******\n"
<< Name << " " << (I.second ? "<New>" : "<Old>")
<< " 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<Application> 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<HRType>("HR Sensor");
AgentHandle BRSensor = App->createSensor<BRType>("BR Sensor");
AgentHandle SpO2Sensor = App->createSensor<SpO2Type>("SpO2 Sensor");
AgentHandle BPSysSensor = App->createSensor<BPSysType>("BPSys Sensor");
AgentHandle BodyTempSensor =
App->createSensor<BodyTempType>("BodyTemp Sensor");
//
// Create functionalities.
//
LOG_INFO("Creating Functionalities for Agents.");
//
// Define abstractions.
//
RangeAbstraction<HRType, WarningScore> HRAbstraction(
{{{0, 40}, Emergency},
{{40, 51}, High},
{{51, 60}, Low},
{{60, 100}, No},
{{100, 110}, Low},
{{110, 129}, High},
{{129, 200}, Emergency}},
Emergency);
RangeAbstraction<BRType, WarningScore> BRAbstraction({{{0, 9}, High},
{{9, 14}, No},
{{14, 20}, Low},
{{20, 29}, High},
{{29, 50}, Emergency}},
Emergency);
RangeAbstraction<SpO2Type, WarningScore> SpO2Abstraction(
{{{1, 85}, Emergency},
{{85, 90}, High},
{{90, 95}, Low},
{{95, 100}, No}},
Emergency);
RangeAbstraction<BPSysType, WarningScore> BPSysAbstraction(
{{{0, 70}, Emergency},
{{70, 81}, High},
{{81, 101}, Low},
{{101, 149}, No},
{{149, 169}, Low},
{{169, 179}, High},
{{179, 200}, Emergency}},
Emergency);
RangeAbstraction<BodyTempType, WarningScore> 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<Optional<WarningScoreType>(
std::pair<WarningScoreType, bool>, std::pair<WarningScoreType, bool>,
std::pair<WarningScoreType, bool>, std::pair<WarningScoreType, bool>,
std::pair<WarningScoreType, bool>)>(
[](std::pair<WarningScoreType, bool> HR,
std::pair<WarningScoreType, bool> BR,
std::pair<WarningScoreType, bool> SpO2,
std::pair<WarningScoreType, bool> BPSys,
std::pair<WarningScoreType, bool> BodyTemp)
-> Optional<WarningScoreType> {
LOG_INFO_STREAM
<< "\n*******\nBody Agent trigged with values:\n"
<< (HR.second ? "<New>" : "<Old>")
<< " HR warning score: " << PRINTABLE(HR.first) << "\n"
<< (BR.second ? "<New>" : "<Old>")
<< " BR warning score: " << PRINTABLE(BR.first) << "\n"
<< (SpO2.second ? "<New>" : "<Old>")
<< " SpO2 warning score: " << PRINTABLE(SpO2.first) << "\n"
<< (BPSys.second ? "<New>" : "<Old>")
<< " BPSys warning score: " << PRINTABLE(BPSys.first) << "\n"
<< (BodyTemp.second ? "<New>" : "<Old>")
<< " BodyTemp warning score: " << PRINTABLE(BodyTemp.first)
<< "\n******\n";
const std::array<WarningScoreType, 5> 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<WarningScoreType> 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<Optional<unit_t>(std::pair<WarningScoreType, bool>)>(
[&ScoreWriter](
std::pair<WarningScoreType, bool> Score) -> Optional<unit_t> {
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<HRType, BRType, SpO2Type, BPSysType, BodyTempType>;
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;
}

File Metadata

Mime Type
text/x-c++
Expires
Sun, Apr 12, 11:28 AM (2 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
306544
Default Alt Text
ews.cpp (13 KB)

Event Timeline