408 lines
12 KiB
C#
408 lines
12 KiB
C#
using AgroBase.Forms.IHM;
|
|
using AgroBase.Models;
|
|
using AgroBase.Services;
|
|
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace AgroBase.Forms
|
|
{
|
|
public partial class frmInstancial : Form
|
|
{
|
|
public static AsyncTaskTimerModel tmrVarreduraDispositivos;
|
|
|
|
public static frmPrincipal frmPrincipal = new frmPrincipal();
|
|
public static frmJoystick frmJoystick = new frmJoystick();
|
|
public static frmIHM frmIHM = new frmIHM()
|
|
{
|
|
FormBorderStyle = FormBorderStyle.None,
|
|
//WindowState = FormWindowState.Maximized,
|
|
TopMost = true
|
|
};
|
|
|
|
private static readonly SemaphoreSlim _startupGate = new SemaphoreSlim(1, 1);
|
|
|
|
private static readonly SemaphoreSlim _shutdownGate = new SemaphoreSlim(1, 1);
|
|
|
|
private static CancellationTokenSource _appCts;
|
|
|
|
private static bool _startupConcluido;
|
|
private static bool _shutdownIniciado;
|
|
|
|
private bool _allowClose;
|
|
|
|
public frmInstancial()
|
|
{
|
|
InitializeComponent();
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
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 { }
|
|
}
|
|
}
|
|
|
|
private void frmInstancial_Shown(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (Variaveis.UsarIHM)
|
|
{
|
|
frmIHM.FormClosing -= frmPrincipal_FormClosing;
|
|
frmIHM.FormClosing += frmPrincipal_FormClosing;
|
|
|
|
frmIHM.Show();
|
|
}
|
|
else
|
|
{
|
|
frmIHM.FormClosing -= FrmIHM_FormClosing;
|
|
frmPrincipal.FormClosing -= frmPrincipal_FormClosing;
|
|
|
|
frmIHM.FormClosing += FrmIHM_FormClosing;
|
|
frmPrincipal.FormClosing += frmPrincipal_FormClosing;
|
|
|
|
frmPrincipal.Show();
|
|
}
|
|
|
|
Hide();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[frmInstancial.Shown] " + ex);
|
|
}
|
|
}
|
|
|
|
private static async Task InicializarServicosAsync()
|
|
{
|
|
await _startupGate.WaitAsync().ConfigureAwait(false);
|
|
|
|
try
|
|
{
|
|
if (_startupConcluido)
|
|
return;
|
|
|
|
Variaveis.OperacaoEmAndamento = OperacaoModel.CarregarParametrosOperacaoPadrao(Enums.ModoOperacao.NaoDefinido, true);
|
|
|
|
_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();
|
|
|
|
await APIService.IniciarRotinasAsync();
|
|
VersionamentoService.IniciarRotinas();
|
|
|
|
//LivoxManagerProcess.Start();
|
|
|
|
await VariaveisOperacao.Operadores.IniciarProcessamento(Variaveis.IniciarWorkers).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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
Variaveis.Fechando = true;
|
|
|
|
try { _appCts?.Cancel(); } catch { }
|
|
|
|
/*
|
|
* 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;
|
|
|
|
try
|
|
{
|
|
await SerialService.EncerrarAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[frmInstancial.Encerrar] Erro ao encerrar SerialService: " + ex.Message);
|
|
}
|
|
|
|
/*
|
|
* Tenta avisar dispositivos antes de fechar CAN/MQTT/processos.
|
|
*/
|
|
try
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Dá uma janela curta para a fila CAN drenar.
|
|
* Usa DateTime aqui só 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
try
|
|
{
|
|
Variaveis.StopUdpChannel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[frmInstancial.Encerrar] Erro ao encerrar UDP: " + ex.Message);
|
|
}
|
|
|
|
try
|
|
{
|
|
await GPSService.EncerrarAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[frmInstancial.Encerrar] Erro ao encerrar GPS: " + ex.Message);
|
|
}
|
|
|
|
try
|
|
{
|
|
await Variaveis.EncerrarMqttAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[frmInstancial.Encerrar] Erro ao encerrar MQTT: " + ex.Message);
|
|
}
|
|
|
|
try
|
|
{
|
|
VariaveisOperacao.Operadores.Encerrar();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Variaveis.MostrarLog("[frmInstancial.Encerrar] Erro ao encerrar OperadoresService: " + ex.Message);
|
|
}
|
|
|
|
try
|
|
{
|
|
_appCts?.Dispose();
|
|
_appCts = null;
|
|
}
|
|
catch { }
|
|
|
|
_startupConcluido = false;
|
|
|
|
if (sairProcesso)
|
|
{
|
|
try
|
|
{
|
|
Environment.Exit(0);
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
|
|
private async void frmPrincipal_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if (_allowClose)
|
|
return;
|
|
|
|
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(Variaveis.ExitShutdownCode);
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
|
|
private void FrmIHM_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
/*
|
|
* 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.
|
|
*/
|
|
//e.Cancel = true;
|
|
//frmIHM.Hide();
|
|
}
|
|
}
|
|
}
|