Project

General

Profile

Código e implementación » History » Version 7

Katalina Oviedo, 12/10/2024 09:37 PM

1 2 Katalina Oviedo
{{thumbnail(AquaPi.png, size=300, title=Logo)}}
2 2 Katalina Oviedo
3 2 Katalina Oviedo
h2. Índice
4 2 Katalina Oviedo
5 2 Katalina Oviedo
* [[Introducción]]
6 2 Katalina Oviedo
* [[Objetivos]]
7 2 Katalina Oviedo
* [[Organización y planificación]]
8 3 Katalina Oviedo
* [[Interfaz]]
9 2 Katalina Oviedo
10 2 Katalina Oviedo
---
11 2 Katalina Oviedo
12 1 Bruno Amestica
h1. Código e implementación
13 4 Katalina Oviedo
14 4 Katalina Oviedo
{{collapse(Código interfaz gráfica)
15 4 Katalina Oviedo
<pre><code class="ruby">
16 4 Katalina Oviedo
from PySide6.QtWidgets import QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox, QStackedWidget, QSpacerItem, QSizePolicy, QLayout
17 4 Katalina Oviedo
from PySide6.QtGui import QPixmap, QIntValidator, QImage
18 4 Katalina Oviedo
from PySide6.QtCore import Qt, QUrl, QThread, Signal
19 4 Katalina Oviedo
from PySide6.QtWebEngineWidgets import QWebEngineView
20 4 Katalina Oviedo
import socket
21 4 Katalina Oviedo
import sys
22 4 Katalina Oviedo
import re
23 4 Katalina Oviedo
import cv2
24 4 Katalina Oviedo
import numpy as np
25 4 Katalina Oviedo
import time
26 4 Katalina Oviedo
from queue import Queue
27 4 Katalina Oviedo
28 4 Katalina Oviedo
class DataReceiver(QThread):
29 4 Katalina Oviedo
    # Señales para actualizar los valores en la interfaz
30 4 Katalina Oviedo
    data_received = Signal(float, float, int)
31 4 Katalina Oviedo
32 4 Katalina Oviedo
    def __init__(self, host, port):
33 4 Katalina Oviedo
        super().__init__()
34 4 Katalina Oviedo
        self.host = host
35 4 Katalina Oviedo
        self.port = port
36 4 Katalina Oviedo
        self.running = True
37 4 Katalina Oviedo
    
38 4 Katalina Oviedo
    def run(self):
39 4 Katalina Oviedo
        try:
40 4 Katalina Oviedo
            # Conexión al servidor
41 4 Katalina Oviedo
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
42 4 Katalina Oviedo
            s.connect((self.host, self.port))
43 4 Katalina Oviedo
44 4 Katalina Oviedo
            while self.running:
45 4 Katalina Oviedo
                # Recibir datos del servidor
46 4 Katalina Oviedo
                data = s.recv(1024)
47 4 Katalina Oviedo
                if not data:
48 4 Katalina Oviedo
                    break
49 4 Katalina Oviedo
50 4 Katalina Oviedo
                # Decodificar y procesar los datos
51 4 Katalina Oviedo
                decoded_data = data.decode('utf-8').strip()
52 4 Katalina Oviedo
                try:
53 4 Katalina Oviedo
                    # Validar que los datos contengan exactamente dos comas (indicativo del formato esperado)
54 4 Katalina Oviedo
                    if decoded_data.count(',') == 2:
55 4 Katalina Oviedo
                        temp, ph, light = map(float, decoded_data.split(','))
56 4 Katalina Oviedo
                        self.data_received.emit(temp, ph, int(light))
57 4 Katalina Oviedo
                       
58 4 Katalina Oviedo
                    else:
59 4 Katalina Oviedo
                        # Ignorar los datos que no tienen el formato correcto
60 4 Katalina Oviedo
                        print("")
61 4 Katalina Oviedo
                except ValueError:
62 4 Katalina Oviedo
                    # Manejar cualquier error inesperado en el procesamiento
63 4 Katalina Oviedo
                    print("")
64 4 Katalina Oviedo
        except Exception as e:
65 4 Katalina Oviedo
            print("Error en la conexión:", e)
66 4 Katalina Oviedo
67 4 Katalina Oviedo
    def stop(self):
68 4 Katalina Oviedo
        self.running = False
69 4 Katalina Oviedo
        self.quit()
70 4 Katalina Oviedo
71 4 Katalina Oviedo
class VideoReceiver(QThread):
72 4 Katalina Oviedo
    frame_received = Signal(QImage)
73 4 Katalina Oviedo
74 4 Katalina Oviedo
    def __init__(self, host, port):
75 4 Katalina Oviedo
        super().__init__()
76 4 Katalina Oviedo
        self.host = host
77 4 Katalina Oviedo
        self.port = port
78 4 Katalina Oviedo
        self.running = True
79 4 Katalina Oviedo
80 4 Katalina Oviedo
    def run(self):
81 4 Katalina Oviedo
        try:
82 4 Katalina Oviedo
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
83 4 Katalina Oviedo
            s.settimeout(5)  # Tiempo de espera de 5 segundos
84 4 Katalina Oviedo
            s.connect((self.host, self.port))
85 4 Katalina Oviedo
86 4 Katalina Oviedo
            while self.running:
87 4 Katalina Oviedo
                # Leer el tamaño del frame (asegúrate de leer siempre 4 bytes)
88 4 Katalina Oviedo
                size_data = b""
89 4 Katalina Oviedo
                while len(size_data) < 4:
90 4 Katalina Oviedo
                    chunk = s.recv(4 - len(size_data))
91 4 Katalina Oviedo
                    if not chunk:
92 4 Katalina Oviedo
                        raise ConnectionError("Conexión cerrada por el servidor.")
93 4 Katalina Oviedo
                    size_data += chunk
94 4 Katalina Oviedo
95 4 Katalina Oviedo
                frame_size = int.from_bytes(size_data, 'big')
96 4 Katalina Oviedo
97 4 Katalina Oviedo
                # Recibir el frame completo
98 4 Katalina Oviedo
                frame_data = b""
99 4 Katalina Oviedo
                while len(frame_data) < frame_size:
100 4 Katalina Oviedo
                    chunk = s.recv(min(4096, frame_size - len(frame_data)))
101 4 Katalina Oviedo
                    if not chunk:
102 4 Katalina Oviedo
                        raise ConnectionError("Conexión cerrada por el servidor.")
103 4 Katalina Oviedo
                    frame_data += chunk
104 4 Katalina Oviedo
105 4 Katalina Oviedo
                # Convertir a imagen
106 4 Katalina Oviedo
                np_array = np.frombuffer(frame_data, dtype=np.uint8)
107 4 Katalina Oviedo
                frame = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
108 4 Katalina Oviedo
                if frame is not None:
109 4 Katalina Oviedo
                    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
110 4 Katalina Oviedo
                    height, width, channel = rgb_frame.shape
111 4 Katalina Oviedo
                    q_image = QImage(rgb_frame.data, width, height, QImage.Format_RGB888)
112 4 Katalina Oviedo
                    self.frame_received.emit(q_image)
113 4 Katalina Oviedo
114 4 Katalina Oviedo
        except Exception as e:
115 4 Katalina Oviedo
            print("Error en la recepción de video:", e)
116 4 Katalina Oviedo
        finally:
117 4 Katalina Oviedo
            s.close()
118 4 Katalina Oviedo
119 4 Katalina Oviedo
    def stop(self):
120 4 Katalina Oviedo
        self.running = False
121 4 Katalina Oviedo
        self.wait()
122 4 Katalina Oviedo
123 4 Katalina Oviedo
class EnviarMensaje(QThread):
124 4 Katalina Oviedo
    """
125 4 Katalina Oviedo
    Clase para enviar mensajes al servidor utilizando una conexión persistente
126 4 Katalina Oviedo
    y una cola para optimizar el envío.
127 4 Katalina Oviedo
    """
128 4 Katalina Oviedo
    def __init__(self, host, port):
129 4 Katalina Oviedo
        super().__init__()
130 4 Katalina Oviedo
        self.host = host
131 4 Katalina Oviedo
        self.port = port
132 4 Katalina Oviedo
        self.running = True
133 4 Katalina Oviedo
        self.queue = Queue()  # Cola para almacenar mensajes a enviar
134 4 Katalina Oviedo
        self.socket = None    # Conexión persistente con el servidor
135 4 Katalina Oviedo
136 4 Katalina Oviedo
    def run(self):
137 4 Katalina Oviedo
        """
138 4 Katalina Oviedo
        Mantiene una conexión persistente con el servidor y envía los mensajes
139 4 Katalina Oviedo
        de la cola según se van añadiendo.
140 4 Katalina Oviedo
        """
141 4 Katalina Oviedo
        try:
142 4 Katalina Oviedo
            # Crear la conexión persistente con el servidor
143 4 Katalina Oviedo
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
144 4 Katalina Oviedo
            self.socket.settimeout(5)
145 4 Katalina Oviedo
            self.socket.connect((self.host, self.port))
146 4 Katalina Oviedo
            print(f"Conexión persistente establecida con {self.host}:{self.port}")
147 4 Katalina Oviedo
148 4 Katalina Oviedo
            while self.running:
149 4 Katalina Oviedo
                try:
150 4 Katalina Oviedo
                    # Obtener un mensaje de la cola (con timeout para evitar bloqueos infinitos)
151 4 Katalina Oviedo
                    mensaje = self.queue.get(timeout=1)
152 4 Katalina Oviedo
153 4 Katalina Oviedo
                    # Si se recibe None, indica que el hilo debe detenerse
154 4 Katalina Oviedo
                    if mensaje is None:
155 4 Katalina Oviedo
                        break
156 4 Katalina Oviedo
157 4 Katalina Oviedo
                    # Enviar el mensaje al servidor
158 4 Katalina Oviedo
                    self.socket.sendall(mensaje.encode('utf-8'))
159 4 Katalina Oviedo
                    print(f"Mensaje enviado: {mensaje}")
160 4 Katalina Oviedo
161 4 Katalina Oviedo
                except Exception as e:
162 4 Katalina Oviedo
                    print(f"Ante la espera: {e}")
163 4 Katalina Oviedo
                    continue
164 4 Katalina Oviedo
165 4 Katalina Oviedo
        except Exception as e:
166 4 Katalina Oviedo
            print(f"Error al establecer conexión persistente: {e}")
167 4 Katalina Oviedo
        finally:
168 4 Katalina Oviedo
            # Asegurarse de cerrar la conexión al finalizar
169 4 Katalina Oviedo
            if self.socket:
170 4 Katalina Oviedo
                self.socket.close()
171 4 Katalina Oviedo
                print("Conexión cerrada.")
172 4 Katalina Oviedo
173 4 Katalina Oviedo
    def enviar_mensaje(self, mensaje):
174 4 Katalina Oviedo
        """
175 4 Katalina Oviedo
        Coloca un mensaje en la cola para ser enviado.
176 4 Katalina Oviedo
        """
177 4 Katalina Oviedo
        self.queue.put(mensaje)
178 4 Katalina Oviedo
179 4 Katalina Oviedo
    def stop(self):
180 4 Katalina Oviedo
        """
181 4 Katalina Oviedo
        Detiene el hilo limpiamente.
182 4 Katalina Oviedo
        """
183 4 Katalina Oviedo
        self.running = False
184 4 Katalina Oviedo
        self.queue.put(None)  # Coloca un marcador para desbloquear el bucle
185 4 Katalina Oviedo
        self.wait()
186 4 Katalina Oviedo
187 4 Katalina Oviedo
class AquaPiApp(QWidget):
188 4 Katalina Oviedo
    def __init__(self):
189 4 Katalina Oviedo
        super().__init__()
190 4 Katalina Oviedo
        self.setWindowTitle("AquaPi")
191 4 Katalina Oviedo
        self.setFixedSize(346.32, 750)
192 4 Katalina Oviedo
        self.rangos = {'temperatura': {'min': 1.0, 'max': 100.0}, 'ph': {'min': 1.0, 'max': 100.0}, 'luz': {'min': 1.0, 'max': 200.0}}
193 4 Katalina Oviedo
        self.estado_parametros = {"temperatura": True, "ph": True, "luz": True}  # True = dentro del rango  
194 4 Katalina Oviedo
        self.cargar_estilos()
195 4 Katalina Oviedo
196 4 Katalina Oviedo
        self.stack = QStackedWidget(self)
197 4 Katalina Oviedo
        self.setLayout(QVBoxLayout())
198 4 Katalina Oviedo
        self.layout().setContentsMargins(0, 0, 0, 0)
199 4 Katalina Oviedo
        self.layout().setSpacing(0)
200 4 Katalina Oviedo
        self.layout().addWidget(self.stack)
201 4 Katalina Oviedo
202 4 Katalina Oviedo
        # Crear widgets de monitoreo una sola vez
203 4 Katalina Oviedo
        self.temp_label = QLabel("0°C")
204 4 Katalina Oviedo
        self.ph_label = QLabel("0.0")
205 4 Katalina Oviedo
        self.luz_label = QLabel("0%")
206 4 Katalina Oviedo
207 4 Katalina Oviedo
        self.data_receiver = None
208 4 Katalina Oviedo
209 4 Katalina Oviedo
        self.mostrar_pantalla_conexion()
210 4 Katalina Oviedo
211 4 Katalina Oviedo
    def mostrar_pantalla_conexion(self):
212 4 Katalina Oviedo
        layout = QVBoxLayout()
213 4 Katalina Oviedo
214 4 Katalina Oviedo
        titulo = QLabel("AQUA PI")
215 4 Katalina Oviedo
        titulo.setAlignment(Qt.AlignCenter)
216 4 Katalina Oviedo
        titulo.setObjectName("titulo_aquapi")
217 4 Katalina Oviedo
218 4 Katalina Oviedo
        entry_ip = QLineEdit()
219 4 Katalina Oviedo
        entry_ip.setObjectName("texto_ruta")
220 4 Katalina Oviedo
        entry_ip.setPlaceholderText("Ingrese la IP del servidor...")
221 4 Katalina Oviedo
222 4 Katalina Oviedo
        entry_ip.returnPressed.connect(lambda: self.verificar_conexion(entry_ip.text()))
223 4 Katalina Oviedo
224 4 Katalina Oviedo
        btn_conexion = QPushButton("Establecer Conexión")
225 4 Katalina Oviedo
        btn_conexion.setObjectName("boton_conectar")
226 4 Katalina Oviedo
        btn_conexion.clicked.connect(lambda: self.verificar_conexion(entry_ip.text()))
227 4 Katalina Oviedo
228 4 Katalina Oviedo
        layout.addWidget(titulo)
229 4 Katalina Oviedo
        layout.addWidget(entry_ip)
230 4 Katalina Oviedo
        layout.addWidget(btn_conexion)
231 4 Katalina Oviedo
        layout.setAlignment(Qt.AlignTop)
232 4 Katalina Oviedo
233 4 Katalina Oviedo
        pantalla_conexion = QWidget()
234 4 Katalina Oviedo
        pantalla_conexion.setLayout(layout)
235 4 Katalina Oviedo
        self.stack.addWidget(pantalla_conexion)
236 4 Katalina Oviedo
        self.stack.setCurrentWidget(pantalla_conexion)
237 4 Katalina Oviedo
238 4 Katalina Oviedo
    def verificar_conexion(self, ip):
239 4 Katalina Oviedo
        if not self.validar_ip(ip):            
240 4 Katalina Oviedo
            QMessageBox.critical(self, "Error de IP", "La IP ingresada no es válida.")
241 4 Katalina Oviedo
            return
242 4 Katalina Oviedo
243 4 Katalina Oviedo
        try:
244 4 Katalina Oviedo
            # Iniciar recepción de datos
245 4 Katalina Oviedo
            self.data_receiver = DataReceiver(ip, 5560)
246 4 Katalina Oviedo
            self.data_receiver.data_received.connect(self.actualizar_datos)  # Conexión correcta
247 4 Katalina Oviedo
            self.data_receiver.start()
248 4 Katalina Oviedo
249 4 Katalina Oviedo
            # Iniciar recepción de video
250 4 Katalina Oviedo
            self.video_receiver = VideoReceiver(ip, 5561)
251 4 Katalina Oviedo
            self.video_receiver.frame_received.connect(self.actualizar_video)
252 4 Katalina Oviedo
            self.video_receiver.start()
253 4 Katalina Oviedo
254 4 Katalina Oviedo
            # Crear la instancia de EnviarMensaje
255 4 Katalina Oviedo
            self.enviar_mensajes = EnviarMensaje(ip, 5562)
256 4 Katalina Oviedo
            self.enviar_mensajes.start()
257 4 Katalina Oviedo
258 4 Katalina Oviedo
259 4 Katalina Oviedo
            # Mostrar la pantalla principal
260 4 Katalina Oviedo
            self.mostrar_pantalla_monitoreo()
261 4 Katalina Oviedo
262 4 Katalina Oviedo
        except Exception as e:
263 4 Katalina Oviedo
            QMessageBox.critical(self, "Error de Conexión", f"No se pudo conectar al servidor: {e}")
264 4 Katalina Oviedo
265 4 Katalina Oviedo
    def validar_ip(self, ip):
266 4 Katalina Oviedo
        # Verificar si la IP ingresada tiene un formato válido
267 4 Katalina Oviedo
        ip_regex = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$")
268 4 Katalina Oviedo
        return ip_regex.match(ip) is not None
269 4 Katalina Oviedo
270 4 Katalina Oviedo
    def mostrar_pantalla_monitoreo(self):
271 4 Katalina Oviedo
        # Verificar si la pantalla de monitoreo ya existe
272 4 Katalina Oviedo
        if hasattr(self, 'pantalla_monitoreo'):
273 4 Katalina Oviedo
            self.stack.setCurrentWidget(self.pantalla_monitoreo)
274 4 Katalina Oviedo
            return
275 4 Katalina Oviedo
276 4 Katalina Oviedo
        # Crear layout para la pantalla de monitoreo
277 4 Katalina Oviedo
        layout = QVBoxLayout()
278 4 Katalina Oviedo
        layout.setContentsMargins(0, 0, 0, 0)
279 4 Katalina Oviedo
280 4 Katalina Oviedo
        # Crear título
281 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
282 4 Katalina Oviedo
        titulo_label = QLabel("AQUAPI", self)
283 4 Katalina Oviedo
        titulo_label.setAlignment(Qt.AlignCenter)
284 4 Katalina Oviedo
        titulo_label.setObjectName("titulo_pantalla")
285 4 Katalina Oviedo
        layout.addWidget(titulo_label)
286 4 Katalina Oviedo
287 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
288 4 Katalina Oviedo
289 4 Katalina Oviedo
        # Crear etiquetas para mostrar los valores (ya inicializadas)
290 4 Katalina Oviedo
        self.temp_label = self.agregar_cuadro(layout, "assets/temperatura.png", "Temperatura", "0°C")
291 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 10))
292 4 Katalina Oviedo
        self.ph_label = self.agregar_cuadro(layout, "assets/ph.png", "pH", "0.0")
293 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 10))
294 4 Katalina Oviedo
        self.luz_label = self.agregar_cuadro(layout, "assets/luz.png", "Luz", "0%")
295 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 88))
296 4 Katalina Oviedo
297 4 Katalina Oviedo
        # Crear barra inferior con botones
298 4 Katalina Oviedo
        btn_transmision = self.img_button(texto="Transmision", ruta_imagen="assets/transmision.png", ancho=120, alto=140, callback=self.mostrar_pantalla_transmision)
299 4 Katalina Oviedo
        btn_config = self.img_button(texto="Configuración", ruta_imagen="assets/config.png", ancho=120, alto=140, callback=self.mostrar_pantalla_config)
300 4 Katalina Oviedo
301 4 Katalina Oviedo
        rectangulo_inferior = QWidget(self)
302 4 Katalina Oviedo
        rectangulo_inferior.setObjectName("rectangulo")
303 4 Katalina Oviedo
        rectangulo_inferior.setFixedHeight(79)
304 4 Katalina Oviedo
305 4 Katalina Oviedo
        rectangulo_layout = QHBoxLayout(rectangulo_inferior)
306 4 Katalina Oviedo
        rectangulo_layout.setContentsMargins(0, 0, 0, 0)
307 4 Katalina Oviedo
308 4 Katalina Oviedo
        rectangulo_layout.addWidget(btn_transmision)
309 4 Katalina Oviedo
        rectangulo_layout.addWidget(btn_config)
310 4 Katalina Oviedo
311 4 Katalina Oviedo
        layout.addWidget(rectangulo_inferior)
312 4 Katalina Oviedo
313 4 Katalina Oviedo
        # Guardar la pantalla de monitoreo en un atributo
314 4 Katalina Oviedo
        self.pantalla_monitoreo = QWidget()
315 4 Katalina Oviedo
        self.pantalla_monitoreo.setLayout(layout)
316 4 Katalina Oviedo
        self.stack.addWidget(self.pantalla_monitoreo)
317 4 Katalina Oviedo
        self.stack.setCurrentWidget(self.pantalla_monitoreo)
318 4 Katalina Oviedo
319 4 Katalina Oviedo
    def actualizar_datos(self, temp, ph, light):
320 4 Katalina Oviedo
        # Actualizar valores
321 4 Katalina Oviedo
        self.temp_label.setText(f"{temp:.2f}°C")
322 4 Katalina Oviedo
        self.ph_label.setText(f"{ph:.2f}")
323 4 Katalina Oviedo
        self.luz_label.setText(f"{light}%")
324 4 Katalina Oviedo
325 4 Katalina Oviedo
        # Validar y cambiar colores
326 4 Katalina Oviedo
        self.validar_y_cambiar_color(self.temp_label, "temperatura", temp, self.parametro_temp_label)
327 4 Katalina Oviedo
        # Validar y actualizar los colores para pH
328 4 Katalina Oviedo
        self.validar_y_cambiar_color(self.ph_label, "ph", ph, self.parametro_ph_label)
329 4 Katalina Oviedo
        # Validar y actualizar los colores para luz
330 4 Katalina Oviedo
        self.validar_y_cambiar_color(self.luz_label, "luz", light, self.parametro_luz_label)
331 4 Katalina Oviedo
332 4 Katalina Oviedo
    def validar_y_cambiar_color(self, label, parametro, valor, parametro_label):
333 4 Katalina Oviedo
        """
334 4 Katalina Oviedo
        Valida el valor y cambia el color del frame y del label del parámetro si el valor no está en rango.
335 4 Katalina Oviedo
        Envía un mensaje solo si el estado cambia (de dentro a fuera de rango o viceversa).
336 4 Katalina Oviedo
        """
337 4 Katalina Oviedo
        valor_numerico = self.convertir_a_numero(valor)
338 4 Katalina Oviedo
        dentro_de_rango = valor_numerico is not None and self.validar_valor(parametro, valor_numerico)
339 4 Katalina Oviedo
340 4 Katalina Oviedo
        # Si el estado cambia, actualizar el estado y enviar el mensaje correspondiente
341 4 Katalina Oviedo
        if self.estado_parametros[parametro] != dentro_de_rango and parametro == "ph":
342 4 Katalina Oviedo
            self.estado_parametros[parametro] = dentro_de_rango
343 4 Katalina Oviedo
            mensaje = "RELAY_ON" if not dentro_de_rango else "RELAY_OFF"
344 4 Katalina Oviedo
            self.enviar_mensajes.enviar_mensaje(mensaje)
345 4 Katalina Oviedo
346 4 Katalina Oviedo
        # Cambiar estilos visuales según el estado
347 4 Katalina Oviedo
        if dentro_de_rango:
348 4 Katalina Oviedo
            label.parentWidget().setStyleSheet(
349 4 Katalina Oviedo
                "border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;"
350 4 Katalina Oviedo
            )
351 4 Katalina Oviedo
            parametro_label.setStyleSheet("color: white; font-size: 21px")
352 4 Katalina Oviedo
        else:
353 4 Katalina Oviedo
            label.parentWidget().setStyleSheet(
354 4 Katalina Oviedo
                "border: 8px solid; border-radius: 20px; background-color: #984F4F; border-color: #984F4F;"
355 4 Katalina Oviedo
            )
356 4 Katalina Oviedo
            parametro_label.setStyleSheet("color: #EF5E5E; font-size: 21px")
357 4 Katalina Oviedo
358 4 Katalina Oviedo
    def validar_valor(self, parametro, valor):
359 4 Katalina Oviedo
        """Valida si el valor está dentro del rango configurado para el parámetro."""
360 4 Katalina Oviedo
        # Convertimos el parámetro a minúsculas para asegurar consistencia
361 4 Katalina Oviedo
        parametro_lower = parametro.lower()
362 4 Katalina Oviedo
        # Verificamos si el parámetro existe en los rangos
363 4 Katalina Oviedo
        if parametro_lower not in self.rangos:
364 4 Katalina Oviedo
            return False
365 4 Katalina Oviedo
        # Accedemos al rango correspondiente
366 4 Katalina Oviedo
        rango = self.rangos[parametro_lower]  # Asegurando que se use la clave correcta
367 4 Katalina Oviedo
        # Verificamos que el valor esté en el formato adecuado (convertirlo a float si es necesario)
368 4 Katalina Oviedo
        try:
369 4 Katalina Oviedo
            valor = float(valor)  # Asegúrate de que 'valor' sea un número
370 4 Katalina Oviedo
        except ValueError:
371 4 Katalina Oviedo
            print(f"Valor no válido: {valor}")
372 4 Katalina Oviedo
            return False
373 4 Katalina Oviedo
        # Comprobamos si el valor está fuera del rango
374 4 Katalina Oviedo
        if valor < rango['min'] or valor > rango['max']:
375 4 Katalina Oviedo
            return False  # Valor fuera de rango
376 4 Katalina Oviedo
        else:
377 4 Katalina Oviedo
            return True  # Valor dentro del rango
378 4 Katalina Oviedo
379 4 Katalina Oviedo
    def closeEvent(self, event):
380 4 Katalina Oviedo
        if self.data_receiver:
381 4 Katalina Oviedo
            self.data_receiver.stop()
382 4 Katalina Oviedo
        if hasattr(self, 'video_receiver') and self.video_receiver:
383 4 Katalina Oviedo
            self.video_receiver.stop()
384 4 Katalina Oviedo
        super().closeEvent(event)
385 4 Katalina Oviedo
    
386 4 Katalina Oviedo
    def actualizar_video(self, frame):
387 4 Katalina Oviedo
        if self.video_label:
388 4 Katalina Oviedo
            self.video_label.setPixmap(QPixmap.fromImage(frame))
389 4 Katalina Oviedo
390 4 Katalina Oviedo
    def mostrar_pantalla_transmision(self):
391 4 Katalina Oviedo
        layout = QVBoxLayout()
392 4 Katalina Oviedo
        layout.setContentsMargins(0, 0, 0, 0)
393 4 Katalina Oviedo
394 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
395 4 Katalina Oviedo
396 4 Katalina Oviedo
        # Título de la pantalla
397 4 Katalina Oviedo
        titulo_label = QLabel("AQUAPI", self)
398 4 Katalina Oviedo
        titulo_label.setAlignment(Qt.AlignCenter)
399 4 Katalina Oviedo
        titulo_label.setObjectName("titulo_pantalla")
400 4 Katalina Oviedo
        layout.addWidget(titulo_label)
401 4 Katalina Oviedo
402 4 Katalina Oviedo
        # Espaciado debajo del título
403 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 134, QSizePolicy.Minimum, QSizePolicy.Fixed))
404 4 Katalina Oviedo
405 4 Katalina Oviedo
        self.video_label = QLabel(self)
406 4 Katalina Oviedo
        self.video_label.setFixedSize(318, 242)
407 4 Katalina Oviedo
        self.video_label.setStyleSheet("background-color: black;")
408 4 Katalina Oviedo
        self.video_label.setAlignment(Qt.AlignCenter)  # Asegura que el contenido del QLabel esté centrado
409 4 Katalina Oviedo
        layout.addWidget(self.video_label, alignment=Qt.AlignCenter)
410 4 Katalina Oviedo
411 4 Katalina Oviedo
        # Espaciado debajo del rectángulo
412 4 Katalina Oviedo
        layout.addItem(QSpacerItem(0, 206, QSizePolicy.Minimum, QSizePolicy.Fixed))
413 4 Katalina Oviedo
414 4 Katalina Oviedo
        # Botones de configuración y monitoreo
415 4 Katalina Oviedo
        btn_config = self.img_button(texto="Configuración", ruta_imagen="assets/config.png", ancho=120, alto=140, callback=self.mostrar_pantalla_config)
416 4 Katalina Oviedo
        btn_monitoreo = self.img_button(texto="Monitoreo", ruta_imagen="assets/monitoreo.png", ancho=120, alto=140, callback=self.mostrar_pantalla_monitoreo)
417 4 Katalina Oviedo
418 4 Katalina Oviedo
        # Rectángulo inferior con los botones
419 4 Katalina Oviedo
        rectangulo_inferior = QWidget(self)
420 4 Katalina Oviedo
        rectangulo_inferior.setObjectName("rectangulo")
421 4 Katalina Oviedo
        rectangulo_inferior.setFixedHeight(79)
422 4 Katalina Oviedo
423 4 Katalina Oviedo
        rectangulo_layout_inferior = QHBoxLayout(rectangulo_inferior)
424 4 Katalina Oviedo
        rectangulo_layout_inferior.setContentsMargins(0, 0, 0, 0)
425 4 Katalina Oviedo
426 4 Katalina Oviedo
        rectangulo_layout_inferior.addWidget(btn_config)
427 4 Katalina Oviedo
        rectangulo_layout_inferior.addItem(QSpacerItem(4, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
428 4 Katalina Oviedo
        rectangulo_layout_inferior.addWidget(btn_monitoreo)
429 4 Katalina Oviedo
        rectangulo_layout_inferior.addItem(QSpacerItem(14, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
430 4 Katalina Oviedo
431 4 Katalina Oviedo
        layout.addWidget(rectangulo_inferior)
432 4 Katalina Oviedo
433 4 Katalina Oviedo
        # Crear el widget final y agregarlo al stack
434 4 Katalina Oviedo
        pantalla_transmision = QWidget()
435 4 Katalina Oviedo
        pantalla_transmision.setLayout(layout)
436 4 Katalina Oviedo
        self.stack.addWidget(pantalla_transmision)
437 4 Katalina Oviedo
438 4 Katalina Oviedo
        # Mostrar la pantalla
439 4 Katalina Oviedo
        self.stack.setCurrentWidget(pantalla_transmision)
440 4 Katalina Oviedo
441 4 Katalina Oviedo
    def convertir_a_numero(self, valor):
442 4 Katalina Oviedo
        """
443 4 Katalina Oviedo
        Convierte un valor a un número, eliminando caracteres extra como '°C' o '%'.
444 4 Katalina Oviedo
        Si no es posible convertirlo, devuelve None.
445 4 Katalina Oviedo
        """
446 4 Katalina Oviedo
        # Si el valor ya es un número, simplemente devolverlo
447 4 Katalina Oviedo
        if isinstance(valor, (int, float)):
448 4 Katalina Oviedo
            return valor
449 4 Katalina Oviedo
450 4 Katalina Oviedo
        # Si es una cadena, intentar convertirla
451 4 Katalina Oviedo
        try:
452 4 Katalina Oviedo
            if isinstance(valor, str):
453 4 Katalina Oviedo
                # Eliminar caracteres específicos como '°C' o '%'
454 4 Katalina Oviedo
                if '°C' in valor:
455 4 Katalina Oviedo
                    valor = valor.replace('°C', '').strip()
456 4 Katalina Oviedo
                if '%' in valor:
457 4 Katalina Oviedo
                    valor = valor.replace('%', '').strip()
458 4 Katalina Oviedo
                # Convertir la cadena a un número
459 4 Katalina Oviedo
                return float(valor)
460 4 Katalina Oviedo
        except (ValueError, TypeError):
461 4 Katalina Oviedo
            print("Error al convertir el valor:", valor)
462 4 Katalina Oviedo
            return None
463 4 Katalina Oviedo
464 4 Katalina Oviedo
    def img_button(self, texto, ruta_imagen, ancho=120, alto=140, callback=None):
465 4 Katalina Oviedo
        boton = QPushButton()
466 4 Katalina Oviedo
        boton.setObjectName("boton_accion")
467 4 Katalina Oviedo
        boton.setFixedSize(ancho, alto)
468 4 Katalina Oviedo
469 4 Katalina Oviedo
        layout = QVBoxLayout(boton)
470 4 Katalina Oviedo
        layout.setContentsMargins(0, 0, 0, 0)
471 4 Katalina Oviedo
        layout.setSpacing(0)
472 4 Katalina Oviedo
473 4 Katalina Oviedo
        label_imagen = QLabel()
474 4 Katalina Oviedo
        pixmap = QPixmap(ruta_imagen).scaled(50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation)
475 4 Katalina Oviedo
        label_imagen.setPixmap(pixmap)
476 4 Katalina Oviedo
        label_imagen.setAlignment(Qt.AlignCenter)
477 4 Katalina Oviedo
        label_imagen.setMinimumSize(31, 31)
478 4 Katalina Oviedo
        label_imagen.setStyleSheet("background-color: none")
479 4 Katalina Oviedo
        layout.addWidget(label_imagen)
480 4 Katalina Oviedo
481 4 Katalina Oviedo
        label_texto = QLabel(texto)
482 4 Katalina Oviedo
        label_texto.setAlignment(Qt.AlignCenter)
483 4 Katalina Oviedo
        label_texto.setStyleSheet("color: white; font-size: 14px; background-color: transparent; font-weight: normal")
484 4 Katalina Oviedo
        layout.addWidget(label_texto)
485 4 Katalina Oviedo
486 4 Katalina Oviedo
        layout.setSizeConstraint(QLayout.SetFixedSize)
487 4 Katalina Oviedo
488 4 Katalina Oviedo
        if callback:
489 4 Katalina Oviedo
            boton.clicked.connect(callback)
490 4 Katalina Oviedo
491 4 Katalina Oviedo
        return boton
492 4 Katalina Oviedo
493 4 Katalina Oviedo
    def input_frame(self, layout, ancho, alto, texto_bajo):
494 4 Katalina Oviedo
        """Crea un cuadro de entrada estilizado con un texto debajo y lo agrega al layout dado."""
495 4 Katalina Oviedo
        cuadro_layout = QVBoxLayout()
496 4 Katalina Oviedo
497 4 Katalina Oviedo
        # Crear el marco estilizado
498 4 Katalina Oviedo
        frame = QWidget(self)
499 4 Katalina Oviedo
        frame.setStyleSheet("border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;")
500 4 Katalina Oviedo
        frame.setFixedSize(ancho, alto)
501 4 Katalina Oviedo
502 4 Katalina Oviedo
        frame_layout = QVBoxLayout(frame)
503 4 Katalina Oviedo
        frame_layout.setSpacing(0)
504 4 Katalina Oviedo
        frame_layout.setContentsMargins(0, 0, 0, 0)
505 4 Katalina Oviedo
506 4 Katalina Oviedo
        # Crear el campo de entrada
507 4 Katalina Oviedo
        input_field = QLineEdit(frame)
508 4 Katalina Oviedo
        
509 4 Katalina Oviedo
        # Función para validar manualmente el texto del input
510 4 Katalina Oviedo
        def validar_entrada():
511 4 Katalina Oviedo
            texto = input_field.text()
512 4 Katalina Oviedo
            # Reemplazar comas por puntos
513 4 Katalina Oviedo
            texto = texto.replace(",", ".")
514 4 Katalina Oviedo
            # Validar si el texto es un número flotante válido
515 4 Katalina Oviedo
            try:
516 4 Katalina Oviedo
                valor = float(texto)
517 4 Katalina Oviedo
                # Formatear el número para que muestre hasta dos decimales, pero no añada decimales innecesarios
518 4 Katalina Oviedo
                if valor.is_integer():
519 4 Katalina Oviedo
                    input_field.setText(f"{int(valor)}")  # Si es un número entero, mostrar sin decimales
520 4 Katalina Oviedo
                else:
521 4 Katalina Oviedo
                    input_field.setText(f"{valor:.2f}")  # Si tiene decimales, mostrar hasta dos decimales
522 4 Katalina Oviedo
523 4 Katalina Oviedo
            except ValueError:
524 4 Katalina Oviedo
                # Si no es un número válido, podemos vaciar el campo o dejarlo como está
525 4 Katalina Oviedo
                input_field.setText("")  # O, si prefieres, deja el valor sin cambiar
526 4 Katalina Oviedo
                return
527 4 Katalina Oviedo
528 4 Katalina Oviedo
        # Conectar el evento de perder el foco (cuando el usuario termina de editar)
529 4 Katalina Oviedo
        input_field.editingFinished.connect(validar_entrada)
530 4 Katalina Oviedo
531 4 Katalina Oviedo
        input_field.setAlignment(Qt.AlignCenter)  # Centrar el texto ingresado
532 4 Katalina Oviedo
        input_field.setObjectName("input_config")  # Identificador para estilos
533 4 Katalina Oviedo
        frame_layout.addWidget(input_field)
534 4 Katalina Oviedo
535 4 Katalina Oviedo
        # Agregar el marco al diseño principal
536 4 Katalina Oviedo
        cuadro_layout.addWidget(frame, alignment=Qt.AlignHCenter)
537 4 Katalina Oviedo
538 4 Katalina Oviedo
        # Crear la etiqueta debajo del input
539 4 Katalina Oviedo
        label_bajo = QLabel(texto_bajo, self)
540 4 Katalina Oviedo
        label_bajo.setAlignment(Qt.AlignCenter)
541 4 Katalina Oviedo
        label_bajo.setStyleSheet("color: white; font-size: 21px; background-color: none; margin-top: 5px")
542 4 Katalina Oviedo
        cuadro_layout.addWidget(label_bajo, alignment=Qt.AlignHCenter)
543 4 Katalina Oviedo
544 4 Katalina Oviedo
        # Agregar el cuadro al diseño recibido
545 4 Katalina Oviedo
        layout.addLayout(cuadro_layout)
546 4 Katalina Oviedo
547 4 Katalina Oviedo
        # Devolver el campo de entrada para seguimiento
548 4 Katalina Oviedo
        return input_field
549 4 Katalina Oviedo
550 4 Katalina Oviedo
    def mostrar_pantalla_config(self):
551 4 Katalina Oviedo
        layout = QVBoxLayout()
552 4 Katalina Oviedo
        layout.setContentsMargins(0, 0, 0, 0)
553 4 Katalina Oviedo
554 4 Katalina Oviedo
        # Título
555 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
556 4 Katalina Oviedo
        titulo_label = QLabel("AQUAPI", self)
557 4 Katalina Oviedo
        titulo_label.setAlignment(Qt.AlignCenter)
558 4 Katalina Oviedo
        titulo_label.setObjectName("titulo_pantalla")
559 4 Katalina Oviedo
        layout.addWidget(titulo_label)
560 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 30, QSizePolicy.Minimum, QSizePolicy.Fixed))
561 4 Katalina Oviedo
562 4 Katalina Oviedo
        # Inputs
563 4 Katalina Oviedo
        self.inputs = []  # Lista para almacenar referencias a los inputs
564 4 Katalina Oviedo
565 4 Katalina Oviedo
        fila1_layout = QHBoxLayout()
566 4 Katalina Oviedo
        label_temp = QLabel("Temperatura")
567 4 Katalina Oviedo
        label_temp.setObjectName("parametro")
568 4 Katalina Oviedo
        label_temp.setAlignment(Qt.AlignCenter)
569 4 Katalina Oviedo
        layout.addWidget(label_temp)
570 4 Katalina Oviedo
        input_min_temp = self.input_frame(fila1_layout, 116, 68, "Min")
571 4 Katalina Oviedo
        input_max_temp = self.input_frame(fila1_layout, 116, 68, "Máx")
572 4 Katalina Oviedo
        self.inputs.append(input_min_temp)
573 4 Katalina Oviedo
        self.inputs.append(input_max_temp)
574 4 Katalina Oviedo
        layout.addLayout(fila1_layout)
575 4 Katalina Oviedo
        layout.addItem(QSpacerItem(0, 2, QSizePolicy.Minimum, QSizePolicy.Fixed))
576 4 Katalina Oviedo
577 4 Katalina Oviedo
        fila2_layout = QHBoxLayout()
578 4 Katalina Oviedo
        label_ph = QLabel("pH")
579 4 Katalina Oviedo
        label_ph.setObjectName("parametro")
580 4 Katalina Oviedo
        label_ph.setAlignment(Qt.AlignCenter)
581 4 Katalina Oviedo
        layout.addWidget(label_ph)
582 4 Katalina Oviedo
        input_min_ph = self.input_frame(fila2_layout, 116, 68, "Min")
583 4 Katalina Oviedo
        input_max_ph = self.input_frame(fila2_layout, 116, 68, "Máx")
584 4 Katalina Oviedo
        self.inputs.append(input_min_ph)
585 4 Katalina Oviedo
        self.inputs.append(input_max_ph)
586 4 Katalina Oviedo
        layout.addLayout(fila2_layout)
587 4 Katalina Oviedo
        layout.addItem(QSpacerItem(0, 5, QSizePolicy.Minimum, QSizePolicy.Fixed))
588 4 Katalina Oviedo
589 4 Katalina Oviedo
        fila3_layout = QHBoxLayout()
590 4 Katalina Oviedo
        label_luz = QLabel("Luz")
591 4 Katalina Oviedo
        label_luz.setObjectName("parametro")
592 4 Katalina Oviedo
        label_luz.setAlignment(Qt.AlignCenter)
593 4 Katalina Oviedo
        layout.addWidget(label_luz)
594 4 Katalina Oviedo
        input_min_luz = self.input_frame(fila3_layout, 116, 68, "Min")
595 4 Katalina Oviedo
        input_max_luz = self.input_frame(fila3_layout, 116, 68, "Máx")
596 4 Katalina Oviedo
        self.inputs.append(input_min_luz)
597 4 Katalina Oviedo
        self.inputs.append(input_max_luz)
598 4 Katalina Oviedo
        layout.addLayout(fila3_layout)
599 4 Katalina Oviedo
        layout.addItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Fixed))
600 4 Katalina Oviedo
601 4 Katalina Oviedo
        # Rellenar los valores en los inputs desde self.rangos
602 4 Katalina Oviedo
        if self.rangos['temperatura']['min'] is not None:
603 4 Katalina Oviedo
            self.inputs[0].setText(str(self.rangos['temperatura']['min']))
604 4 Katalina Oviedo
        if self.rangos['temperatura']['max'] is not None:
605 4 Katalina Oviedo
            self.inputs[1].setText(str(self.rangos['temperatura']['max']))
606 4 Katalina Oviedo
        
607 4 Katalina Oviedo
        if self.rangos['ph']['min'] is not None:
608 4 Katalina Oviedo
            self.inputs[2].setText(str(self.rangos['ph']['min']))
609 4 Katalina Oviedo
        if self.rangos['ph']['max'] is not None:
610 4 Katalina Oviedo
            self.inputs[3].setText(str(self.rangos['ph']['max']))
611 4 Katalina Oviedo
612 4 Katalina Oviedo
        if self.rangos['luz']['min'] is not None:
613 4 Katalina Oviedo
            self.inputs[4].setText(str(self.rangos['luz']['min']))
614 4 Katalina Oviedo
        if self.rangos['luz']['max'] is not None:
615 4 Katalina Oviedo
            self.inputs[5].setText(str(self.rangos['luz']['max']))
616 4 Katalina Oviedo
617 4 Katalina Oviedo
        # Botón Confirmar
618 4 Katalina Oviedo
        confirmar_btn = QPushButton("Confirmar", self)
619 4 Katalina Oviedo
        confirmar_btn.setObjectName("confirmar_btn")
620 4 Katalina Oviedo
        confirmar_btn.clicked.connect(self.enviar_datos)  # Conectar el evento del botón
621 4 Katalina Oviedo
        layout.addWidget(confirmar_btn, alignment=Qt.AlignCenter)
622 4 Katalina Oviedo
623 4 Katalina Oviedo
        # Espaciador antes del rectángulo inferior
624 4 Katalina Oviedo
        layout.addItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Fixed))
625 4 Katalina Oviedo
626 4 Katalina Oviedo
        # Rectángulo inferior con botones
627 4 Katalina Oviedo
        pantalla_config = QWidget()
628 4 Katalina Oviedo
        pantalla_config.setLayout(layout)
629 4 Katalina Oviedo
        self.stack.addWidget(pantalla_config)
630 4 Katalina Oviedo
        self.stack.setCurrentWidget(pantalla_config)
631 4 Katalina Oviedo
632 4 Katalina Oviedo
        btn_monitoreo = self.img_button(
633 4 Katalina Oviedo
            texto="Monitoreo",
634 4 Katalina Oviedo
            ruta_imagen="assets/monitoreo.png",
635 4 Katalina Oviedo
            ancho=120,
636 4 Katalina Oviedo
            alto=140,
637 4 Katalina Oviedo
            callback=self.mostrar_pantalla_monitoreo
638 4 Katalina Oviedo
        )
639 4 Katalina Oviedo
        btn_transmision = self.img_button(
640 4 Katalina Oviedo
            texto="Transmision",
641 4 Katalina Oviedo
            ruta_imagen="assets/transmision.png",
642 4 Katalina Oviedo
            ancho=120,
643 4 Katalina Oviedo
            alto=140,
644 4 Katalina Oviedo
            callback=self.mostrar_pantalla_transmision
645 4 Katalina Oviedo
        )
646 4 Katalina Oviedo
647 4 Katalina Oviedo
        rectangulo_inferior = QWidget(self)
648 4 Katalina Oviedo
        rectangulo_inferior.setObjectName("rectangulo")
649 4 Katalina Oviedo
        rectangulo_inferior.setFixedHeight(79)
650 4 Katalina Oviedo
651 4 Katalina Oviedo
        rectangulo_layout = QHBoxLayout(rectangulo_inferior)
652 4 Katalina Oviedo
        rectangulo_layout.setContentsMargins(0, 0, 0, 0)
653 4 Katalina Oviedo
654 4 Katalina Oviedo
        rectangulo_layout.addWidget(btn_monitoreo)
655 4 Katalina Oviedo
        rectangulo_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
656 4 Katalina Oviedo
        rectangulo_layout.addWidget(btn_transmision)
657 4 Katalina Oviedo
        rectangulo_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
658 4 Katalina Oviedo
659 4 Katalina Oviedo
        layout.addWidget(rectangulo_inferior)
660 4 Katalina Oviedo
661 4 Katalina Oviedo
    def agregar_cuadro(self, layout, icono_path, parametro, valor):
662 4 Katalina Oviedo
        # Crear el frame y asignarle un objectName único
663 4 Katalina Oviedo
        frame = QWidget(self)
664 4 Katalina Oviedo
        frame.setObjectName(parametro.lower())  # Asegurando que esté en minúsculas
665 4 Katalina Oviedo
        frame.setStyleSheet(f"border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;")
666 4 Katalina Oviedo
667 4 Katalina Oviedo
        frame_layout = QHBoxLayout(frame)
668 4 Katalina Oviedo
        frame_layout.setSpacing(0)
669 4 Katalina Oviedo
        frame_layout.setContentsMargins(0, 0, 0, 0)
670 4 Katalina Oviedo
        frame.setFixedSize(262, 94)
671 4 Katalina Oviedo
672 4 Katalina Oviedo
        icono_label = QLabel(frame)
673 4 Katalina Oviedo
        pixmap = QPixmap(icono_path).scaled(57, 57, Qt.KeepAspectRatio, Qt.SmoothTransformation)
674 4 Katalina Oviedo
        icono_label.setPixmap(pixmap)
675 4 Katalina Oviedo
        icono_label.setObjectName("icono_label")
676 4 Katalina Oviedo
        frame_layout.addWidget(icono_label)
677 4 Katalina Oviedo
678 4 Katalina Oviedo
        text_layout = QVBoxLayout()
679 4 Katalina Oviedo
        valor_label = QLabel(valor, frame)
680 4 Katalina Oviedo
        valor_label.setObjectName("valor_label")
681 4 Katalina Oviedo
        text_layout.addWidget(valor_label)
682 4 Katalina Oviedo
        frame_layout.addLayout(text_layout)
683 4 Katalina Oviedo
        text_layout.setSpacing(0)
684 4 Katalina Oviedo
        text_layout.setContentsMargins(0, 0, 0, 0)
685 4 Katalina Oviedo
686 4 Katalina Oviedo
        # Agregar el frame al layout
687 4 Katalina Oviedo
        layout.addWidget(frame, alignment=Qt.AlignHCenter)
688 4 Katalina Oviedo
689 4 Katalina Oviedo
        # Agregar el parámetro al layout
690 4 Katalina Oviedo
        parametro_label = QLabel(parametro, self)
691 4 Katalina Oviedo
        parametro_label.setAlignment(Qt.AlignCenter)
692 4 Katalina Oviedo
        parametro_label.setStyleSheet("color: white ; font-size: 21px")
693 4 Katalina Oviedo
        layout.addWidget(parametro_label)
694 4 Katalina Oviedo
695 4 Katalina Oviedo
        # Actualizar la etiqueta correspondiente según el parámetro
696 4 Katalina Oviedo
        if parametro.lower() == "temperatura":
697 4 Katalina Oviedo
            self.temp_label = valor_label
698 4 Katalina Oviedo
            self.parametro_temp_label = parametro_label
699 4 Katalina Oviedo
        elif parametro.lower() == "ph":
700 4 Katalina Oviedo
            self.ph_label = valor_label
701 4 Katalina Oviedo
            self.parametro_ph_label = parametro_label
702 4 Katalina Oviedo
        elif parametro.lower() == "luz":
703 4 Katalina Oviedo
            self.luz_label = valor_label
704 4 Katalina Oviedo
            self.parametro_luz_label = parametro_label
705 4 Katalina Oviedo
706 4 Katalina Oviedo
        return valor_label
707 4 Katalina Oviedo
708 4 Katalina Oviedo
    def cargar_estilos(self):
709 4 Katalina Oviedo
        with open("styles.css", "r") as f:
710 4 Katalina Oviedo
            css = f.read()
711 4 Katalina Oviedo
            self.setStyleSheet(css)
712 4 Katalina Oviedo
713 4 Katalina Oviedo
    def enviar_datos(self):
714 4 Katalina Oviedo
        """Recopila los datos ingresados en los inputs y los guarda."""
715 4 Katalina Oviedo
        datos = [input_widget.text() for input_widget in self.inputs]
716 4 Katalina Oviedo
717 4 Katalina Oviedo
        # Guardamos los rangos
718 4 Katalina Oviedo
        self.rangos = {
719 4 Katalina Oviedo
            'temperatura': {'min': float(datos[0]), 'max': float(datos[1])},
720 4 Katalina Oviedo
            'ph': {'min': float(datos[2]), 'max': float(datos[3])},
721 4 Katalina Oviedo
            'luz': {'min': float(datos[4]), 'max': float(datos[5])},
722 4 Katalina Oviedo
        }
723 4 Katalina Oviedo
724 4 Katalina Oviedo
app = QApplication(sys.argv)
725 4 Katalina Oviedo
ventana = AquaPiApp()
726 4 Katalina Oviedo
ventana.show()
727 4 Katalina Oviedo
sys.exit(app.exec())
728 4 Katalina Oviedo
</code></pre>
729 4 Katalina Oviedo
}}
730 5 Katalina Oviedo
731 5 Katalina Oviedo
{{collapse(Código servidor)
732 7 Katalina Oviedo
<pre><code class="ruby">
733 7 Katalina Oviedo
import serial
734 7 Katalina Oviedo
import time
735 7 Katalina Oviedo
import socket
736 7 Katalina Oviedo
import cv2
737 7 Katalina Oviedo
import threading
738 1 Bruno Amestica
739 7 Katalina Oviedo
# Configuración de la conexión serial con Arduino
740 1 Bruno Amestica
741 7 Katalina Oviedo
arduino_port = '/dev/ttyACM0'  # Cambia esto según tu puerto
742 7 Katalina Oviedo
baud_rate = 9600
743 7 Katalina Oviedo
try:
744 7 Katalina Oviedo
    ser = serial.Serial(arduino_port, baud_rate, timeout=0.1)
745 7 Katalina Oviedo
    print("Conexión serial establecida con Arduino en", arduino_port)
746 7 Katalina Oviedo
except Exception as e:
747 7 Katalina Oviedo
    print("Error al conectar con el Arduino:", e)
748 7 Katalina Oviedo
    exit()
749 7 Katalina Oviedo
# Configuración del servidor
750 1 Bruno Amestica
751 7 Katalina Oviedo
host = ''  # Escucha en todas las interfaces
752 7 Katalina Oviedo
port_sensor = 5560  # Puerto para datos del sensor
753 7 Katalina Oviedo
port_video = 5561   # Puerto para transmisión de video
754 7 Katalina Oviedo
# Función para procesar datos del Arduino
755 7 Katalina Oviedo
756 7 Katalina Oviedo
def process_data(data):
757 7 Katalina Oviedo
758 7 Katalina Oviedo
    try:
759 7 Katalina Oviedo
760 7 Katalina Oviedo
        temp, ph, light = map(float, data.split(","))
761 7 Katalina Oviedo
762 7 Katalina Oviedo
        return f"{temp:.2f}, {ph:.2f}, {light:.2f}"
763 7 Katalina Oviedo
764 7 Katalina Oviedo
    except ValueError:
765 7 Katalina Oviedo
766 7 Katalina Oviedo
        print("Error al procesar los datos recibidos:", data)
767 7 Katalina Oviedo
768 7 Katalina Oviedo
        return "Error en los datos"
769 7 Katalina Oviedo
770 7 Katalina Oviedo
# Función para manejar datos del sensor
771 7 Katalina Oviedo
772 7 Katalina Oviedo
def handle_sensor_data(conn):
773 7 Katalina Oviedo
774 7 Katalina Oviedo
    try:
775 7 Katalina Oviedo
776 7 Katalina Oviedo
        while True:
777 7 Katalina Oviedo
778 7 Katalina Oviedo
            if ser.in_waiting > 0:
779 7 Katalina Oviedo
780 7 Katalina Oviedo
                line = ser.readline().decode('utf-8').strip()
781 7 Katalina Oviedo
782 7 Katalina Oviedo
                sensor_data = process_data(line)
783 7 Katalina Oviedo
784 7 Katalina Oviedo
                conn.sendall(sensor_data.encode('utf-8'))
785 7 Katalina Oviedo
786 7 Katalina Oviedo
                time.sleep(0.1)  # Evita saturar la conexión
787 7 Katalina Oviedo
788 7 Katalina Oviedo
    except Exception as e:
789 7 Katalina Oviedo
790 7 Katalina Oviedo
        print("Error en la conexión del sensor:", e)
791 7 Katalina Oviedo
792 7 Katalina Oviedo
    finally:
793 7 Katalina Oviedo
794 7 Katalina Oviedo
        conn.close()
795 7 Katalina Oviedo
796 7 Katalina Oviedo
# Función para transmitir video
797 7 Katalina Oviedo
798 7 Katalina Oviedo
def handle_video_stream(conn):
799 7 Katalina Oviedo
800 7 Katalina Oviedo
    try:
801 7 Katalina Oviedo
802 7 Katalina Oviedo
        # Configuración de la cámara
803 7 Katalina Oviedo
804 7 Katalina Oviedo
        cap = cv2.VideoCapture(0)  # Usa la cámara de la Raspberry Pi
805 7 Katalina Oviedo
806 7 Katalina Oviedo
        cap.set(3, 640)  # Ancho del video
807 7 Katalina Oviedo
808 7 Katalina Oviedo
        cap.set(4, 480)  # Altura del video
809 7 Katalina Oviedo
810 7 Katalina Oviedo
        while cap.isOpened():
811 7 Katalina Oviedo
812 7 Katalina Oviedo
            ret, frame = cap.read()
813 7 Katalina Oviedo
814 7 Katalina Oviedo
            if not ret:
815 7 Katalina Oviedo
816 7 Katalina Oviedo
                break
817 7 Katalina Oviedo
818 7 Katalina Oviedo
            # Codificar el marco en formato JPEG
819 7 Katalina Oviedo
820 7 Katalina Oviedo
            _, buffer = cv2.imencode('.jpg', frame)
821 7 Katalina Oviedo
            frame_size = len(buffer)
822 7 Katalina Oviedo
            
823 7 Katalina Oviedo
            conn.sendall(frame_size.to_bytes(4,'big'))
824 7 Katalina Oviedo
            
825 7 Katalina Oviedo
            conn.sendall(buffer.tobytes())
826 7 Katalina Oviedo
827 7 Katalina Oviedo
    except Exception as e:
828 7 Katalina Oviedo
829 7 Katalina Oviedo
        print("Error en la transmisión de video:", e)
830 7 Katalina Oviedo
831 7 Katalina Oviedo
    finally:
832 7 Katalina Oviedo
833 7 Katalina Oviedo
        conn.close()
834 7 Katalina Oviedo
835 7 Katalina Oviedo
        cap.release()
836 7 Katalina Oviedo
837 7 Katalina Oviedo
# Configurar y aceptar conexiones
838 7 Katalina Oviedo
839 7 Katalina Oviedo
def setup_server(port):
840 7 Katalina Oviedo
841 7 Katalina Oviedo
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
842 7 Katalina Oviedo
843 7 Katalina Oviedo
    s.bind((host, port))
844 7 Katalina Oviedo
845 7 Katalina Oviedo
    s.listen(1)
846 7 Katalina Oviedo
847 7 Katalina Oviedo
    print(f"Servidor escuchando en el puerto {port}")
848 7 Katalina Oviedo
849 7 Katalina Oviedo
    return s
850 7 Katalina Oviedo
851 7 Katalina Oviedo
if __name__ == "__main__":
852 7 Katalina Oviedo
853 7 Katalina Oviedo
    # Servidor para datos del sensor
854 7 Katalina Oviedo
855 7 Katalina Oviedo
    sensor_server = setup_server(port_sensor)
856 7 Katalina Oviedo
857 7 Katalina Oviedo
    video_server = setup_server(port_video)
858 7 Katalina Oviedo
859 7 Katalina Oviedo
    while True:
860 7 Katalina Oviedo
861 7 Katalina Oviedo
        # Aceptar conexiones para ambos servicios
862 7 Katalina Oviedo
863 7 Katalina Oviedo
        sensor_conn, _ = sensor_server.accept()
864 7 Katalina Oviedo
865 7 Katalina Oviedo
        print("Cliente conectado para datos del sensor.")
866 7 Katalina Oviedo
867 7 Katalina Oviedo
        video_conn, _ = video_server.accept()
868 7 Katalina Oviedo
869 7 Katalina Oviedo
        print("Cliente conectado para transmisión de video.")
870 7 Katalina Oviedo
871 7 Katalina Oviedo
        # Crear hilos para manejar ambas conexiones
872 7 Katalina Oviedo
873 7 Katalina Oviedo
        threading.Thread(target=handle_sensor_data, args=(sensor_conn,)).start()
874 7 Katalina Oviedo
875 7 Katalina Oviedo
        threading.Thread(target=handle_video_stream, args=(video_conn,)).start()
876 6 Katalina Oviedo
</code></pre>
877 1 Bruno Amestica
}}