2026-03-10 19:56:55 +00:00
|
|
|
|
using OperationControl.Models;
|
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
using System.Windows.Threading;
|
|
|
|
|
|
using static AgroBase.Models.Enums;
|
|
|
|
|
|
|
|
|
|
|
|
namespace OperationControl.ViewModels.Views.Operacao
|
|
|
|
|
|
{
|
|
|
|
|
|
public class OperacaoBottomViewModel : INotifyPropertyChanged
|
|
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public ObservableCollection<AlertaModel> AlertasAtivos { get; } = new();
|
|
|
|
|
|
|
2026-03-10 19:56:55 +00:00
|
|
|
|
private AlertaModel _toastAlerta;
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public AlertaModel ToastAlerta
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _toastAlerta;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_toastAlerta == value) return;
|
|
|
|
|
|
_toastAlerta = value;
|
|
|
|
|
|
OnPropertyChanged(nameof(ToastAlerta));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-10 19:56:55 +00:00
|
|
|
|
private readonly DispatcherTimer _toastTimer;
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
|
|
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|
|
|
|
|
|
|
|
|
|
|
// EVENTO PARA O PAI TRATAR A NAVEGAÇÃO
|
|
|
|
|
|
public event Action<AlertaModel> SolicitarAbrirDiagnostico;
|
|
|
|
|
|
|
2026-03-10 19:56:55 +00:00
|
|
|
|
public OperacaoBottomViewModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
_toastTimer = new DispatcherTimer
|
|
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
Interval = TimeSpan.FromSeconds(6)
|
2026-03-10 19:56:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
_toastTimer.Tick += (s, e) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
_toastTimer.Stop();
|
|
|
|
|
|
ToastAlerta = null;
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SelecionarAlerta(AlertaModel alerta)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (alerta == null) return;
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
SolicitarAbrirDiagnostico?.Invoke(alerta);
|
2026-03-10 19:56:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public void LimparToast()
|
2026-03-10 19:56:55 +00:00
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
_toastTimer.Stop();
|
|
|
|
|
|
ToastAlerta = null;
|
2026-03-10 19:56:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void MostrarToast(AlertaModel alerta)
|
|
|
|
|
|
{
|
|
|
|
|
|
ToastAlerta = alerta;
|
|
|
|
|
|
_toastTimer.Stop();
|
|
|
|
|
|
_toastTimer.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public void AdicionarAlerta(string roverId, AgroBase.Models.Enums.T_Code modulo, SeveridadeAlerta severidade, string mensagem, string? modId = null)
|
2026-03-10 19:56:55 +00:00
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
App.Current.Dispatcher.BeginInvoke(new Action(() =>
|
2026-03-10 19:56:55 +00:00
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
var existente = AlertasAtivos.FirstOrDefault(x =>
|
|
|
|
|
|
x.Rover_ID == roverId &&
|
|
|
|
|
|
x.Modulo == modulo &&
|
|
|
|
|
|
x.Mod_ID == modId);
|
2026-03-10 19:56:55 +00:00
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
if (existente == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var alerta = new AlertaModel
|
|
|
|
|
|
{
|
|
|
|
|
|
Rover_ID = roverId,
|
|
|
|
|
|
Modulo = modulo,
|
|
|
|
|
|
Mod_ID = modId,
|
|
|
|
|
|
Severidade = severidade,
|
|
|
|
|
|
Mensagem = mensagem,
|
2026-03-12 14:00:43 +00:00
|
|
|
|
CriadoEm = DateTime.Now
|
2026-03-11 20:11:17 +00:00
|
|
|
|
};
|
2026-03-10 19:56:55 +00:00
|
|
|
|
|
|
|
|
|
|
AlertasAtivos.Add(alerta);
|
2026-03-11 20:11:17 +00:00
|
|
|
|
MostrarToast(alerta);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
existente.Mensagem = mensagem;
|
|
|
|
|
|
existente.Severidade = severidade;
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
2026-03-10 19:56:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public void RemoverAlerta(string roverId, AgroBase.Models.Enums.T_Code modulo, SeveridadeAlerta? severidade = null, string? modId = null)
|
2026-03-10 19:56:55 +00:00
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
App.Current.Dispatcher.BeginInvoke(new Action(() =>
|
2026-03-10 19:56:55 +00:00
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
var alerta = AlertasAtivos.FirstOrDefault(x =>
|
|
|
|
|
|
x.Rover_ID == roverId &&
|
|
|
|
|
|
x.Modulo == modulo &&
|
|
|
|
|
|
x.Mod_ID == modId &&
|
|
|
|
|
|
(severidade != null ? x.Severidade == severidade : true));
|
|
|
|
|
|
|
|
|
|
|
|
if (alerta != null)
|
|
|
|
|
|
AlertasAtivos.Remove(alerta);
|
|
|
|
|
|
}));
|
2026-03-10 19:56:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public void ClicarNoAlerta(AlertaModel alerta)
|
2026-03-10 19:56:55 +00:00
|
|
|
|
{
|
2026-03-11 20:11:17 +00:00
|
|
|
|
if (alerta == null) return;
|
|
|
|
|
|
SelecionarAlerta(alerta);
|
2026-03-10 19:56:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
public void ClicarNoToast()
|
|
|
|
|
|
{
|
|
|
|
|
|
var alerta = ToastAlerta;
|
|
|
|
|
|
if (alerta == null) return;
|
2026-03-10 19:56:55 +00:00
|
|
|
|
|
2026-03-11 20:11:17 +00:00
|
|
|
|
LimparToast();
|
|
|
|
|
|
SelecionarAlerta(alerta);
|
|
|
|
|
|
}
|
2026-03-10 19:56:55 +00:00
|
|
|
|
|
2026-03-12 01:00:21 +00:00
|
|
|
|
|
|
|
|
|
|
SeveridadeAlerta DeParaStatus(StatusModulo status, bool mandatorio)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (status)
|
|
|
|
|
|
{
|
|
|
|
|
|
case StatusModulo.Operante:
|
|
|
|
|
|
return SeveridadeAlerta.Info;
|
|
|
|
|
|
case StatusModulo.Alerta:
|
|
|
|
|
|
return SeveridadeAlerta.Warning;
|
|
|
|
|
|
case StatusModulo.Falha:
|
|
|
|
|
|
return mandatorio ? SeveridadeAlerta.Critical : SeveridadeAlerta.Error;
|
|
|
|
|
|
case StatusModulo.Conectado:
|
|
|
|
|
|
return mandatorio ? SeveridadeAlerta.Warning : SeveridadeAlerta.Info;
|
|
|
|
|
|
case StatusModulo.Desconectado:
|
|
|
|
|
|
return mandatorio ? SeveridadeAlerta.Critical : SeveridadeAlerta.Error;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return SeveridadeAlerta.Info;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-10 19:56:55 +00:00
|
|
|
|
public void AtualizarListaAlertasRoverSelecionado(string rover_id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == rover_id);
|
|
|
|
|
|
if (rover == null) return;
|
|
|
|
|
|
var dados_leitura = rover?.DadosLeitura;
|
|
|
|
|
|
if (dados_leitura?.ModulosSaude == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mod in dados_leitura.ModulosSaude)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (mod.saude_individual.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var ind in mod.saude_individual)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ind.status != StatusModulo.Operante)
|
|
|
|
|
|
{
|
|
|
|
|
|
AdicionarAlerta(rover.RoverId, mod.modulo, DeParaStatus(ind.status, ind.em_uso), string.Join(", ", ind.motivos), modId: ind.label);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoverAlerta(rover.RoverId, mod.modulo, modId: ind.label);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (mod.status != StatusModulo.Operante)
|
|
|
|
|
|
{
|
|
|
|
|
|
AdicionarAlerta(rover.RoverId, mod.modulo, DeParaStatus(mod.status, rover.ModulosMandatorios?.Any(x => x.Dispositivo == mod.modulo && x.Mandatorio) ?? false), string.Join(", ", mod.motivos));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoverAlerta(rover.RoverId, mod.modulo);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-12 01:00:21 +00:00
|
|
|
|
|
|
|
|
|
|
public void AtualizarListaAlertasTodosRovers()
|
|
|
|
|
|
{
|
|
|
|
|
|
App.Current.Dispatcher.BeginInvoke(new Action(() =>
|
|
|
|
|
|
{
|
2026-03-12 14:00:43 +00:00
|
|
|
|
string GerarChave(string roverId, T_Code modulo, string? modId) => $"{roverId}|{modulo}|{modId ?? ""}";
|
|
|
|
|
|
|
|
|
|
|
|
var agora = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
|
|
// 1) Gera os alertas que DEVERIAM existir neste momento
|
2026-03-12 01:00:21 +00:00
|
|
|
|
var alertasEsperados = new List<AlertaModel>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var rover in VariaveisControleOperacao.RoversNaRede)
|
|
|
|
|
|
{
|
|
|
|
|
|
var dadosLeitura = rover?.DadosLeitura;
|
|
|
|
|
|
if (dadosLeitura?.ModulosSaude == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mod in dadosLeitura.ModulosSaude)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (mod.saude_individual != null && mod.saude_individual.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var ind in mod.saude_individual)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ind.status == StatusModulo.Operante)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
alertasEsperados.Add(new AlertaModel
|
|
|
|
|
|
{
|
|
|
|
|
|
Rover_ID = rover.RoverId,
|
|
|
|
|
|
Modulo = mod.modulo,
|
|
|
|
|
|
Mod_ID = ind.label,
|
|
|
|
|
|
Severidade = DeParaStatus(ind.status, ind.em_uso),
|
2026-03-12 14:00:43 +00:00
|
|
|
|
Mensagem = string.Join(", ", ind.motivos ?? new List<string>())
|
|
|
|
|
|
// NÃO define Timestamp aqui ainda
|
2026-03-12 01:00:21 +00:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (mod.status == StatusModulo.Operante)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
bool mandatorio = rover.ModulosMandatorios?.Any(x =>
|
|
|
|
|
|
x.Dispositivo == mod.modulo && x.Mandatorio) ?? false;
|
|
|
|
|
|
|
|
|
|
|
|
alertasEsperados.Add(new AlertaModel
|
|
|
|
|
|
{
|
|
|
|
|
|
Rover_ID = rover.RoverId,
|
|
|
|
|
|
Modulo = mod.modulo,
|
|
|
|
|
|
Mod_ID = null,
|
|
|
|
|
|
Severidade = DeParaStatus(mod.status, mandatorio),
|
2026-03-12 14:00:43 +00:00
|
|
|
|
Mensagem = string.Join(", ", mod.motivos ?? new List<string>())
|
|
|
|
|
|
// NÃO define Timestamp aqui ainda
|
2026-03-12 01:00:21 +00:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var mapaEsperado = alertasEsperados.ToDictionary(
|
|
|
|
|
|
x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID),
|
|
|
|
|
|
x => x);
|
|
|
|
|
|
|
2026-03-12 14:00:43 +00:00
|
|
|
|
var mapaAtual = AlertasAtivos.ToDictionary(
|
|
|
|
|
|
x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID),
|
|
|
|
|
|
x => x);
|
|
|
|
|
|
|
|
|
|
|
|
// 2) Remove os alertas que não existem mais
|
|
|
|
|
|
var chavesParaRemover = mapaAtual.Keys
|
|
|
|
|
|
.Where(chave => !mapaEsperado.ContainsKey(chave))
|
|
|
|
|
|
.ToList();
|
2026-03-12 01:00:21 +00:00
|
|
|
|
|
2026-03-12 14:00:43 +00:00
|
|
|
|
foreach (var chave in chavesParaRemover)
|
2026-03-12 01:00:21 +00:00
|
|
|
|
{
|
2026-03-12 14:00:43 +00:00
|
|
|
|
var alertaRemover = mapaAtual[chave];
|
2026-03-12 01:00:21 +00:00
|
|
|
|
|
2026-03-12 14:00:43 +00:00
|
|
|
|
AlertasAtivos.Remove(alertaRemover);
|
|
|
|
|
|
|
|
|
|
|
|
if (ToastAlerta == alertaRemover)
|
|
|
|
|
|
LimparToast();
|
2026-03-12 01:00:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 14:00:43 +00:00
|
|
|
|
// 3) Adiciona novos ou atualiza os existentes
|
|
|
|
|
|
foreach (var kv in mapaEsperado)
|
2026-03-12 01:00:21 +00:00
|
|
|
|
{
|
2026-03-12 14:00:43 +00:00
|
|
|
|
var chave = kv.Key;
|
|
|
|
|
|
var esperado = kv.Value;
|
2026-03-12 01:00:21 +00:00
|
|
|
|
|
2026-03-12 14:00:43 +00:00
|
|
|
|
if (!mapaAtual.TryGetValue(chave, out var existente))
|
2026-03-12 01:00:21 +00:00
|
|
|
|
{
|
2026-03-12 14:00:43 +00:00
|
|
|
|
// Novo alerta
|
|
|
|
|
|
esperado.CriadoEm = agora;
|
2026-03-12 01:00:21 +00:00
|
|
|
|
AlertasAtivos.Add(esperado);
|
|
|
|
|
|
MostrarToast(esperado);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-12 14:00:43 +00:00
|
|
|
|
// Alerta já existia -> mantém Timestamp original
|
2026-03-12 01:00:21 +00:00
|
|
|
|
bool mudou =
|
|
|
|
|
|
existente.Mensagem != esperado.Mensagem ||
|
|
|
|
|
|
existente.Severidade != esperado.Severidade;
|
|
|
|
|
|
|
|
|
|
|
|
if (mudou)
|
|
|
|
|
|
{
|
2026-03-12 14:00:43 +00:00
|
|
|
|
existente.Mensagem = esperado.Mensagem;
|
|
|
|
|
|
existente.Severidade = esperado.Severidade;
|
|
|
|
|
|
|
|
|
|
|
|
// Se quiser que Timestamp represente "última mudança", mantém isso:
|
|
|
|
|
|
existente.AtualizadoEm = agora;
|
|
|
|
|
|
|
2026-03-12 01:00:21 +00:00
|
|
|
|
MostrarToast(existente);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-12 14:00:43 +00:00
|
|
|
|
|
|
|
|
|
|
var ordenado = AlertasAtivos.OrderByDescending(x => x.AtualizadoEm ?? x.CriadoEm).ToList();
|
|
|
|
|
|
AlertasAtivos.Clear();
|
|
|
|
|
|
foreach (var a in ordenado)
|
|
|
|
|
|
{
|
|
|
|
|
|
AlertasAtivos.Add(a);
|
|
|
|
|
|
}
|
2026-03-12 01:00:21 +00:00
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-10 19:56:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|