From cdbcb8f1d72a243d2ae205abe4adb9bcce349f0a Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Mon, 8 Dec 2025 14:39:22 -0300 Subject: [PATCH] adicionado modulo can no health worker --- .../workers/health_worker/modulos/can.py | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/can.py diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/can.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/can.py new file mode 100644 index 000000000..e458000cb --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/can.py @@ -0,0 +1,118 @@ +import time +from shared.contexto_global_redis import ContextoGlobalRedis +from shared.enums import StatusModulo, T_Code +from health_worker.modulos.base import ModuloDiagnosticoBase + +class ModuloCAN(ModuloDiagnosticoBase): + def __init__(self): + self.t_code = T_Code.Can + self.nome = "CAN" + self.timeout = 5 + + def atualizar_saude(self): + try: + now = time.perf_counter() + m = ContextoGlobalRedis.get_modulo(self.t_code) or {} + + SAUDE_MIN_ALERTA = 80 + PESO_POR_ERRO = 10 # cada erro pode pesar até 10% + PESO_MAX_ERROS = 40 # máximo 40% de penalização vinda só de erros críticos + JANELA_RESET_EC = 600.0 # 10 minutos + + conectado = bool(m.get("conectado", False)) + inicado = bool(m.get("iniciado", False)) + executando = bool(m.get("executando", False)) + erros_criticos = int(m.get("erros_criticos", 0)) + + # períodos-alvo (segundos) — se vier 0/None, usa fallback prudente + tb_tx = float(m.get("tempo_base_tx", 1.0)) or 1.0 + tb_rx = float(m.get("tempo_base_rx", 1.0)) or 1.0 + + # timestamps monotônicos (segundos) + last_tx = float(m.get("last_tx", 0.0)) + last_rx = float(m.get("last_rx", 0.0)) + last_ec = float(m.get("last_ec", 0.0)) + + age_tx = max(0.0, now - last_tx) + age_rx = max(0.0, now - last_rx) + age_ec = max(0.0, now - last_ec) + + # tempo-limite pra considerar "morto": 3 períodos ou 3s (o que for maior) + timeout_rx = max(30.0, 3.0 * tb_rx) + timeout_tx = max(30.0, 3.0 * tb_tx) # informativo (RX é vital) + + saude = 100 + motivos = [] + + if not conectado: + saude = 0 + motivos.append("desconectado") + else: + if not inicado: + saude -= 50 + motivos.append("não iniciado") + + if executando: + # Penalização por atraso relativo ao período-alvo + # Ex.: se tb_rx=1s e age_rx=1.5s → atraso_rel=0.5 → penaliza 0.5*70=35% + atraso_rel_rx = max(0.0, (age_rx / tb_rx) - 1.0) + atraso_rel_tx = max(0.0, (age_tx / tb_tx) - 1.0) + + p_rx = int(min(round(atraso_rel_rx * 60), 60)) # RX pesa mais (até 70%) + p_tx = int(min(round(atraso_rel_tx * 40), 40)) # TX pesa menos (até 40%) + + if p_rx > 0: + saude -= p_rx + motivos.append(f"RX atrasado {age_rx:.2f}s (-{p_rx}%)") + if p_tx > 0: + saude -= p_tx + motivos.append(f"TX atrasado {age_tx:.2f}s (-{p_tx}%)") + + if erros_criticos > 0 and age_ec <= JANELA_RESET_EC: + # penalidade base cresce com o número de erros, limitada pelo teto + penalidade_base = min(erros_criticos * PESO_POR_ERRO, PESO_MAX_ERROS) + + # fator de recência: 1 se acabou de acontecer, 0 se está no limite da janela + fator_recencia = max(0.0, 1.0 - (age_ec / JANELA_RESET_EC)) + + p_ec = int(round(penalidade_base * fator_recencia)) + + if p_ec > 0: + saude -= p_ec + motivos.append(f"{erros_criticos} erros críticos, último há {age_ec:.0f}s (-{p_ec}%)") + + saude = max(saude, 0) + + status = StatusModulo.OPERANTE + if not conectado: + status = StatusModulo.DESCONECTADO + elif saude <= 0: + status = StatusModulo.FALHA + elif saude < SAUDE_MIN_ALERTA: + status = StatusModulo.ALERTA + + payload = { + "conectado": conectado, + "status": status.value, + "saude": saude, + "motivos": motivos, + "saude_individual": [], + "condicoes_operacionais": [], + "detalhes": { + "age_rx_s": age_rx, + "age_tx_s": age_tx, + "age_ec_s": age_ec, + "timeout_rx_s": timeout_rx, + "timeout_tx_s": timeout_tx, + "periodo_alvo_rx_s": tb_rx, + "periodo_alvo_tx_s": tb_tx, + }, + } + + ContextoGlobalRedis.atualizar_ctx_dict( + ContextoGlobalRedis.ModKey(self.t_code), + saude=payload + ) + except Exception as e: + print(f"Erro ao atualizar saude do modulo {self.t_code.name}: {e}") +