Health Monitor Project

A health monitor system monitors the devices of each (candidate) patient in a hospital, extracts and stores information regarding one or more patients from sensor devices. This information is used for controlling some actuators that help improve the health of the patient or alert the personnel responsible in case of some events. Each device (sensor or actuator) communicates with the monitor system via TCP sockets, the communication protocol (format of communication data) and possible communication scenarios are given below in section Device Communication.

The purpose of this project is to design the core of this architecture: a monitor that accepts input from devices, processes it, and then possibly sends commands to some actuator devices. The image below depicts the communication flow between the devices and the Health Monitor application.

Health Monitor

Figure 1: The communication network topology of such a system. Each device can send or receive data from the system.

The complexity of creating such an architecture from scratch exceeds what we can do as a part of this course. So, instead of using the topology above where multiple devices communicate with the system, the image below presents a simplified approach.

Health Monitor Simulation

Figure 2: The simulator and the Health Monitor communication topology. There is one stream for input to the Health monitor and one stream for output. All sensor measurements use the Health Monitor input stream and all the actuator commands use the Health Monitor output stream.

The simulator has been already been implemented and can be found in this link. Information on how to use it is provided both in Device Communication below and in the repository itself.

Rather than having a separate link/stream between each device and the monitor, the simulator only uses two streams (input and output): it sends all information (measurements) from potentially multiple simulated devices via the monitor input stream, and reads commands to simulated actuators via the output stream.

In order to distinguish between multiple simulated devices, every message sent and received must contain a unique id of some device. The monitor, for example, can use this id to call different handler functions for messages from different sensors.

Main Focus

The main focus of the project should be to design and implement an application that parses incoming messages messages from sensors and potentially stores them. Also, it has rules (logic) of your design which specify what commands should be sent to which actuators and when. As such, you should specify in the requirements what are the sensor devices that the Health Monitor system reads data from, what are the actuators, what commands do the actuators accept, and the logic of trigerring these commands. Then you should design how the system will actually handle the input and use the the logic to produce the required outputs.

For example:

If a skin thermometer sensor is over 40 degrees Celsius threshold for 5 minutes and the nurse alarm for this patient has not been on since the thermometer passed this threshold, then set nurse alarm on for this patient.

Device communication

The devices communicate with the monitor via 2 TCP ports. One port handles the incoming messages from the sensors, and the other is used to send commands to the actuators. You have to decide on the port numbers by yourself. The messages have a certain format as can be seen below.

Sensor messages have the general format of:

{
  "deviceId": number,
  "deviceDescription": The sensor device's description string,
  "data": the measurement of the sensor
}

Actuator messages have the format:

{
  "deviceId": number,
  "state": number
}

All messages are JSON objects.

Both sensors and actuators are simulated by the same tool. The simulated sensors and actuators are configured via a JSON file. Configuration options include the type of measurements, their range, frequency, etc.

The sensors can be thought of as TCP clients and the actuators as TCP servers. The ports they use can be also configured in the same JSON configuration file. For more detailed information, refer to the simulator tool repo.

From the C++ side there also a library which can be useful for handling the communication with the simulator from the HealthMonitor. It is a renewal of an educational version of the Berkeley C socket API for TCP and UDP. It can be found in this repository. It can be easily used for this project. Details and descriptions can be found in the link. Also there is an examples folder which has various examples of how to use this library to handle TCP communication in different ways.

The simulator

Output of each device

Below there are example configurations for possible sensor device outputs and example messages coming from the simulator.

  1. Heart rate: One integer number varying each 5 seconds. Configuration:
    {
      "deviceId": 0,
      "deviceDescription": "HeartRate",
      "outputType": "int",
      "range": [0, 400],
      "timeInterval": 5
    }
    
    Message:
    { "deviceId": 1, "deviceDescription": "HeartRate", "data": 72 }
    
  2. Blood Systolic, Diastolic Pressure: Two integer number varying each 5 seconds.

Configurations:

[
  {
    "deviceId": 0,
    "deviceDescription": "BloodSystolic",
    "outputType": "int",
    "range": [0, 200],
    "timeInterval": 5
  },
  {
    "deviceId": 1,
    "deviceDescription": "BloodDiastolic",
    "outputType": "int",
    "range": [0, 200],
    "timeInterval": 5
  }
]

Messages:

   {'deviceId': 0, 'deviceDescription': 'BloodSystolic', 'data': 72}
   {'deviceId': 1, 'deviceDescription': 'BloodDiastolic', 'data': 102}
  1. Temperature(Celsius): An integer number changing every minute.

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "Thermometer",
  "outputType": "integer",
  "range": [0, 100],
  "timeInterval": 60
}

Messages:

{ "deviceId": 0, "deviceDescription": "Thermometer", "data": 24 }
  1. Spirometer flow rate: A floating number between 0.0 and 100.0 every 30 seconds.

Configuration:

{
  "deviceId": 0,
  "deviceDescription": "Spirometer",
  "outputType": "float",
  "range": [0, 100],
  "timeInterval": 30
}

Messages:

{ "deviceId": 0, "deviceDescription": "Spirometer", "data": 52.342 }
  1. Skin humidity at 20 different points: 20 floating numbers in the range of 0-100, changing every minute. Configurations:

    [
    {"deviceId": 0,
     "deviceDescription": "SkinThermometer",
     "outputType": "float",
     "range": [0,100],
     "timeInterval": 60},
    {"deviceId": 1,
     "deviceDescription": "SkinThermometer",
     "outputType": "float",
     "range": [0,100],
     "timeInterval": 60},
     ... repeat for another 18 times (and 18 different ids)
     ]
    

    Messages:

    {'deviceId': 0, 'deviceDescription': 'SkinThermometer', 'data': 22.4142}
    {'deviceId': 1, 'deviceDescription': 'SkinThermometer', 'data': 22.402}
    {'deviceId': 15, 'deviceDescription': 'SkinThermometer', 'data': 22.3112}
    {'deviceId': 2, 'deviceDescription': 'SkinThermometer', 'data': 22.40}
    ...
    

    Note that the messages are not send always in order even if the timing is the same message from sensor with id 15 comes before message from sensor with id 2.

  2. Room temperature(Celcius): An integer number changing every two minutes.

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "Thermometer",
  "outputType": "int",
  "range": [0, 100],
  "timeInterval": 120
}

Messages:

{ "deviceId": 0, "deviceDescription": "Thermometer", "data": 25 }
  1. Room humidity: A floating number in the range of 0-100, changing every two minutes.

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "Humidity",
  "outputType": "float",
  "range": [0.0, 100.0],
  "timeInterval": 120
}

Messages:

{ "deviceId": 0, "deviceDescription": "Humidity", "data": 70.0023 }
  1. Sleeping state: Boolean value if patient is sleeping changing every one minute. This can be thought as an integer in the range [0,1].

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "Sleeping",
  "outputType": "int",
  "range": [0, 1],
  "timeInterval": 60
}

Messages:

{ "deviceId": 0, "deviceDescription": "Sleeping", "data": 0 }
  1. Substance of X in blood: float value in the range 0-1 changing every 10 minutes. X can be any mineral out of {calcium, iron, magnesium, potassium, zinc}.

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "magnesiumSubstance",
  "outputType": "float",
  "range": [0.0, 1.0],
  "timeInterval": 600
}

Messages:

{ "deviceId": 0, "deviceDescription": "magnesiumSubstance", "data": 0.00003 }
  1. Level of substance Y artificially provided: float value in the range 0-1 changing every 30 seconds.

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "Ysubstance",
  "outputType": "float",
  "range": [0.0, 1.0],
  "timeInterval": 30
}

Messages:

{ "deviceId": 0, "deviceDescription": "Ysubstance", "data": 0.002 }
  1. Patient image frames from a camera per 30 seconds.

Configurations:

{
  "deviceId": 0,
  "deviceDescription": "Camera",
  "outputType": "image",
  "range": [324, 224],
  "timeInterval": 30
}

Messages:

  {'deviceId': 0, 'deviceDescription': 'Camera', 'data': [[[8,23,254]...],...]}

The messages data holds an array of size 224 x 324 x 3 with integer numbers in the range of [0,255].

  1. Muscle electrical tension out of 10 muscles updated every 20 seconds.

Configurations:

[
 {"deviceId": 0,
  "deviceDescription": "mVTension",
  "outputType": "float",
  "range": [0.0, 20.0],
  "timeInterval": 20},
 {"deviceId": 1,
  "deviceDescription": "mVTension",
  "outputType": "float",
  "range": [0.0, 20.0],
  "timeInterval": 20},
  ... 8 more such devices are defined
]

Messages:

  {'deviceId': 9, 'deviceDescription': 'mVTension', 'data': 10.07}
  {'deviceId': 0, 'deviceDescription': 'mVTension', 'data': 10.07}
  {'deviceId': 8, 'deviceDescription': 'mVTension', 'data': 10.07}
  ...
  1. Feel free to provide any additional outputs, but you have to change the generation tool.

Configuration:

{"deviceId": "unique device id number",
 "deviceDescription": "A description string of the sensor device",
 "outputType": "float | int | image",
 "range": ["low","high"],
 "timeInterval": "number of seconds between transmissions",
 "scenario": [{"time": "time between transmission of
                        the previous state and this state",
               "value": "The deterministic value of simulated measurement"},
              ...
              ]
}

Possible outputs of the backend

Below there are example configurations for possible actuator device inputs and example messages coming from the HealthMonitor. Messages should end with a newline (Unix) character, spaces do not matter.

  1. Send text to hospital system to call an ambulance.

Configuration:

{
  "deviceId": 2,
  "deviceDescription": "textAmbulance",
  "inputType": "text",
  "range": []
}

Message:

{ "deviceId": 2, "state": "Come now to the health center!!!" }
  1. Invoke nurse alarm, to help the patient.

Configuration:

{
  "deviceId": 2,
  "deviceDescription": "nurseAlarm",
  "inputType": "text",
  "range": []
}

Message:

{ "deviceId": 2, "state": "Nurse needed at room 7!!!" }
  1. Increase or decrease substance Y level inside a range 0-1.

Configuration:

{
  "deviceId": 2,
  "deviceDescription": "substanceYlevel",
  "inputType": "float",
  "range": [0, 1]
}

Message:

{ "deviceId": 2, "state": 0.7 }
  1. Invoke doctor alarm, to see the patient (Something is not going well).

Configuration:

{
  "deviceId": 2,
  "deviceDescription": "doctorAlarm",
  "inputType": "int",
  "range": [0, 1]
}

Message:

{ "deviceId": 2, "state": 1 }

If message

{ "deviceId": 2, "state": 3 }

is sent then the simulator will not change the actuator's state and will return back to the HealthMonitor an error message in JSON format:

{'deviceId': 2, 'deviceDescription': 'doctorAlarm', 'data': the current state, 'error': true}
  1. Statistics report for the doctor.

Configuration:

{
  "deviceId": 3,
  "deviceDescription": "doctorStatistics",
  "inputType": "text",
  "range": []
}

Message:

{ "deviceId": 3, "state": "stats:Healthy" }
  1. Feel free to provide any additional outputs.

Configuration:

{
  "deviceId": "unique device id number",
  "deviceDescription": "Device functionality description",
  "inputType": "float|int",
  "range": ["low", "high"]
}

How to read/output JSONs

One common library to use for parsing and outputting json objects/files is the jsonxx. This is a recommended library of course, but not the only one you could use.

Deliverable 1 - Software Requirements Specification (SRS)

In this document ~12 functional and ~5 non-functional software requirements will be defined and described. Also 4 scenarios have to be described inside this document using the template form of the lectures. The most requirements come from the fact that your logic (sensor messages processing) is static and not abstract leading to easier functional requirements and less non-functional requirements. More information about the first deliverable in general, no matter the project, and the feedback can be found in this document. The template which will be used is this document or its corresponding doc which is more compatible.

Deliverable 2 - Software Design Specification (SDS)

The architecture you will use it is your choice, the (MVC) Model-View-Controller pattern may not necessary be applicable for this project. For this document you will design the architecture of your liking that will satisfy the requirements of the first deliverable. This application is event driven application, meaning that the most code is actually executed whenever a sensors message reaches HealthMonitor. So, for your first design draft it is recommended to think in terms of information flow. Another recommendation is to try not making the logic of the application too abstract. The logic of dealing with specific sensors and actuators is hardwired into your application.

More information about the second deliverable in general, no matter the project, and the feedback can be found in this document. The template which will be used is this document.

Deliverable 3 - Implementation

For this phase you will implement the design that the other team has prepared for you.

A good way to start your implementation is to look at what the Python tool is doing to output data. Also the examples in the practical CPP sockets will help to deal with the communication. However, avoid use threads if you are not already comfortable with the concepts. Download the package, set it up and test it with the simulator before starting to implement anything.

As a guideline, your implementation should contain at most around 800-1000 lines of code. This is not a requirement, but a guideline: if you feel you need more, you probably need to rethink your implementation and follow the DRY (don't repeat yourself) principle.

If your application needs a configuration file, this file can have any format, not only JSON.

The application configuration can be static and baked into the source code, however it can also be dynamic and read from a file. In order to read and parse these files you may find an open-source library and use it as a dependency, but you need to make sure there is no issues with the license and this dependency is mentioned in the README.

Deliverable 4 - Testing report

See here.