ajustes no main_async

This commit is contained in:
Diego Freitas 2026-06-30 15:59:40 -03:00
parent c5e5785718
commit d7f1689610
1 changed files with 147 additions and 15 deletions

View File

@ -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)