From d7f1689610317813018de5464c6a61e54b619c1f Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Tue, 30 Jun 2026 15:59:40 -0300 Subject: [PATCH] ajustes no main_async --- .../Python/Scripts/workers/main_async.py | 162 ++++++++++++++++-- 1 file changed, 147 insertions(+), 15 deletions(-) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/main_async.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/main_async.py index 4d9de4560..03e087da2 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/main_async.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/main_async.py @@ -1,7 +1,12 @@ import sys import io +import os import time +import signal +import traceback +import multiprocessing from multiprocessing import Process + from manager_worker.main import main as iniciar_manager_worker from health_worker.main import main as iniciar_health_worker from visual_worker.main import main as iniciar_visual_worker @@ -9,12 +14,41 @@ from camera_worker.main import main as iniciar_camera_worker from weed_worker.main import main as iniciar_weed_worker from shared.contexto_global_redis import ContextoGlobalRedis -#sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') -def main(): - ContextoGlobalRedis.reset_operacao() +SHUTDOWN = False - processos = [ + +def configurar_console(): + """ + Garante saída UTF-8 quando o processo é iniciado pelo C# com stdout/stderr redirecionados. + Evita problemas com emojis/logs em Windows. + """ + try: + if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8", errors="replace", line_buffering=True) + + if hasattr(sys.stderr, "reconfigure"): + sys.stderr.reconfigure(encoding="utf-8", errors="replace", line_buffering=True) + except Exception: + try: + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace", line_buffering=True) + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace", line_buffering=True) + except Exception: + pass + + +def log(msg): + print(msg, flush=True) + + +def tratar_sinal(sig, frame): + global SHUTDOWN + SHUTDOWN = True + log(f"⛔ Sinal recebido: {sig}. Encerrando núcleo de workers...") + + +def criar_processos(): + return [ Process(target=iniciar_manager_worker, name="ManagerWorker"), Process(target=iniciar_health_worker, name="HealthWorker"), Process(target=iniciar_visual_worker, name="VisualWorker"), @@ -22,20 +56,118 @@ def main(): Process(target=iniciar_weed_worker, name="WeedWorker"), ] + +def iniciar_processos(processos): for p in processos: - p.daemon = True + # Não usar daemon em campo. + # Se algum worker precisar criar subprocessos, daemon=True pode quebrar. + p.daemon = False p.start() - print(f"✅ Processo '{p.name}' iniciado com PID {p.pid}") + log(f"✅ Processo '{p.name}' iniciado com PID {p.pid}") + + +def encerrar_processos(processos, motivo="encerramento solicitado"): + log(f"⛔ Encerrando processos filhos: {motivo}") + + for p in processos: + try: + if p.is_alive(): + log(f"⛔ Terminando '{p.name}' PID={p.pid}") + p.terminate() + except Exception as ex: + log(f"⚠️ Erro ao terminar '{p.name}': {ex}") + + limite_s = 8 + inicio = time.time() + + while time.time() - inicio < limite_s: + vivos = [p for p in processos if p.is_alive()] + if not vivos: + break + time.sleep(0.2) + + for p in processos: + try: + if p.is_alive(): + log(f"🧨 Matando processo resistente '{p.name}' PID={p.pid}") + p.kill() + except Exception as ex: + log(f"⚠️ Erro ao matar '{p.name}': {ex}") + + for p in processos: + try: + p.join(timeout=2) + log(f"✅ Processo '{p.name}' encerrado. ExitCode={p.exitcode}") + except Exception as ex: + log(f"⚠️ Erro ao aguardar '{p.name}': {ex}") + + +def monitorar_processos(processos): + """ + Mantém o processo pai vivo e supervisiona os filhos. + Se algum worker morrer sozinho, finaliza todos e sai com erro. + O C# deve reiniciar o núcleo. + """ + ultimo_status = 0 + + while not SHUTDOWN: + mortos = [p for p in processos if p.exitcode is not None] + + if mortos: + for p in mortos: + log(f"❌ Worker morreu: {p.name}, PID={p.pid}, ExitCode={p.exitcode}") + + encerrar_processos(processos, motivo="worker morreu") + return 2 + + agora = time.time() + if agora - ultimo_status >= 10: + status = ", ".join([f"{p.name}=PID:{p.pid}/alive:{p.is_alive()}" for p in processos]) + log(f"💓 Supervisor ativo. {status}") + ultimo_status = agora + + time.sleep(1) + + encerrar_processos(processos, motivo="shutdown solicitado") + return 0 + + +def main(): + configurar_console() + + signal.signal(signal.SIGINT, tratar_sinal) try: - while True: - time.sleep(1) - except KeyboardInterrupt: - print("⛔ Encerrando processos...") - for p in processos: - p.terminate() - p.join() - print("✅ Todos os processos encerrados.") + signal.signal(signal.SIGTERM, tratar_sinal) + except Exception: + pass + + log("🚀 Iniciando supervisor dos workers Python...") + log(f"Python exe: {sys.executable}") + log(f"CWD: {os.getcwd()}") + log(f"Args: {sys.argv}") + + ContextoGlobalRedis.reset_operacao() + + processos = criar_processos() + iniciar_processos(processos) + + exit_code = monitorar_processos(processos) + + log(f"✅ Supervisor finalizado. ExitCode={exit_code}") + return exit_code + if __name__ == "__main__": - main() + multiprocessing.freeze_support() + + try: + codigo = main() + sys.exit(codigo) + except KeyboardInterrupt: + log("⛔ KeyboardInterrupt no processo pai.") + sys.exit(0) + except Exception: + log("💥 ERRO FATAL NO PROCESSO PAI main_async.py") + traceback.print_exc() + sys.exit(1)