Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F5298084
ews.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Size
13 KB
Referenced Files
None
Subscribers
None
ews.cpp
View Options
//===-- 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
*******
\n
Body 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
Details
Attached
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)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment