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; 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(); 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 }, //}); } 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) { 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, Variaveis.GpsService?.UltimaLeitura?.Latitude ?? 0, Variaveis.GpsService?.UltimaLeitura?.Longitude ?? 0, Variaveis.GpsService?.UltimaLeitura?.OrientacaoReal ?? 0, MAP.markers.GetColorForMarker(d, isBase: true), "Base" ); } else { var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.Key == d); if (rover.Value != null) { MAP.markers.AddMarker( d, rover.Value.Gps?.Latitude ?? 0, rover.Value.Gps?.Longitude ?? 0, rover.Value.Gps?.AnguloCarroDefinido ?? 0, MAP.markers.GetColorForMarker(d, isBase: false), rover.Key ); } } } } int idx_selected = dispositivos.IndexOf(VariaveisControleOperacao.SelectedRoverId); if (idx_selected > -1) cmbDispositivo.SelectedIndex = idx_selected; } private void cmbDispositivo_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { AtualizarDadosTela_Telemetria(((ComboBox)sender).SelectedValue.ToString()); } public void LimparDadosTela_Telemetria() { VariaveisControleOperacao.SelectedRoverId = ""; } public void AtualizarDadosTela_Telemetria(string rover_id) { if (rover_id != VariaveisControleOperacao.SelectedRoverId) return; if (rover_id == VariaveisControleOperacao.BaseMarkerID) { Variaveis.MostrarLog($"Atualizando dados da tela para a base"); AtualizarDadosTela_GNSS(Variaveis.GpsService?.UltimaLeitura ?? new AgroBase.Models.GPSModel()); AtualizarDadosTela_Heading(Variaveis.GpsService?.UltimaLeitura?.AnguloCarroDefinido ?? 0); } else { var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.Key == rover_id); if (rover.Value == null) return; Variaveis.MostrarLog($"Atualizando dados da tela para o dispositivo {rover_id}"); AtualizarDadosTela_GNSS(rover.Value.Gps); AtualizarDadosTela_Heading(rover.Value.Gps.AnguloCarroDefinido, rover.Value.Modo == AgroBase.Models.Enums.ModoOperacao.Manual ? double.NaN : rover.Value.Trajetoria.AnguloCaminho); AtualizarDadosTela_IMU(rover.Value.IMU.InclinacaoLateral, rover.Value.IMU.InclinacaoFrontal); AtualizarDadostela_LIDAR(rover.Value.LivoxLidar.bboxes); } } #endregion #region IMU public void AtualizarDadosTela_IMU(double roll, double pitch) { IMU.RollDeg = roll; IMU.PitchDeg = pitch; } #endregion #region HEADING public void AtualizarDadosTela_Heading(double headingRover, double headingCourse = double.NaN) { HDG.Heading = headingRover; HDG.CourseHeading = headingCourse; } #endregion #region MAPA private void MAP_MarkerClicked(object? sender, MapMarkerManager.MarkerClickedEventArgs e) { Variaveis.MostrarLog($"Rover em foco: {e.MarkerId}"); if (string.IsNullOrEmpty(e.MarkerId)) { LimparDadosTela_Telemetria(); } else { VariaveisControleOperacao.SelectedRoverId = e.MarkerId; AtualizarDadosTela_Telemetria(e.MarkerId); } } private void MAP_StreetMapClicked(object? sender, MapViewControl.StreetMapClickedEventArgs e) { Variaveis.MostrarLog($"Rua clicada: {e.StreetId}. Ruas selecionadas: {string.Join(",", e.SelectedStreetsIds)}"); } #endregion #region PULVERIZADOR private void btnB0_Click(object sender, RoutedEventArgs e) { btnB0.Content = (btnB0.Content.ToString() == "Ligar") ? "Desligar" : "Ligar"; } #endregion #region POSICAO public void AtualizarDadosTela_GNSS(AgroBase.Models.GPSModel dados, string marker_id = null, double progresso = double.NaN) { lblGNSS_Posicao.Foreground = dados.Inicializado ? new SolidColorBrush(System.Windows.Media.Colors.Green) : new SolidColorBrush(System.Windows.Media.Colors.Red); lblGNSS_Correcao.Content = $"Correção {dados.QualidadeFix} " + (dados.QualidadeFix == AgroBase.Models.Enums.TiposCorrecaoGPS.BaseFix ? (dados.OrientacaoReal.ToString("0.00") + "°") : ((dados.Ntrip_ativado ? "N" : "B") + $" {dados.IdadeCorrecao.ToString("0.0")} s")); lblGNSS_Satelites.Content = $"Satélites {dados.NumeroSatelites} ({dados.PrecisaoCm.ToString("0.00")} cm)"; lblGNSS_Altitude.Content = $"Altitude {dados.Altitude.ToString("0.00")} m"; if (double.IsNaN(progresso)) { lblGNSS_Progresso.Visibility = Visibility.Hidden; btnGNSS_FixarBase.Visibility = Visibility.Hidden; } else { lblGNSS_Progresso.Visibility = Visibility.Visible; btnGNSS_FixarBase.Visibility = Visibility.Visible; lblGNSS_Progresso.Content = $"{progresso.ToString("0.00")}%"; if ((Variaveis.GpsService?.BaseFix?.CorrecaoAbsoluta ?? false) && Variaveis.GpsService.BaseFix.FixLiberado && btnGNSS_FixarBase.Content.ToString() == "Parar") { Variaveis.GpsService.BaseFix.FixLiberado = false; btnGNSS_FixarBase.Content = "Fixar"; } } if (!string.IsNullOrEmpty(marker_id)) MAP.markers.UpdateMarkerPosition(marker_id, dados.Latitude, dados.Longitude, dados.OrientacaoReal); } private void btnGNSS_FixarBase_Click(object sender, RoutedEventArgs e) { if (!Variaveis.GpsService.BaseFix.FixLiberado) { if (Variaveis.GpsService.BaseFix.CorrecaoAbsoluta) { var res = MessageBox.Show("Mudar posição da base?", "Tem certeza que deseja fixar a posição da base novamente?", MessageBoxButton.YesNo, MessageBoxImage.Question); if (res == MessageBoxResult.Yes) { btnGNSS_FixarBase.Content = "Parar"; Variaveis.GpsService.BaseFix.FixLiberado = true; Task.Run(async () => await Variaveis.GpsService.ConfigurarModulo()); } } else { btnGNSS_FixarBase.Content = "Parar"; Variaveis.GpsService.BaseFix.FixLiberado = true; } } else { btnGNSS_FixarBase.Content = "Fixar"; Variaveis.GpsService.BaseFix.FixLiberado = false; } } #endregion #region LIDAR public void AtualizarDadostela_LIDAR(List bboxes) { BBoxes3D.SetBboxes(bboxes); } #endregion } }