using System.Windows; using ScottPlot; // Plot using ScottPlot.Plottables; // ISignal / IScatter using System.Timers; using Colors = ScottPlot.Colors; // Timer using System.Windows.Threading; using OperationControl.Controls; using System.Diagnostics; using OperationControl.Models; using AgroBase.Models.Components; using OperationControl.Services; using System.Windows.Controls; using System.Runtime.CompilerServices; using System.Windows.Media; using DotSpatial.Projections.Transforms; using System; 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 AgroBase.Models.Operadores; namespace OperationControl.Windows { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { // buffers (X = tempo em segundos) private readonly List xs = new(); private readonly List ysCorr = new(); // Corrente (A) private readonly List ysVolt = new(); // Tensão (V) private readonly List ysTemp = new(); // Temperatura (°C) private readonly List ysRpm = new(); // RPM private readonly System.Timers.Timer tmr = new(1000) { AutoReset = true }; // 10 Hz private double t = 0; private readonly Random rng = new(); DispatcherTimer _clock = new DispatcherTimer(); Random _rand = new Random(); private LibVLC _libVLC; private MediaPlayer _playerFront; private MediaPlayer _playerWeed; private Media _mediaFront; private Media _mediaWeed; private Label _labelModuloSelecionado; public MainWindow() { InitializeComponent(); AtualizarListaDispositivos(new List() { VariaveisControleOperacao.SelectedRoverId }); // Título / eixos Plot.Plot.Title("MOV ET – Corrente / Tensão / Temp / RPM (últimos 60s)"); Plot.Plot.Axes.Bottom.Label.Text = "Tempo (s)"; Plot.Plot.Axes.Left.Label.Text = "Unidades (A, V, °C, RPM)"; // inicializa com um ponto para cada série PushSample(initial: true); // legenda Plot.Plot.ShowLegend(Alignment.UpperRight); // loop tmr.Elapsed += (_, __) => Dispatcher.Invoke(() => PushSample()); tmr.Start(); 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; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { } private void MainWindow_Closed(object? sender, EventArgs e) { PararStreamCameraFrontal(true); PararStreamCameraErvas(true); _libVLC?.Dispose(); } private void PushSample(bool initial = false) { // avança tempo if (!initial) t += 1; // 10 Hz xs.Add(t); // ------- MOCK de dados plausíveis ------- // Corrente (A): 3–12 A com ruído double corr = 8 + 2 * Math.Sin(t * 0.7) + (rng.NextDouble() - 0.5); corr = Math.Clamp(corr, 0, 30); // Tensão (V): 36–42 V, ripple leve e queda lenta double volt = 40.5 + 0.6 * Math.Sin(t * 0.15) - 0.002 * t; volt = Math.Clamp(volt, 30, 42); // Temperatura (°C): sobe devagar double temp = 38 + 4 * Math.Sin(t * 0.05) + 0.01 * t; temp = Math.Clamp(temp, 20, 85); // RPM: 0–1500 com variação double rpm = 1100 + 200 * Math.Sin(t * 0.9) + 80 * (rng.NextDouble() - 0.5); rpm = Math.Clamp(rpm, 0, 2000); ysCorr.Add(corr); ysVolt.Add(volt); ysTemp.Add(temp); ysRpm.Add(rpm); // mantém só últimos 60 s while (xs.Count > 0 && xs[^1] - xs[0] > 60.0) { xs.RemoveAt(0); ysCorr.RemoveAt(0); ysVolt.RemoveAt(0); ysTemp.RemoveAt(0); ysRpm.RemoveAt(0); } // redesenha (v5: passe arrays diretamente) Plot.Plot.Clear(); // Corrente (A) var scCorr = Plot.Plot.Add.Scatter(xs.ToArray(), ysCorr.ToArray()); scCorr.Label = "Corrente (A)"; scCorr.Color = ScottPlot.Colors.Cyan; scCorr.LineWidth = 2; // Tensão (V) var scVolt = Plot.Plot.Add.Scatter(xs.ToArray(), ysVolt.ToArray()); scVolt.Label = "Tensão (V)"; scVolt.Color = ScottPlot.Colors.Lime; scVolt.LineWidth = 2; // Temperatura (°C) var scTemp = Plot.Plot.Add.Scatter(xs.ToArray(), ysTemp.ToArray()); scTemp.Label = "Temp (°C)"; scTemp.Color = ScottPlot.Colors.Magenta; scTemp.LineWidth = 2; // RPM var scRpm = Plot.Plot.Add.Scatter(xs.ToArray(), ysRpm.ToArray()); scRpm.Label = "RPM"; scRpm.Color = ScottPlot.Colors.Orange; scRpm.LineWidth = 2; // janela de 60 s rolando double xEnd = xs[^1]; double xStart = Math.Max(0, xEnd - 60.0); Plot.Plot.Axes.SetLimitsX(xStart, xEnd); Plot.Plot.Axes.AutoScaleY(); // ajusta Y automaticamente para caber tudo Plot.Refresh(); } #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?.Gps?.Latitude ?? 0, lon: rover.DadosLeitura?.Gps?.Longitude ?? 0, heading: rover.DadosLeitura?.Gps?.AnguloCarroDefinido ?? 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; brdControle.IsEnabled = !dados_base; brdAjustesOperacao.IsEnabled = !dados_base; if (!dados_base) { sldGNSS_LeverArm.Value = VariaveisControleOperacao.RoverEmFoco.DadosLeitura?.Gps?.LeverArmLateral ?? 0; 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?.Gps?.Latitude, lon: dados_leitura?.Gps?.Longitude, heading: dados_leitura?.Gps?.OrientacaoReal, predict: simulacao); MAP.markers.UpdateMarkerInfo(rover_id, status: dados_leitura.StatusOperacao); } 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"); AtualizarDadosTela_GNSS(Variaveis.GpsService?.UltimaLeitura ?? new AgroBase.Models.GPSModel(), 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}"); AtualizarDadosTela_Diagnosticos(rover); if (dados_leitura.Trajetoria != null) AtualizarDadosTela_Resumo(dados_leitura); if (dados_leitura.Gps != null) AtualizarDadosTela_GNSS(dados_leitura.Gps, marker_id: rover_id); if (dados_leitura.Controle != null) AtualizarDadosTela_Controle(dados_leitura.Controle, dados_leitura.Movimentacao.AnguloMedio, dados_leitura.Movimentacao.VelocidadeMedia, dados_leitura.Emergencia, !dados_leitura.OperacaoIniciada, dados_leitura.StatusOperacao == 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.Gps != null) AtualizarDadosTela_Heading(dados_leitura.Gps.AnguloCarroDefinido, dados_leitura.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 (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.OperacaoSensoriamentoLogModel dados) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { pgbResumo_RuaAtual.Value = dados.Trajetoria.PercentualRuaAtual; lblResumo_RuaAtual.Content = $"{dados.Trajetoria.CorredorAtual.DistanciaPercorridaCorredor.ToString("0.00")} / {dados.Trajetoria.CorredorAtual.DistanciaTotal.ToString("0.00")}"; pgbResumo_AreaTotal.Value = dados.Trajetoria.PercentualOperacao; lblResumo_AreaTotal.Content = $"{dados.Trajetoria.DistanciaPercorrida.ToString("0.00")} / {dados.Trajetoria.DistanciaTotal.ToString("0.00")}"; pgbResumo_Bateria.Value = dados.Bateria.PorcentagemBateria; lblResumo_Bateria.Content = $"{dados.Bateria.TensaoInstantanea.ToString("0.00")} V ({dados.Bateria.TempoEstimadoRestanteMinutos})"; pgbResumo_Herbicida.Value = dados.Atuador.PercentualReservatorio; lblResumo_Herbicida.Content = $"{dados.Atuador.VolumeReservatorio.ToString("0.00")} L ({0.ToString("0.00")})"; pgbResumo_Infestacao.Value = dados.Atuador.PercentualErvasTerreno; lblResumo_Infestacao.Content = $"{dados.Atuador.VazaoMedia.ToString("0.00")}"; pgbResumo_Conexao.Value = 0; lblResumo_Conexao.Content = $"0,0 dBi (0,00 m)"; lblResumo_OperacaoLiberada.Content = $"Operação " + (dados.OperacaoLiberada ? "🔓 Liberada" : "🔒 Bloqueada"); lblResumo_OperacaoModo.Content = $"Modo {dados.Modo}"; lblResumo_OperacaoStatus.Content = $"Status {dados.StatusOperacao}" + (dados.StatusOperacao == AgroBase.Models.Enums.StatusOperacao.Aguardando ? $" ({dados.TempoAguardandoSeg.ToString("0")})" : ""); lblResumo_StatusCarro.Content = $"Carro {dados.Trajetoria.StatusCarro}"; lblResumo_TemperaturaGeral.Content = $"Temperatura {(dados.DadosPerformance.CPU_temp ?? 0).ToString("0.00")} °C"; lblResumo_OperacaoDuracao.Content = $"Duração {dados.TempoDecorrido.ToString("HH:mm:ss")} / {dados.Trajetoria.TempoEstimadoOperacao}"; } catch (Exception exUi) { Variaveis.MostrarLog($"Erro ao atualizar UI Resumo: {exUi.Message}"); } })); } #endregion #region DIAGNOSTICOS private void AtualizarDadosTela_Diagnosticos(AgroBase.Models.Operacoes.OperacaoParametrosModel dados) { lblDiagnosticos_Impedimento.Text = "Impedimento: " + (string.IsNullOrEmpty(dados.DadosLeitura.ErroOperacaoLiberada) ? "Nenhum" : dados.DadosLeitura.ErroOperacaoLiberada.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