using System.Windows; using OperationControl.Controls; using OperationControl.Models; using System.Windows.Controls; using System.Windows.Media; using LibVLCSharp.Shared; using System.Windows.Input; using System.IO; using MediaPlayer = LibVLCSharp.Shared.MediaPlayer; using ComboBox = System.Windows.Controls.ComboBox; using Application = System.Windows.Application; using MessageBox = System.Windows.MessageBox; using Brush = System.Windows.Media.Brush; using Brushes = System.Windows.Media.Brushes; using Label = System.Windows.Controls.Label; using Newtonsoft.Json.Linq; using AgroBase.Models.Operadores; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Threading; namespace OperationControl.Windows { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window, INotifyPropertyChanged { private LibVLC _libVLC; private MediaPlayer _playerFront; private MediaPlayer _playerWeed; private Media _mediaFront; private Media _mediaWeed; public MainWindow() { InitializeComponent(); cmbDir_Direcao.Items.Clear(); cmbDir_Direcao.Items.Add("Parado"); cmbDir_Direcao.Items.Add("Direita"); cmbDir_Direcao.Items.Add("Esquerda"); cmbDir_Direcao.SelectedIndex = 0; cmbDir_Movimento.Items.Clear(); foreach (var m in Enum.GetNames(typeof(AgroBase.Models.Enums.TipoMovimentoDirecional))) cmbDir_Movimento.Items.Add(m); cmbDir_Movimento.SelectedIndex = 0; cmbNpc_CoolerModo.Items.Clear(); cmbNpc_CoolerModo.Items.Add("Automático"); cmbNpc_CoolerModo.Items.Add("Manual"); cmbNpc_CoolerModo.SelectedIndex = 0; AtualizarListaDispositivos(new List() { VariaveisControleOperacao.SelectedRoverId }); graficoAcompanhamento = new GraficoModel(pltSaude_Acompanhamento); DataContext = this; //DataContext = this; // atualizar as caixas //BBoxes3D.SetBboxes(new[] //{ // new AgroBase.Models.LivoxBboxModel{ cx=1.2, cy=0.5, cz=0.4, d=0.6, w=0.4, h=0.8 }, // new AgroBase.Models.LivoxBboxModel{ cx=3.0, cy=-0.7, cz=0.6, d=0.8, w=0.5, h=1.2 }, //}); // Opções focadas em baixa latência _libVLC = new LibVLC( "--network-caching=1000", // ms (ajusta depois) "--clock-jitter=0", "--clock-synchro=0" ); // Player da câmera frontal _playerFront = new MediaPlayer(_libVLC); VideoFront.MediaPlayer = _playerFront; _playerWeed = new MediaPlayer(_libVLC); VideoWeed.MediaPlayer = _playerWeed; Loaded += MainWindow_Loaded; Closed += MainWindow_Closed; _toastTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(6) // tempo do toast na tela }; _toastTimer.Tick += (s, e) => { _toastTimer.Stop(); ToastAlerta = null; }; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { } private void MainWindow_Closed(object? sender, EventArgs e) { PararStreamCameraFrontal(true); PararStreamCameraErvas(true); _libVLC?.Dispose(); } #region DISPOSITIVOS public void AtualizarListaDispositivos(List dispositivos) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { cmbDispositivo.Items.Clear(); foreach (var d in dispositivos) { cmbDispositivo.Items.Add(d); if (!MAP.markers.Added(d)) { if (d == VariaveisControleOperacao.BaseMarkerID) { MAP.markers.AddMarker( d, true, lat: Variaveis.GpsService?.UltimaLeitura?.Latitude ?? 0, lon: Variaveis.GpsService?.UltimaLeitura?.Longitude ?? 0, heading: Variaveis.GpsService?.UltimaLeitura?.OrientacaoReal ?? 0, label: "Base" ); } else { var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == d); if (rover != null) { MAP.markers.AddMarker( d, false, lat: rover.DadosLeitura?.Gnss?.Latitude ?? 0, lon: rover.DadosLeitura?.Gnss?.Longitude ?? 0, heading: rover.DadosLeitura?.Gnss?.OrientacaoReal ?? 0 ); } } } } int idx_selected = dispositivos.IndexOf(VariaveisControleOperacao.SelectedRoverId); if (idx_selected > -1) cmbDispositivo.SelectedIndex = idx_selected; } catch (Exception exUi) { Variaveis.MostrarLog($"Erro ao atualizar UI lista dispositivos: {exUi.Message}"); } })); } private void cmbDispositivo_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { var selecionado = ((ComboBox)sender).SelectedItem; if (selecionado == null) return; bool dados_base = selecionado == VariaveisControleOperacao.BaseMarkerID; if (dados_base && !VariaveisControleOperacao.BaseEmFoco) { PararStreamCameraFrontal(); PararStreamCameraErvas(); } VariaveisControleOperacao.SelectedRoverId = selecionado.ToString(); MAP.markers.SetMarkerFocused(selecionado.ToString(), true); AtualizarDadosTela_Telemetria(selecionado.ToString()); brdResumo.IsEnabled = !dados_base; brdDiagnostico.IsEnabled = !dados_base; brdStatusModulo.IsEnabled = !dados_base; brdControle.IsEnabled = !dados_base; brdAjustesOperacao.IsEnabled = !dados_base; if (!dados_base) { btnParametros_Atualizar_Click(sender, e); IniciarStreamCameraFrontal(); IniciarStreamCameraErvas(); } } public void LimparDadosTela_Telemetria(string rover_id) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { if (!string.IsNullOrEmpty(rover_id)) { //MAP.markers.RemoveMarker(rover_id); //cmbDispositivo.Items.Remove(rover_id); } else { //cmbDispositivo.SelectedIndex = 0; } } catch (Exception exUi) { Variaveis.MostrarLog($"Erro ao atualizar UI telemetria (clear): {exUi.Message}"); } })); } public void AtualizarDadosTela_Telemetria(string rover_id) { bool dados_base = rover_id == VariaveisControleOperacao.BaseMarkerID; var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == rover_id); var dados_leitura = rover?.DadosLeitura; if (dados_base) { MAP.markers.UpdateMarkerPosition(rover_id, lat: Variaveis.GpsService?.UltimaLeitura?.Latitude, lon: Variaveis.GpsService?.UltimaLeitura?.Longitude, heading: Variaveis.GpsService?.UltimaLeitura?.OrientacaoReal); } else if (dados_leitura != null) { var simulacao = dados_leitura.Controle?.SimulacaoMPC?.Select(x => new double[] { x.longitude, x.latitude })?.ToList(); MAP.markers.UpdateMarkerPosition(rover_id, lat: dados_leitura.Gnss?.Latitude, lon: dados_leitura.Gnss?.Longitude, heading: dados_leitura.Gnss?.OrientacaoReal, predict: simulacao); MAP.markers.UpdateMarkerInfo(rover_id, status: dados_leitura.Operacao?.Status ?? AgroBase.Models.Enums.StatusOperacao.NaoIniciado); } if (rover != null && dados_leitura?.ModulosSaude != null) { SeveridadeAlerta DeParaStatus(AgroBase.Models.Enums.StatusModulo status, bool mandatorio) { switch (status) { case AgroBase.Models.Enums.StatusModulo.Operante: return SeveridadeAlerta.Info; case AgroBase.Models.Enums.StatusModulo.Alerta: return SeveridadeAlerta.Warning; case AgroBase.Models.Enums.StatusModulo.Falha: return (mandatorio) ? SeveridadeAlerta.Critical : SeveridadeAlerta.Error; case AgroBase.Models.Enums.StatusModulo.Conectado: return (mandatorio) ? SeveridadeAlerta.Warning : SeveridadeAlerta.Info; case AgroBase.Models.Enums.StatusModulo.Desconectado: return (mandatorio) ? SeveridadeAlerta.Critical : SeveridadeAlerta.Error; default: return SeveridadeAlerta.Info; } } foreach (var mod in dados_leitura.ModulosSaude) { if (mod.saude_individual.Any()) { foreach (var ind in mod.saude_individual) { if (ind.status != AgroBase.Models.Enums.StatusModulo.Operante) { AdicionarAlerta(rover.RoverId, mod.modulo, DeParaStatus(ind.status, ind.em_uso), string.Join(", ", ind.motivos), Mod_ID: ind.label); } else { RemoverAlerta(rover.RoverId, mod.modulo, Mod_ID: ind.label); } } } else { if (mod.status != AgroBase.Models.Enums.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); } } } } if (rover_id != VariaveisControleOperacao.SelectedRoverId) return; Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { if (rover_id == VariaveisControleOperacao.BaseMarkerID) { Variaveis.MostrarLog($"Atualizando dados da tela para a base"); lblDispositivos_Status.Content = VariaveisControleOperacao.StatusBase.ToString(); AplicarStatusModuloNaLabel(lblDispositivos_Status, VariaveisControleOperacao.StatusBase, true, false); AtualizarDadosTela_GNSS(Variaveis.GpsService?.UltimaLeitura?.ToGnssModel() ?? new AgroBase.Models.Operacoes.OperacaoParametrosDadosGNSSModel(), marker_id: VariaveisControleOperacao.BaseMarkerID); AtualizarDadosTela_Heading(Variaveis.GpsService?.UltimaLeitura?.AnguloCarroDefinido ?? 0); } else { if (dados_leitura == null) return; Variaveis.MostrarLog($"Atualizando dados da tela para o dispositivo {rover_id}"); lblDispositivos_Status.Content = (rover?.DadosLeitura?.StatusRover ?? AgroBase.Models.Enums.StatusModulo.Desconectado).ToString(); AplicarStatusModuloNaLabel(lblDispositivos_Status, rover?.DadosLeitura?.StatusRover ?? AgroBase.Models.Enums.StatusModulo.Desconectado, true, false); AtualizarDadosTela_Diagnosticos(rover); AtualizarDadosTela_Resumo_Conexao(dados_leitura); if (dados_leitura.Trajetoria != null) AtualizarDadosTela_Resumo(dados_leitura); if (dados_leitura.Gnss != null) AtualizarDadosTela_GNSS(dados_leitura.Gnss, marker_id: rover_id); if (dados_leitura.Controle != null) AtualizarDadosTela_Controle(dados_leitura.Controle, dados_leitura.Direcional.AnguloMedio, dados_leitura.Movimentacao.VelocidadeMediaMs, dados_leitura.Operacao.Emergencia, !dados_leitura.Operacao.Iniciada, dados_leitura.Operacao.Status == AgroBase.Models.Enums.StatusOperacao.Calibrando); //if (rover.Value.Controle != null) // AtualizarDadosTela_ParametrosOperacao(rover.Value.Controle); if (dados_leitura.Trajetoria != null) AtualizarDadosTela_Mapa(dados_leitura.Trajetoria); if (dados_leitura.Gnss != null) AtualizarDadosTela_Heading(dados_leitura.Gnss.OrientacaoReal, dados_leitura.Operacao.Modo == AgroBase.Models.Enums.ModoOperacao.Manual ? double.NaN : dados_leitura.Trajetoria.AnguloCaminho); if (dados_leitura.Imu != null) AtualizarDadosTela_IMU(dados_leitura.Imu.InclinacaoLateral, dados_leitura.Imu.InclinacaoFrontal); if (rover.Controle != null) AtualizarDadosTela_Pulverizador(dados_leitura); if (dados_leitura.LivoxLidar != null) AtualizarDadostela_LIDAR(dados_leitura.LivoxLidar.bboxes); } } catch (Exception exUi) { Variaveis.MostrarLog($"Erro ao atualizar UI telemetria (set): {exUi.Message}"); } })); } #endregion #region RESUMO OPERACIONAL public void AtualizarDadosTela_Resumo(AgroBase.Models.Operacoes.OperacaoParametrosDadosModel dados) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { pgbResumo_RuaAtual.Value = dados.Trajetoria.PercentualRuaAtual; lblResumo_RuaAtual.Content = $"{dados.Trajetoria.CorredorAtual.DistanciaPercorridaCorredor:0.00} m / {dados.Trajetoria.CorredorAtual.DistanciaTotal:0.00} m ({dados.Trajetoria.CorredorAtual.Idx + 1})"; pgbResumo_AreaTotal.Value = dados.Trajetoria.PercentualOperacao; lblResumo_AreaTotal.Content = $"{dados.Trajetoria.DistanciaPercorrida:0.00} m / {dados.Trajetoria.DistanciaTotal:0.00} m"; pgbResumo_Bateria.Value = dados.Bateria.PercentualBateria; lblResumo_Bateria.Content = $"{dados.Bateria.TensaoInstantanea:0.00} V ({TimeSpan.FromMinutes(dados.Bateria.TempoEstimadoRestanteMinutos).ToString(@"dd\.hh\:mm\:ss")} {dados.Bateria.DistanciaEstimadaRestanteMetros:0.00} m)"; pgbResumo_Herbicida.Value = dados.Atuador.PercentualReservatorio; lblResumo_Herbicida.Content = $"{dados.Atuador.VolumeReservatorioL:0.00} L ({TimeSpan.FromMinutes(dados.Atuador.TempoEstimadoRestanteMinutos).ToString(@"hh\:mm\:ss")} {dados.Atuador.DistanciaEstimadaRestanteMetros:0.00} m)"; pgbResumo_Infestacao.Value = dados.Atuador.PercentualErvasTerreno; lblResumo_Infestacao.Content = $"{dados.Atuador.HerbicidaPorAtuacaoMl:0.00} mL/atuação"; lblResumo_OperacaoLiberada.Content = $"Operação " + (dados.Operacao.Liberada ? "🔓 Liberada" : "🔒 Bloqueada"); lblResumo_OperacaoModo.Content = $"Modo {dados.Operacao.Modo}"; lblResumo_OperacaoStatus.Content = $"Status {dados.Operacao.Status}" + (dados.Operacao.Status == AgroBase.Models.Enums.StatusOperacao.Aguardando ? $" ({dados.Operacao.TempoAguardandoSegs:0})" : ""); lblResumo_StatusCarro.Content = $"Carro {dados.Trajetoria.StatusCarro}"; lblResumo_TemperaturaGeral.Content = $"Temperatura {(dados.PerformanceNcp.CPU_temp ?? 0):0.00} °C"; lblResumo_OperacaoDuracao.Content = $"Duração {TimeSpan.FromSeconds(dados.Operacao.TempoDecorridoSegs).ToString(@"hh\:mm\:ss")} / " + (string.IsNullOrEmpty(dados.Trajetoria.TempoEstimadoOperacao) ? "00:00:00" : dados.Trajetoria.TempoEstimadoOperacao); } catch (Exception exUi) { Variaveis.MostrarLog($"Erro ao atualizar UI Resumo: {exUi.Message}"); } })); } public void AtualizarDadosTela_Resumo_Conexao(AgroBase.Models.Operacoes.OperacaoParametrosDadosModel dados) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { var conexao = dados.ModulosSaude?.FirstOrDefault(x => x.modulo == AgroBase.Models.Enums.T_Code.Ipb); double conexao_saude = conexao?.saude ?? 0; double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value() ?? 0.0; double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value() ?? 0.0; double bwTot = conexao?.detalhes?["bw_total_mbps"]?.Value() ?? 0; pgbResumo_Conexao.Value = conexao_saude; lblResumo_Conexao.Content = $"{conexao_latencia:0.00} ms {conexao_perda:0.00}% {bwTot:0.00} Mbps"; } catch (Exception exUi) { Variaveis.MostrarLog($"Erro ao atualizar UI Resumo_Conexao: {exUi.Message}"); } })); } #endregion #region DIAGNOSTICOS private Label _labelModuloSelecionado; private GraficoModel graficoAcompanhamento; private void AtualizarDadosTela_Diagnosticos(AgroBase.Models.Operacoes.OperacaoParametrosModel dados) { lblDiagnosticos_Impedimento.Text = "Impedimento: " + (string.IsNullOrEmpty(dados.DadosLeitura.Operacao.ImpedimentoErro) ? "Nenhum" : dados.DadosLeitura.Operacao.ImpedimentoErro.Replace("\n\n", "; ")); // Canvas que está dentro do Border var canvas = brdDiagnostico.Child as Canvas; if (canvas == null) return; // Percorre apenas labels dentro do Canvas com o prefixo desejado foreach (var lbl in canvas.Children.OfType