Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Facultad de Medicina
Telemedicina
Actividad Final
Producto Integrador de Aprendizaje (PIA)
Profesor:
Ing. Manuel Francisco Vicencio
Ing. Oscar E. Cervantes García
Integrantes:
1
Introducción
El presente reporte técnico tiene como objetivo proporcionar una visión
general de la implementación de una plataforma de telemedicina en un ambiente
hospitalario. Dicha iniciativa se desarrolla a partir de la intención de poder brindar
servicios médicos remotos, los cuales permitan a los pacientes acceder a una
atención médica que sea eficiente y conveniente, sea desde la comodidad de sus
hogares o bien, en cualquier ubicación disponible en el horario oportuno.
El siguiente reporte aborda los aspectos clave del proyecto, tales como la
arquitectura de la plataforma, los procesos de integración entre los usuarios, las
funciones principales, los desafíos más probables y consideraciones de seguridad.
Con ello, se espera brindar una comprensión clara y detallada de la plataforma, así
como de los beneficios y mejoras que se esperan.
2
Monitoreo de equipo médico asociado: La plataforma recopila
constantemente datos de temperatura y humedad de la máquina de anestesia
asignada a cada paciente que pasa por proceso de cirugía. Esto proporciona un
monitoreo continuo de las condiciones ambientales en las que opera la máquina
durante el procedimiento quirúrgico. Cualquier desviación de los rangos predefinidos
puede alertar al personal médico y de mantenimiento sobre posibles problemas o
necesidades de ajuste.
Objetivos
El proyecto de implementación de la plataforma de telemedicina tiene como
objetivo principal mejorar el acceso y la calidad de los servicios de atención médica,
brindando a los pacientes la posibilidad de recibir atención médica remota y
oportuna. Los objetivos específicos del proyecto son los siguientes:
3
Arquitectura
Descripción general
Nuestro sistema puede ser visto como un conjunto modular, pues tiene
componentes que han sido diseñados de manera independiente y que realizan una
función específica.
Componentes principales
Plataforma Web:
Incluye una interfaz de usuario con la que interactúan los pacientes y
el personal asociado, como médicos, soporte, recepción u otros departamentos
involucrados. Contiene el acceso al registro médico de pacientes, citas, monitoreo
de variables fisiológicas y de equipo médico, así como resultados de pruebas.
Incluye también la identificación de usuarios, se accede a la base de datos y se
procesa la información de los dispositivos externos IoT.
Base de datos:
Aquí se almacena toda la información relacionada con los pacientes,
citas médicas, resultados de pruebas, registros médicos, monitoreo de dispositivos,
etc. Se usa una base de datos relacional a través de PostgreSQL.
Dispositivos IoT:
4
Se utilizan para recopilar datos de carácter médico, tales como
variables fisiológicas de pacientes y también para monitorear el rendimiento del
equipo médico asociado a éste durante una intervención. Los dispositivos envían los
datos a la plataforma de telemedicina para su procesamiento y visualización.
5
registrados en la plataforma, así como de sus citas, resultados, registros médicos,
etc. Una base de datos permite una gestión eficaz de grandes volúmenes de datos.
El acceso y modificación de la base de datos permiten el registro y la actualización
de la información médica de manera rápida y precisa, lo que facilita la colaboración
entre el equipo técnico y médico que utiliza la plataforma, por ende, provee la
oportunidad de mejor toma de decisiones basadas en datos actualizados
constantemente.
Diagramas
6
siguiendo con la ejemplificación. La información es capturada por el sensor y
procesada por el microcontrolador, indicando la correcta operación de dicho
dispositivo médico en el caso de una intervención con nuestro paciente. Todos estos
datos se envían a la plataforma, la cual valida su recepción.
Funcionalidades Principales
7
Medidas de seguridad implementadas para proteger los datos
Implementación técnica
Detalles sobre la implementación de la plataforma, incluyendo los lenguajes de
programación utilizados, las bibliotecas o frameworks empleados, y cualquier otro
aspecto relevante.
Lenguajes de programación y frameworks utilizados
8
1. numpy: La librería NumPy es fundamental para el cálculo numérico en
Python. Proporciona estructuras de datos eficientes y de alto rendimiento,
como matrices multidimensionales, y funciones para operaciones
matemáticas y manipulación de datos.
2. matplotlib.pyplot: Esta librería se utiliza para la visualización de datos y la
generación de gráficos en Python. Proporciona una amplia gama de
funciones para crear y personalizar gráficos, diagramas y visualizaciones.
3. sklearn.model_selection: Scikit-learn es una librería ampliamente utilizada
para el aprendizaje automático en Python. La sublibrería model_selection
ofrece herramientas para dividir los datos en conjuntos de entrenamiento y
prueba, lo que facilita la evaluación y validación de los modelos.
4. PIL.Image: El módulo Image de la librería Pillow (fork de PIL) se utiliza para
trabajar con imágenes en Python. Proporciona una amplia gama de funciones
para cargar, manipular y guardar imágenes en diversos formatos.
5. tensorflow: TensorFlow es una biblioteca popular y de alto rendimiento para el
aprendizaje automático y la inteligencia artificial. Proporciona un entorno de
programación eficiente para construir y entrenar redes neuronales, así como
herramientas para el despliegue y la inferencia de modelos.
6. tensorflow.keras: Keras es una API de alto nivel que se ejecuta sobre
TensorFlow y facilita la construcción y entrenamiento de modelos de
aprendizaje profundo. Proporciona una interfaz sencilla y elegante para
definir capas, arquitecturas de modelos y funciones de pérdida, y ofrece una
amplia gama de utilidades para el procesamiento de datos.
7. tensorflow.keras.layers: Este módulo de Keras proporciona una variedad de
capas neuronales que se pueden utilizar para construir arquitecturas de redes
neuronales personalizadas. Incluye capas convolucionales, capas de pooling,
capas de normalización y muchas otras capas comunes utilizadas en el
procesamiento de imágenes y otras tareas.
8. tensorflow.keras.models: Este módulo proporciona la clase Model, que se
utiliza para crear y entrenar modelos de aprendizaje profundo en TensorFlow.
Permite definir modelos mediante la composición de capas y proporciona
métodos para compilar, entrenar y evaluar los modelos.
9. tensorflow.keras.losses: Este módulo contiene varias funciones de pérdida
que se utilizan para medir la discrepancia entre las salidas predichas y las
salidas reales durante el entrenamiento de los modelos. Incluye funciones
como SparseCategoricalCrossentropy y CategoricalCrossentropy.
10. tensorflow.keras.optimizers: Este módulo proporciona optimizadores que se
utilizan para ajustar los pesos y los sesgos de los modelos durante el
entrenamiento. Ofrece algoritmos populares como Adam, que se utiliza
comúnmente en el aprendizaje profundo debido a su eficacia y rapidez.
11. tensorflow.keras.callbacks: Este módulo contiene clases que se utilizan como
devoluciones de llamada durante el entrenamiento de los modelos. Las
devoluciones de llamada, como EarlyStopping y ModelCheckpoint, permiten
controlar el progreso del entrenamiento y realizar
9
Implementación de dispositivos IoT
Resultados
Se logró mejorar el acceso y la calidad de los servicios de atención médica,
brindando a los pacientes la posibilidad de recibir atención médica remota y
oportuna, cumpliendo así el objetivo principal del proyecto.
Conclusiones y Recomendaciones
Conclusiones obtenidas del proyecto, lecciones aprendidas y recomendaciones para
futuras mejoras o expansiones.
Anexos
Documentación adicional relevante, como el modelo destilado utilizado, el informe
del entrenamiento del modelo de IA, archivos de código fuente, capturas de pantalla
u otros materiales respaldatorios.
Código IA usado:
https://colab.research.google.com/drive/1bCBAxV0HT8lL4uUgDCiafxRTksujkGFk?u
sp=sharing
10
https://colab.research.google.com/drive/1R5tDBo_bSqGJLjwThWD-tMQfDTmiCh2f?
usp=sharing
11
tabla para que no sobrepase los límites de la página cuando se visualiza en modo
horizontal.
12
Figura xxxxx. Dispositivo IoT implementado. Obsérvese el adaptador con la
memoria microSD externa y el sensor de temperatura y humedad relativa DHT11.
13
Figura xxxxx. Evidencia de lectura en tiempo real con monitor serie de Arduino IDE.
14
Figura xxxxx. Archivo de texto leído desde la memoria microSD con el registro de
temperatura y humedad sensadas, obsérvese la etiqueta de tiempo en milisegundos
desde el inicio del programa de sensado.
15
Figura xxxxx. Página del dispositivo IoT o sensor de temperatura y humedad
visualizada desde el dispositivo móvil iPhone 13 Pro. Aunque se observan todos los
datos, el tamaño de la fuente podría ajustarse de mejor forma.
16
#include <DHT.h>
#include <Ticker.h>
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WiFiClient wifiClient;
File configFile;
#include <WebSocketsServer.h>
WebSocketsServer webSocket = WebSocketsServer(81);
#include "conexiones.h"
#include "respuestasHTML.h"
#define LED 2
#define DHTTYPE DHT11 // Sensor DHT 11
#define DHTPin D8 // PIN AL QUE SE CONECTA EL sensor
#include <EEPROM.h>
#define CONFIG_ADDRESS 0
struct Config {
String nombre;
String ingreso;
String tiempo;
};
17
bool readSensorFlag = false;
#include <SD.h>
Config configuracion;
void guardarConfiguracion();
String SendHTML(float temperatura,float humedad);
void leerSensor();
void tickFunction() {
readSensorFlag = true;
}
void setup() {
Serial.begin(115200);
dht.begin(); // Inicializa la lectura del sensor
myTicker.attach(3, tickFunction);
readSensorFlag = false;
EEPROM.begin(sizeof(configuracion));
EEPROM.get(CONFIG_ADDRESS, configuracion);
EEPROM.end();
WiFiMulti.addAP(ssid_1, password_1);
WiFiMulti.addAP(ssid_2, password_2);
Serial.println("Estableciendo conexion WiFi...");
while (WiFiMulti.run(TiempoEsperaWifi) != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println("");
Serial.println("Conexion exitosa!");
Serial.print("Direccion IP: ");
18
Serial.println(WiFi.localIP());
webSocket.begin();
Serial.println("Servidor HTTP iniciado.");
webSocket.onEvent(webSocketEvent);
server.on("/", []() {
char html[sizeof(index_html) + deviceName.length() + 1];
sprintf(html, index_html, deviceName.c_str());
server.send(200, "text/html", html);
});
server.on("/configuracion", []() {
String personalizacion_html = "";
server.on("/personalizar", []() {
String message = "";
if (server.arg("Nombre") == "") {
message += "\nNo se recibio un nuevo nombre";
} else {
message += "\nNombre recibido = ";
nombre = server.arg("Nombre");
message += server.arg("Nombre");
}
if (server.arg("Ingreso") == "") {
message += "\nNo se recibio fecha de ingreso";
} else {
message += "\nIngreso en = ";
ingreso = server.arg("Ingreso");
message += server.arg("Ingreso");
}
if (server.arg("Tiempo") == "") {
message += "\nNo se recibio tiempo de uso";
} else {
message += "\nTiempo de uso = ";
19
tiempo = server.arg("Tiempo");
message += server.arg("Tiempo");
}
Serial.println(message);
server.begin();
pinMode(LED, OUTPUT);
if (SD.exists("config.txt")) {
Serial.println("Leyendo archivo de configuracion");
configFile = SD.open("config.txt");
if (configFile) {
// Leer la configuración desde el archivo y usarla.
configFile.close();
}
} else {
Serial.println("Creando archivo de configuracion");
configFile = SD.open("config.txt", FILE_WRITE);
if (configFile) {
// Escribir configuración por defecto en el archivo.
configFile.close();
}
}
}
void loop() {
if (WiFi.status() == WL_CONNECTED) {
server.handleClient();
webSocket.loop();
20
String str = String(temperaturaAnterior) + "," + String(temperatura) + "," +
String(humedadAnterior) + "," + String(humedad);
int str_len = str.length() + 1;
char char_array[str_len];
str.toCharArray(char_array, str_len);
webSocket.broadcastTXT(char_array);
previousMillis = now;
}
current_millis = millis();
if (current_millis - ultimo_post > 30000) {
ultimo_post = current_millis;
leerSensor();
HTTPClient http;
http.begin(wifiClient, "http://192.168.1.70:80/temperatura");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpCod = http.POST(texto);
if (httpCod == 200) {
String respuesta = http.getString();
Serial.println("El servidor respondió: ");
Serial.println(respuesta);
} else {
Serial.println("El servidor respondió: ");
Serial.println(httpCod);
}
http.end();
}
if (readSensorFlag) {
leerSensor();
readSensorFlag = false;
}
}
}
21
void leerSensor() {
digitalWrite(LED, !(digitalRead(LED)));
humedadAnterior = humedad; // Almacena el valor anterior de humedad
temperaturaAnterior = temperatura; // Almacena el valor anterior de temperatura
humedad = dht.readHumidity();
temperatura = dht.readTemperature();
if (isnan(humedad) || isnan(temperatura))
{
Serial.println("Error al recibir datos del sensor!");
return;
}
server.send(200, "text/html", SendHTML(temperatura,humedad));
Serial.print("Temperatura medida: ");
Serial.println(temperatura);
Serial.print("Humedad medida: ");
Serial.println(humedad);
texto = "temperatura=" + String(temperatura) + "&humedad=" + String(humedad);
}
void guardarConfiguracion() {
EEPROM.begin(sizeof(configuracion));
EEPROM.put(CONFIG_ADDRESS, configuracion);
EEPROM.commit();
EEPROM.end();
}
22
}
23
const char personalization_1[] PROGMEM = R"=====(
<html>
<head>
<title>Configuracion</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.cs
s" rel="stylesheet"
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
crossorigin="anonymous">
</head>
24
</body>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.m
in.js"
integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe"
crossorigin="anonymous"></script>
</html>
)=====";
Código Python
import logging
from flask import Flask, render_template, request, redirect, url_for
import os
import requests
import json
import datetime
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user,
login_required, logout_user, current_user
app = Flask(__name__)
app.config['SECRET_KEY'] = 's4d56f7ghuidu87fbnkiuh'
gunicorn_error_logger = logging.getLogger('gunicorn.error')
app.logger.handlers.extend(gunicorn_error_logger.handlers)
25
app.logger.setLevel(logging.DEBUG)
db_user = "tmuser"
db_pswdr = "tmpassword"
db_host = "localhost:5432/"
db_name = "tmdatabase"
app.config['SQLALCHEMY_DATABASE_URI'] =
'postgresql://'+db_user+':'+db_pswdr+'@'+db_host+db_name
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
class consultas(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer)
created_on = db.Column(db.DateTime)
motivo = db.Column(db.String(150))
diagnostico = db.Column(db.String(150))
prescripcion = db.Column(db.String(150))
incapacidad = db.Column(db.String(50))
dispositivo = db.Column(db.Integer)
class dispositivos(db.Model):
id = db.Column(db.Integer, primary_key=True)
device_number = db.Column(db.Integer)
assigned_to = db.Column(db.Integer)
assigned_period = db.Column(db.String(50))
assigned_date = db.Column(db.DateTime)
remote_ip = db.Column(db.String(50))
26
def processGArequest(uname,upwrd):
if (uname=="manuel@me.com" and upwrd=="root"):
response = redirect(url_for("dashboard",user_id='0'))
return response
quser = accounts.query.filter_by(username=uname).first()
if quser:
if quser.password==upwrd: # or
check_password_hash(quser.password,upwrd):
print("User validated through DB, right password used")
login_user(quser)
response = redirect(url_for("dashboard",user_id=quser.id))
response.set_cookie('telemed_app', str(quser.id))
return response
return render_template('access.html')
@app.route("/")
def home():
return render_template("index.html")
@app.route('/dashboard/<user_id>')
def dashboard(user_id):
qaccount = accounts.query.filter_by(id=user_id).first()
rol_id = qaccount.role_id
27
elif str(rol_id) == '1': # Paciente
estudios = consultas.query.filter_by(user_id=int(user_id))
return
render_template('board_patient.html',uname=qaccount.full_name,consultas
=estudios,rol_id=rol_id)
elif str(rol_id) == '2': # Enfermero
pacientes = accounts.query.filter_by(role_id=1)
# print("Pacientes found: ",pacientes)
return
render_template('board_nurse.html',uname=qaccount.full_name,pacientes=p
acientes)
elif str(rol_id) == '3': # Medico
pacientes = accounts.query.filter_by(role_id=1)
return
render_template('board_doctor.html',uname=qaccount.full_name,casos=paci
entes)
elif str(rol_id) == '4': # Medico especialista
pacientes = accounts.query.filter_by(role_id=1)
return
render_template('board_doctor.html',uname=qaccount.full_name,casos=paci
entes)
# elif str(ur_projects.id) == '5': # Investigador
# return
render_template('boards/manager-board.html',uname=qaccount.full_name,pd
ata=ur_projects)
@app.route('/expediente/<user_id>')
def expediente(user_id):
historial = consultas.query.filter_by(user_id=int(user_id))
paciente = accounts.query.filter_by(id=int(user_id)).first()
req_id = request.cookies.get('telemed_app')
print("\tLooking for req_id user :",req_id)
qaccount = accounts.query.filter_by(id=req_id).first()
rol_id = qaccount.role_id
# Roles definidos en las bases de datos
# if str(rol_id) == '0': # Administrador
# elif str(rol_id) == '1': # Paciente
# elif str(rol_id) == '2': # Enfermero
# elif str(rol_id) == '3': # Medico
# elif str(rol_id) == '4': # Medico especialista
# elif str(rol_id) == '5': # Investigador
return render_template('board_expediente.html',
uname=paciente.full_name,
28
patId=user_id,
urol=rol_id,
consultas=historial)
@app.route('/agregar/<user_id>')
def agregar(user_id):
historial = consultas.query.filter_by(user_id=int(user_id))
paciente = accounts.query.filter_by(id=int(user_id)).first()
req_id = request.cookies.get('telemed_app')
print("\tLooking for req_id user :",req_id)
qaccount = accounts.query.filter_by(id=req_id).first()
rol_id = qaccount.role_id
return render_template('board_nuevoRecord.html',
uname=paciente.full_name,
patId=user_id,
urol=rol_id,
consultas=historial)
return redirect(url_for("expediente",user_id=user_id))
@app.route('/dispositivo/<int:device_id>')
def dispositivo(device_id):
configuracion =
dispositivos.query.filter_by(device_number=int(device_id)).first ()
paciente =
accounts.query.filter_by(id=int(configuracion.assigned_to)).first()
req_id = request.cookies.get('telemed_app')
29
consulta =
consultas.query.filter_by(dispositivo=configuracion.device_number).firs
t()
qaccount = accounts.query.filter_by(id=req_id).first()
rol_id = qaccount.role_id
if int(rol_id) > 2:
return render_template('board_dispositivo.html',
devID=configuracion.device_number,
uname=paciente. full_name,
consulta=consulta,
assigned_period=configuracion.assigned_period,
assigned_date=configuracion.assigned_date)
@app.route('/visualizar/<device_id>')
def visualizar (device_id):
configuracion =
dispositivos.query.filter_by(device_number=int(device_id)).first()
paciente =
accounts.query.filter_by(id=int(configuracion.assigned_to)).first()
req_id = request.cookies.get('telemed_app')
return redirect("http://"+configuracion.remote_ip)
@app.route('/home_page', methods=['GET'])
def shortenurl():
if request.method == 'GET':
return redirect('/')
@app.route("/1378118")
def welcome():
return {"message":"Bienvenido, Victor Manuel Castaneda de Leon"}
@app.route("/admin/delete_records")
def delete_records():
return {"message":"registros eliminados correctamente"}
@app.route("/admin/create_records")
def create_records():
return {"message":"registros eliminados correctamente"}
30
@login_manager.user_loader
def load_user(user_id):
return accounts.query.get(int(user_id))
@app.route("/logout")
def logout():
logout_user()
return redirect('/')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=82, debug=True)
Código init.sql
create table accounts (
id serial PRIMARY KEY,
username VARCHAR (50),
password VARCHAR (50),
email VARCHAR (255),
created_on TIMESTAMP,
full_name VARCHAR (150),
verif_status VARCHAR (50),
role_id INT
);
31
insert into accounts(username,password,full_name,email,role_id)
values ('especialista_1','entrar','Ronald McMiller','esp01@mail.me',4);
insert into accounts(username,password,full_name,email,role_id)
values ('especialista_2','entrar','Juliana Shou','esp02@mail.me',4);
insert into accounts(username,password,full_name,email,role_id)
values ('especialista_3','entrar','David Jackson','esp03@mail.me',4);
32
);
33