agrobot_base/AgroBase/AgroBase/Forms/frmInstancial.cs

499 lines
14 KiB
C#
Raw Normal View History

using AgroBase.Forms.IHM;
2024-07-05 18:59:05 +00:00
using AgroBase.Models;
using AgroBase.Services;
2023-10-09 16:06:23 +00:00
using System;
using System.Threading;
2023-08-31 11:50:53 +00:00
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AgroBase.Forms
{
public partial class frmInstancial : Form
{
public static AsyncTaskTimerModel tmrVarreduraDispositivos;
2023-08-31 11:50:53 +00:00
public static frmPrincipal frmPrincipal = new frmPrincipal();
public static frmJoystick frmJoystick = new frmJoystick();
public static frmIHM frmIHM = new frmIHM()
2023-08-31 11:50:53 +00:00
{
FormBorderStyle = FormBorderStyle.None,
//WindowState = FormWindowState.Maximized,
//TopMost = true
};
private static readonly SemaphoreSlim _startupGate =
new SemaphoreSlim(1, 1);
2025-11-06 16:18:32 +00:00
private static readonly SemaphoreSlim _shutdownGate =
new SemaphoreSlim(1, 1);
2026-03-05 02:03:31 +00:00
private static CancellationTokenSource _appCts;
private static bool _startupConcluido;
private static bool _shutdownIniciado;
2024-09-24 19:02:54 +00:00
private bool _allowClose;
public frmInstancial()
{
InitializeComponent();
2025-06-30 10:43:54 +00:00
/*
* O construtor precisa ser leve.
* Nada de abrir MQTT, Redis, Python, CAN, Livox ou operadores aqui.
* O WinForms ainda está criando handles e a aplicação ainda não
* possui lifecycle seguro para aguardar falhas.
*/
ThreadPool.SetMinThreads(
workerThreads: 50,
completionPortThreads: 50
);
}
2025-11-05 20:03:46 +00:00
private async void frmInstancial_Load(object sender, EventArgs e)
{
try
{
await InicializarServicosAsync();
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Load] Falha crítica na inicialização: " +
ex
);
try
{
MessageBox.Show(
"Falha ao inicializar o sistema:\n\n" +
ex.Message,
"AgroBase",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
catch { }
}
2023-08-31 11:50:53 +00:00
}
private void frmInstancial_Shown(object sender, EventArgs e)
{
try
2024-07-05 18:59:05 +00:00
{
if (!Variaveis.Producao)
{
frmIHM.FormClosing -= FrmIHM_FormClosing;
frmPrincipal.FormClosing -= frmPrincipal_FormClosing;
frmIHM.FormClosing += FrmIHM_FormClosing;
frmPrincipal.FormClosing += frmPrincipal_FormClosing;
frmPrincipal.Show();
}
else
{
frmIHM.FormClosing -= frmPrincipal_FormClosing;
frmIHM.FormClosing += frmPrincipal_FormClosing;
frmIHM.Show();
}
Hide();
2024-07-05 18:59:05 +00:00
}
catch (Exception ex)
2024-07-05 18:59:05 +00:00
{
Variaveis.MostrarLog(
"[frmInstancial.Shown] " + ex
);
2024-07-05 18:59:05 +00:00
}
2023-08-31 11:50:53 +00:00
}
2024-03-01 19:49:20 +00:00
private static async Task InicializarServicosAsync()
2024-07-05 18:59:05 +00:00
{
await _startupGate.WaitAsync()
.ConfigureAwait(false);
try
{
if (_startupConcluido)
return;
_shutdownIniciado = false;
Variaveis.Fechando = false;
_appCts?.Dispose();
_appCts = new CancellationTokenSource();
FuncoesGlobais.AddApplicationToStartup();
/*
* Ordem pensada para campo:
* 1. limpar processos externos velhos;
* 2. subir infraestrutura local;
* 3. subir comunicação MQTT/UDP;
* 4. iniciar rotinas e operadores;
* 5. iniciar varredura física.
*/
PythonService.EncerrarProcessos();
RedisService.Iniciar();
RedisService.LimparDadosIniciais();
GeneralJoystick.IniciarRotinas();
await Variaveis.IniciarMqttAsync(_appCts.Token)
.ConfigureAwait(false);
/*
* Hoje pode ficar comentado se UDP não for usado no rover,
* mas o método fica aqui no lugar certo do lifecycle.
*/
Variaveis.IniciarUDP();
APIService.IniciarRotinas();
VersionamentoService.IniciarRotinas();
LivoxManagerProcess.Start();
await VariaveisOperacao
.Operadores
.IniciarProcessamento(false)
.ConfigureAwait(false);
AudioAlertaService.SelecionarVoz(
VariaveisEquipamento.VozAlerta
);
IniciarTimerVarreduraDispositivos();
_startupConcluido = true;
Variaveis.MostrarLog(
"[frmInstancial] Inicialização concluída."
);
}
catch
{
await EncerrarProcessosInternoAsync(
sairProcesso: false
).ConfigureAwait(false);
throw;
}
finally
{
_startupGate.Release();
}
}
private static void IniciarTimerVarreduraDispositivos()
{
if (tmrVarreduraDispositivos != null)
return;
tmrVarreduraDispositivos =
new AsyncTaskTimerModel(
"tmrVarreduraDispositivos",
tmrVarreduraDispositivos_Tick,
SerialService.InteraloVerificacao,
null,
15000
);
tmrVarreduraDispositivos.Start();
}
private static async Task tmrVarreduraDispositivos_Tick()
{
if (Variaveis.Fechando)
return;
await SerialService
.RealizarVarreduraPortasUSB()
.ConfigureAwait(false);
2024-07-05 18:59:05 +00:00
}
/*
* Método mantido para compatibilidade com chamadas antigas.
* Não é mais chamado no Load, porque o startup correto é
* Variaveis.IniciarMqttAsync().
*/
2024-07-05 18:59:05 +00:00
private async void IniciarConexaoMqtt()
{
try
{
await Variaveis.IniciarMqttAsync();
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.IniciarConexaoMqtt] " +
ex.Message
);
}
2024-07-05 18:59:05 +00:00
}
public static Task EncerrarProcessos()
{
return EncerrarProcessos(sairProcesso: false);
}
public static async Task EncerrarProcessos(
bool sairProcesso)
{
await _shutdownGate.WaitAsync()
.ConfigureAwait(false);
try
{
if (_shutdownIniciado)
return;
_shutdownIniciado = true;
await EncerrarProcessosInternoAsync(
sairProcesso
).ConfigureAwait(false);
}
finally
{
_shutdownGate.Release();
}
}
private static async Task EncerrarProcessosInternoAsync(
bool sairProcesso)
2024-07-05 18:59:05 +00:00
{
Variaveis.Fechando = true;
2024-07-05 18:59:05 +00:00
try { _appCts?.Cancel(); } catch { }
2025-11-05 20:03:46 +00:00
/*
* Primeiro paramos timers para nenhuma rotina reentrar
* enquanto desmontamos serial, CAN, MQTT e processos.
*/
try
{
await AsyncTaskTimerModel
.PararTimersAsync()
.ConfigureAwait(false);
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao parar timers: " +
ex.Message
);
}
tmrVarreduraDispositivos = null;
2026-03-05 16:53:30 +00:00
try
{
await SerialService
.EncerrarAsync()
.ConfigureAwait(false);
}
catch (Exception ex)
2024-07-05 18:59:05 +00:00
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao encerrar SerialService: " +
ex.Message
);
2024-07-05 18:59:05 +00:00
}
/*
* Tenta avisar dispositivos antes de fechar CAN/MQTT/processos.
*/
try
2024-07-05 18:59:05 +00:00
{
foreach (var disp in Variaveis.DispositivosConectados)
{
try
{
disp.EnviarProtocolosConfiguracao(false);
disp.SalvarParametros(disp.Dados);
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao salvar " +
disp.Dispositivo + ": " + ex.Message
);
}
}
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao percorrer dispositivos: " +
ex.Message
);
2024-07-05 18:59:05 +00:00
}
/*
* uma janela curta para a fila CAN drenar.
* Usa DateTime aqui por compatibilidade com o padrão atual,
* mas sem travar indefinidamente.
*/
try
{
var timeout = DateTime.Now.AddSeconds(10);
while (!CanManager.CanService.FilaLiberada &&
DateTime.Now < timeout)
{
await Task.Delay(200)
.ConfigureAwait(false);
}
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro aguardando fila CAN: " +
ex.Message
);
}
2024-09-24 19:02:54 +00:00
try
{
LivoxManagerProcess.Dispose();
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao encerrar Livox: " +
ex.Message
);
}
try
{
PythonService.EncerrarProcessos();
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao encerrar Python: " +
ex.Message
);
}
2024-07-05 18:59:05 +00:00
try
{
Variaveis.StopUdpChannel();
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao encerrar UDP: " +
ex.Message
);
}
2024-09-24 19:02:54 +00:00
try
{
await GPSService
.EncerrarAsync()
.ConfigureAwait(false);
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao encerrar GPS: " +
ex.Message
);
}
2024-09-24 19:02:54 +00:00
try
{
await Variaveis
.EncerrarMqttAsync()
.ConfigureAwait(false);
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.Encerrar] Erro ao encerrar MQTT: " +
ex.Message
);
}
2024-07-05 18:59:05 +00:00
try
{
_appCts?.Dispose();
_appCts = null;
}
catch { }
_startupConcluido = false;
if (sairProcesso)
{
try
{
Environment.Exit(0);
}
catch { }
}
}
private async void frmPrincipal_FormClosing(
object sender,
FormClosingEventArgs e)
2024-07-05 18:59:05 +00:00
{
if (_allowClose)
return;
2024-07-05 18:59:05 +00:00
e.Cancel = true;
try
{
await EncerrarProcessos(
sairProcesso: false
);
_allowClose = true;
BeginInvoke(
new Action(() =>
{
try
{
Close();
}
catch { }
})
);
}
catch (Exception ex)
{
Variaveis.MostrarLog(
"[frmInstancial.FormClosing] " + ex
);
try
{
Environment.Exit(1);
}
catch { }
}
2024-07-05 18:59:05 +00:00
}
private void FrmIHM_FormClosing(
object sender,
FormClosingEventArgs e)
2024-10-04 13:11:23 +00:00
{
/*
* Em modo desenvolvimento, fechar a IHM apenas oculta a janela.
* Em produção, o evento de fechamento da IHM é associado ao
* frmPrincipal_FormClosing e faz shutdown real.
*/
2024-10-04 13:11:23 +00:00
e.Cancel = true;
frmIHM.Hide();
}
2023-08-31 11:50:53 +00:00
}
}