367 lines
13 KiB
C#
367 lines
13 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// Interaction logic for MainWindow.xaml
|
||
/// </summary>
|
||
public partial class MainWindow : Window
|
||
{
|
||
// buffers (X = tempo em segundos)
|
||
private readonly List<double> xs = new();
|
||
private readonly List<double> ysCorr = new(); // Corrente (A)
|
||
private readonly List<double> ysVolt = new(); // Tensão (V)
|
||
private readonly List<double> ysTemp = new(); // Temperatura (°C)
|
||
private readonly List<double> 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<string>() { 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<string> 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<AgroBase.Models.LivoxBboxModel> bboxes)
|
||
{
|
||
BBoxes3D.SetBboxes(bboxes);
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
} |