Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F10547861
sa-ews0.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Size
13 KB
Referenced Files
None
Subscribers
None
sa-ews0.cpp
View Options
//===-- 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
<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
=
"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
<
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
Sat, May 30, 11:47 PM (22 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
328440
Default Alt Text
sa-ews0.cpp (13 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment