From c4cf9a274288a898c627121d7f6ad2466fa964a7 Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Wed, 11 Mar 2026 22:00:21 -0300 Subject: [PATCH] Ajustes na barra pulverizadora --- .../Controls/MapViewControl.xaml.cs | 28 +- .../Pulverizador/BicoLequeIndicator.xaml | 57 +-- .../Pulverizador/BicoLequeIndicator.xaml.cs | 110 +++--- .../Controls/Pulverizador/Converters.cs | 117 ++---- .../Pulverizador/PulverizadorIndicator.xaml | 361 ++++++++++-------- .../PulverizadorIndicator.xaml.cs | 56 ++- AgroBase/OperationControl/Models/AppShell.cs | 89 +++++ AgroBase/OperationControl/Models/Variaveis.cs | 4 +- .../ConfigConexao/ConfigConexaoViewModel.cs | 3 +- .../Views/Operacao/OperacaoBottomViewModel.cs | 148 ++++++- .../PreparacaoMapaTopViewModel.cs | 21 +- .../ViewModels/Windows/DockWindowViewModel.cs | 290 +++++++------- .../Views/Operacao/MonitoramentoView.xaml | 90 ++--- .../Views/Operacao/MonitoramentoView.xaml.cs | 1 + .../Views/Operacao/OperacaoCenterView.xaml | 2 +- .../Views/Operacao/OperacaoCenterView.xaml.cs | 5 +- 16 files changed, 776 insertions(+), 606 deletions(-) diff --git a/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs b/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs index 625c86d10..e914e9377 100644 --- a/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs +++ b/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs @@ -51,6 +51,9 @@ namespace OperationControl.Controls public static readonly DependencyProperty LongitudeProperty = DependencyProperty.Register(nameof(Longitude), typeof(double), typeof(MapViewControl), new PropertyMetadata(0.0, OnViewPropertyChanged)); public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register(nameof(Scale), typeof(double), typeof(MapViewControl), new PropertyMetadata(5000.0, OnViewPropertyChanged)); public static readonly DependencyProperty MbTilesPathProperty = DependencyProperty.Register(nameof(MbTilesPath), typeof(string), typeof(MapViewControl), new PropertyMetadata(string.Empty, OnMbTilesPathChanged)); + + public string PathMapaSelecionado; + public double Latitude { get => (double)GetValue(LatitudeProperty); @@ -122,7 +125,10 @@ namespace OperationControl.Controls public List RuasMapaCarregado; public event EventHandler? StreetMapClicked; - private bool ParametrizandoOperacao => !(VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Iniciada ?? false) && (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Status ?? StatusOperacao.NaoIniciado) == StatusOperacao.Parametrizando; + private bool ParametrizandoOperacao => + !(VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Iniciada ?? false) && + (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Status ?? StatusOperacao.NaoIniciado) == StatusOperacao.Parametrizando && + ((App)System.Windows.Application.Current).Shell.Dock?._vm?._viewOperacaoRight?._vm?.SecaoAtual == ViewModels.Views.Operacao.SecaoRightBar.Parametrizacao; protected virtual void OnStreetMapClicked(string streetId) { StreetMapClicked?.Invoke(this, new StreetMapClickedEventArgs(streetId, RuasMapaCarregado.Where(x => x.Selected).Select(x => x.Id).ToList())); @@ -653,15 +659,15 @@ namespace OperationControl.Controls if (dlg.ShowDialog() == true) { - var path = dlg.FileName; - if (path.EndsWith(".shp", StringComparison.OrdinalIgnoreCase)) + PathMapaSelecionado = dlg.FileName; + if (PathMapaSelecionado.EndsWith(".shp", StringComparison.OrdinalIgnoreCase)) { - var geojson = ConvertShpToGeoJson(path); + var geojson = ConvertShpToGeoJson(PathMapaSelecionado); AddGeoJsonLayer(geojson); } else { - var geojson = File.ReadAllText(path); + var geojson = File.ReadAllText(PathMapaSelecionado); AddGeoJsonLayer(geojson); } } @@ -800,19 +806,21 @@ namespace OperationControl.Controls } - public void LoadMapFile(string path) + public void LoadMapFile(string? path) { - if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) + if (string.IsNullOrWhiteSpace(path) || !File.Exists(path) || path == PathMapaSelecionado) return; - if (path.EndsWith(".shp", StringComparison.OrdinalIgnoreCase)) + PathMapaSelecionado = path; + + if (PathMapaSelecionado.EndsWith(".shp", StringComparison.OrdinalIgnoreCase)) { - var geojson = ConvertShpToGeoJson(path); + var geojson = ConvertShpToGeoJson(PathMapaSelecionado); AddGeoJsonLayer(geojson); } else { - var geojson = File.ReadAllText(path); + var geojson = File.ReadAllText(PathMapaSelecionado); AddGeoJsonLayer(geojson); } } diff --git a/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml b/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml index a3e2cb6d2..ab31986e4 100644 --- a/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml +++ b/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml @@ -1,15 +1,15 @@  + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + mc:Ignorable="d" + MinWidth="56" + MinHeight="80" + d:DesignWidth="80" + d:DesignHeight="88"> - - @@ -17,33 +17,36 @@ - + - - + - - - + + + + - - - - - - + + + + + - - + + + + - - + - - - + + + - + \ No newline at end of file diff --git a/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml.cs b/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml.cs index 689a8d999..c473bf7c1 100644 --- a/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml.cs +++ b/AgroBase/OperationControl/Controls/Pulverizador/BicoLequeIndicator.xaml.cs @@ -1,179 +1,166 @@ -using System.Windows; -using System.Windows.Controls; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows; using System.Windows.Media; +using System.Windows.Controls; using UserControl = System.Windows.Controls.UserControl; using Brush = System.Windows.Media.Brush; using Point = System.Windows.Point; -using System.ComponentModel; -using System.Runtime.CompilerServices; namespace OperationControl.Controls.Pulverizador { - /// - /// Interaction logic for BicoControl.xaml - /// public partial class BicoLequeIndicator : UserControl { public BicoLequeIndicator() { InitializeComponent(); - Loaded += (s, e) => UpdateGeometry(); + + Loaded += (_, __) => UpdateGeometry(); + SizeChanged += (_, __) => UpdateGeometry(); } - #region DependencyProperties básicas - - // Comando enviado para a solenoide public bool IsCommandOn { get => (bool)GetValue(IsCommandOnProperty); set => SetValue(IsCommandOnProperty, value); } - public static readonly DependencyProperty IsCommandOnProperty = DependencyProperty.Register(nameof(IsCommandOn), typeof(bool), typeof(BicoLequeIndicator), new PropertyMetadata(false, OnVisualPropertyChanged)); + public static readonly DependencyProperty IsCommandOnProperty = + DependencyProperty.Register(nameof(IsCommandOn), typeof(bool), typeof(BicoLequeIndicator), + new PropertyMetadata(false, OnVisualPropertyChanged)); - // Leitura de ligado (se está realmente pulverizando) public bool IsSpraying { get => (bool)GetValue(IsSprayingProperty); set => SetValue(IsSprayingProperty, value); } - public static readonly DependencyProperty IsSprayingProperty = DependencyProperty.Register(nameof(IsSpraying), typeof(bool), typeof(BicoLequeIndicator), new PropertyMetadata(false, OnVisualPropertyChanged)); + public static readonly DependencyProperty IsSprayingProperty = + DependencyProperty.Register(nameof(IsSpraying), typeof(bool), typeof(BicoLequeIndicator), + new PropertyMetadata(false, OnVisualPropertyChanged)); - // Ângulo central do jato (0 = vertical; positivo gira p/ esquerda, negativo p/ direita) public double AnguloControle { get => (double)GetValue(AnguloControleProperty); set => SetValue(AnguloControleProperty, value); } - public static readonly DependencyProperty AnguloControleProperty = DependencyProperty.Register(nameof(AnguloControle), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(0.0, OnGeometryPropertyChanged)); + public static readonly DependencyProperty AnguloControleProperty = + DependencyProperty.Register(nameof(AnguloControle), typeof(double), typeof(BicoLequeIndicator), + new PropertyMetadata(0.0, OnGeometryPropertyChanged)); - // Abertura total do leque (em graus) – ex: 110° public double OpeningAngle { get => (double)GetValue(OpeningAngleProperty); set => SetValue(OpeningAngleProperty, value); } - public static readonly DependencyProperty OpeningAngleProperty = DependencyProperty.Register(nameof(OpeningAngle), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(110.0, OnGeometryPropertyChanged)); + public static readonly DependencyProperty OpeningAngleProperty = + DependencyProperty.Register(nameof(OpeningAngle), typeof(double), typeof(BicoLequeIndicator), + new PropertyMetadata(110.0, OnGeometryPropertyChanged)); - // Vazao instantanea (mL/s) public double Vazao { get => (double)GetValue(VazaoProperty); set => SetValue(VazaoProperty, value); } - public static readonly DependencyProperty VazaoProperty = DependencyProperty.Register(nameof(Vazao), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(0.0, OnGeometryPropertyChanged)); + public static readonly DependencyProperty VazaoProperty = + DependencyProperty.Register(nameof(Vazao), typeof(double), typeof(BicoLequeIndicator), + new PropertyMetadata(0.0)); - // Tempo atuado (s) public double TempoAtuado { get => (double)GetValue(TempoAtuadoProperty); set => SetValue(TempoAtuadoProperty, value); } - public static readonly DependencyProperty TempoAtuadoProperty = DependencyProperty.Register(nameof(TempoAtuado), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(0.0, OnGeometryPropertyChanged)); + public static readonly DependencyProperty TempoAtuadoProperty = + DependencyProperty.Register(nameof(TempoAtuado), typeof(double), typeof(BicoLequeIndicator), + new PropertyMetadata(0.0)); - // Atuacoes public int Atuacoes { get => (int)GetValue(AtuacoesProperty); set => SetValue(AtuacoesProperty, value); } - public static readonly DependencyProperty AtuacoesProperty = DependencyProperty.Register(nameof(Atuacoes), typeof(int), typeof(BicoLequeIndicator), new PropertyMetadata(0, OnGeometryPropertyChanged)); - - #endregion + public static readonly DependencyProperty AtuacoesProperty = + DependencyProperty.Register(nameof(Atuacoes), typeof(int), typeof(BicoLequeIndicator), + new PropertyMetadata(0)); private static void OnGeometryPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var ctrl = (BicoLequeIndicator)d; - ctrl.UpdateGeometry(); + if (d is BicoLequeIndicator ctrl) + ctrl.UpdateGeometry(); } private static void OnVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var ctrl = (BicoLequeIndicator)d; - ctrl.UpdateVisualState(); + if (d is BicoLequeIndicator ctrl) + ctrl.UpdateVisualState(); } - /// - /// Atualiza cor da solenoide e visibilidade do leque. - /// private void UpdateVisualState() { - if (SolenoidRect == null || SprayPolygon == null) + if (SolenoidRect == null || SprayPolygon == null || CenterLine == null) return; var onBrush = (Brush)FindResource("CommandOnBrush"); var offBrush = (Brush)FindResource("CommandOffBrush"); SolenoidRect.Stroke = IsCommandOn ? onBrush : offBrush; - SprayPolygon.Visibility = IsSpraying && OpeningAngle > 0 ? Visibility.Visible : Visibility.Collapsed; CenterLine.Visibility = IsSpraying ? Visibility.Visible : Visibility.Collapsed; } - /// - /// Recalcula geometria do leque e da linha central a partir do ângulo central e abertura. - /// private void UpdateGeometry() { - if (DrawCanvas == null || SprayPolygon == null || CenterLine == null) + if (DrawCanvas == null || SprayPolygon == null || CenterLine == null || SolenoidRect == null) return; double width = DrawCanvas.ActualWidth; double height = DrawCanvas.ActualHeight; - if (width <= 0) width = 120; - if (height <= 0) height = 80; - // Origem do bico (ponto da solenoide) + if (width <= 0) width = 60; + if (height <= 0) height = 40; + double originX = width / 2.0; - double originY = 10.0; // um pouco abaixo do topo + double originY = 8.0; - double length = height - 10.0; // comprimento do jato até próximo do solo + double length = Math.Max(16.0, height - 10.0); - // Metade da abertura em radianos double halfAngleRad = OpeningAngle * 0.5 * Math.PI / 180.0; - - // Deslocamento horizontal na base do leque double dx = Math.Tan(halfAngleRad) * length; - // Pontos em sistema local (0,0 = bico; y para baixo) Point apex = new Point(0, 0); Point leftLocal = new Point(-dx, length); Point rightLocal = new Point(dx, length); Point centerLocal = new Point(0, length); - // Rotação pelo ângulo central (em radianos). double theta = AnguloControle * Math.PI / 180.0; double cos = Math.Cos(theta); double sin = Math.Sin(theta); - Point Rot(Point p) => new Point( + Point Rotate(Point p) => new Point( p.X * cos - p.Y * sin, p.X * sin + p.Y * cos); - Point apexR = Rot(apex); - Point leftR = Rot(leftLocal); - Point rightR = Rot(rightLocal); - Point centerR = Rot(centerLocal); + Point apexR = Rotate(apex); + Point leftR = Rotate(leftLocal); + Point rightR = Rotate(rightLocal); + Point centerR = Rotate(centerLocal); - // Translada para as coordenadas do Canvas apexR.Offset(originX, originY); leftR.Offset(originX, originY); rightR.Offset(originX, originY); centerR.Offset(originX, originY); - // Atualiza Polygon do leque - var pts = new PointCollection { leftR, apexR, rightR }; - SprayPolygon.Points = pts; + SprayPolygon.Points = new PointCollection { leftR, apexR, rightR }; - // Atualiza linha central do jato CenterLine.X1 = apexR.X; CenterLine.Y1 = apexR.Y; CenterLine.X2 = centerR.X; CenterLine.Y2 = centerR.Y; - // Reposiciona a solenoide exatamente na origem Canvas.SetLeft(SolenoidRect, originX - SolenoidRect.Width / 2.0); Canvas.SetTop(SolenoidRect, originY - SolenoidRect.Height / 2.0); + + UpdateVisualState(); } } @@ -239,5 +226,4 @@ namespace OperationControl.Controls.Pulverizador protected void OnPropertyChanged([CallerMemberName] string propName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } - -} +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Controls/Pulverizador/Converters.cs b/AgroBase/OperationControl/Controls/Pulverizador/Converters.cs index c3f40862f..ea36a0231 100644 --- a/AgroBase/OperationControl/Controls/Pulverizador/Converters.cs +++ b/AgroBase/OperationControl/Controls/Pulverizador/Converters.cs @@ -1,101 +1,39 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Data; namespace OperationControl.Controls.Pulverizador { - class Converters + public class BarraTopConverter : IMultiValueConverter { - } + public double MinAlturaCm { get; set; } = 50.0; + public double MaxAlturaCm { get; set; } = 80.0; + public double TopMinPx { get; set; } = 18.0; + public double TopMaxPx { get; set; } = 62.0; - public class AlturaBarraToMarginFisicaConverter : IValueConverter - { - public double GroundTop { get; set; } = 80.0; // Y do solo - public double Top80Px { get; set; } = 20.0; // Y da barra quando está a 80 cm - public double AlturaMaxCm { get; set; } = 80.0; - public double BarraOffset { get; set; } = 13.0; - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is double alturaCm) - { - alturaCm = Math.Clamp(alturaCm, 0, AlturaMaxCm); - - double distanciaPx = GroundTop - Top80Px; // px que representam 80 cm - double pxPorCm = distanciaPx / AlturaMaxCm; // regra de 3 - double alturaPx = alturaCm * pxPorCm; - - double yBarra = GroundTop - alturaPx; // Y da barra na GRID - - double marginTop = yBarra - BarraOffset; // compensa o offset interno - - return new Thickness(33, marginTop, 33, 0); - } - - return new Thickness(33, 0, 33, 0); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - => System.Windows.Data.Binding.DoNothing; - } - - public class BicoPositionFisicaConverter : IMultiValueConverter - { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values.Length < 5) - return 0.0; + if (values.Length < 2) + return new Thickness(0); - if (values[0] is not double barraWidth || - values[1] is not int index || - values[2] is not int qtd || - values[3] is not double distEntreBicosCm || - values[4] is not double comprimentoBarraCm) - { - return 0.0; - } + if (values[0] is not double alturaCm || values[1] is not double areaHeight) + return new Thickness(0); - if (qtd <= 0 || barraWidth <= 0 || comprimentoBarraCm <= 0) - return 0.0; + if (MaxAlturaCm <= MinAlturaCm) + return new Thickness(0); - // garante valores válidos - distEntreBicosCm = Math.Max(0.0, distEntreBicosCm); + double extraOffset = 0.0; + if (parameter != null) + double.TryParse(parameter.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out extraOffset); - if (qtd == 1) - { - // 1 bico: centro da barra - return barraWidth / 2.0; - } + double h = Math.Max(MinAlturaCm, Math.Min(MaxAlturaCm, alturaCm)); + double t = (h - MinAlturaCm) / (MaxAlturaCm - MinAlturaCm); - // span "ideal" em cm (do primeiro ao último bico) - double spanIdealCm = (qtd - 1) * distEntreBicosCm; + double maxTop = Math.Min(TopMaxPx, Math.Max(TopMinPx, areaHeight - 20)); + double top = maxTop - (t * (maxTop - TopMinPx)); - // Se o span ideal for maior que a barra física, comprimimos - double escala = 1.0; - if (spanIdealCm > comprimentoBarraCm && qtd > 1) - { - escala = comprimentoBarraCm / spanIdealCm; - } - - double distEfetivaCm = distEntreBicosCm * escala; - double spanEfetivoCm = (qtd - 1) * distEfetivaCm; - - // centraliza o conjunto de bicos na barra - double offsetCm = (comprimentoBarraCm - spanEfetivoCm) / 2.0; - - // posição em cm do bico atual - double xCm = offsetCm + index * distEfetivaCm; - - // regra de 3: cm → px - double pxPorCm = barraWidth / comprimentoBarraCm; - double xPx = xCm * pxPorCm; - - return xPx; + return new Thickness(0, top + extraOffset, 0, 0); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) @@ -116,12 +54,11 @@ namespace OperationControl.Controls.Pulverizador return 0.0; } - // px por cm na faixa do solo (usando comprimento da barra como referência física) double pxPorCm = totalWidth / comprimentoBarraCm; double widthPx = larguraRuaCm * pxPorCm; - // não deixa a rua ficar maior que a área disponível - if (widthPx > totalWidth) widthPx = totalWidth; + if (widthPx > totalWidth) + widthPx = totalWidth; return widthPx; } @@ -134,7 +71,8 @@ namespace OperationControl.Controls.Pulverizador { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (values.Length < 4) return new Thickness(0); + if (values.Length < 4) + return new Thickness(0); if (values[0] is not double totalWidth || values[1] is not double erroLateralCm || @@ -150,19 +88,14 @@ namespace OperationControl.Controls.Pulverizador double center = totalWidth / 2.0; - // erro > 0 = robô desviado pra direita → rua aparece indo pra esquerda + // erro > 0 = robô está para a direita, então a rua aparece deslocando para a esquerda double roadCenter = center - erroLateralCm * pxPorCm; - double left = roadCenter - widthPx / 2.0; - // SEM travas aqui: pode ser negativo ou maior que totalWidth - // o Grid com ClipToBounds cuida de cortar o desenho - return new Thickness(left, 0, 0, 0); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); } - -} +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml b/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml index 4158beb31..2365d53ba 100644 --- a/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml +++ b/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml @@ -1,192 +1,253 @@  - - - - - - + xmlns:ctrl="clr-namespace:OperationControl.Controls" + mc:Ignorable="d" + MinWidth="420" + MinHeight="150" + d:DesignWidth="900" + d:DesignHeight="180"> - - - - - - + + + + + + + + - - + + + + + + - - - - - - + + + + + + + - - - + + - + + - + - - - - - - + + + - - - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml.cs b/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml.cs index f2496efa2..b7fbad8f8 100644 --- a/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml.cs +++ b/AgroBase/OperationControl/Controls/Pulverizador/PulverizadorIndicator.xaml.cs @@ -7,28 +7,22 @@ using UserControl = System.Windows.Controls.UserControl; namespace OperationControl.Controls { - /// - /// Interaction logic for PulverizadorIndicator.xaml - /// public partial class PulverizadorIndicator : UserControl { public PulverizadorIndicator() { InitializeComponent(); - RebuildBicos(); - DataContext = this; } - public double DistanciaEntreBicosCm { get => (double)GetValue(DistanciaEntreBicosCmProperty); set => SetValue(DistanciaEntreBicosCmProperty, value); } - public static readonly DependencyProperty DistanciaEntreBicosCmProperty = DependencyProperty.Register(nameof(DistanciaEntreBicosCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(50.0)); - + public static readonly DependencyProperty DistanciaEntreBicosCmProperty = + DependencyProperty.Register(nameof(DistanciaEntreBicosCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(50.0)); public double ComprimentoBarraCm { @@ -36,7 +30,8 @@ namespace OperationControl.Controls set => SetValue(ComprimentoBarraCmProperty, value); } - public static readonly DependencyProperty ComprimentoBarraCmProperty = DependencyProperty.Register(nameof(ComprimentoBarraCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(103.7)); + public static readonly DependencyProperty ComprimentoBarraCmProperty = + DependencyProperty.Register(nameof(ComprimentoBarraCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(103.7)); public double AlturaBarra { @@ -44,7 +39,8 @@ namespace OperationControl.Controls set => SetValue(AlturaBarraProperty, value); } - public static readonly DependencyProperty AlturaBarraProperty = DependencyProperty.Register(nameof(AlturaBarra), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(50.0)); + public static readonly DependencyProperty AlturaBarraProperty = + DependencyProperty.Register(nameof(AlturaBarra), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(50.0)); public bool IsBombaOn { @@ -52,7 +48,8 @@ namespace OperationControl.Controls set => SetValue(IsBombaOnProperty, value); } - public static readonly DependencyProperty IsBombaOnProperty = DependencyProperty.Register(nameof(IsBombaOn), typeof(bool), typeof(PulverizadorIndicator), new PropertyMetadata(false)); + public static readonly DependencyProperty IsBombaOnProperty = + DependencyProperty.Register(nameof(IsBombaOn), typeof(bool), typeof(PulverizadorIndicator), new PropertyMetadata(false)); public double PressaoLinha { @@ -60,8 +57,8 @@ namespace OperationControl.Controls set => SetValue(PressaoLinhaProperty, value); } - public static readonly DependencyProperty PressaoLinhaProperty = DependencyProperty.Register(nameof(PressaoLinha), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); - + public static readonly DependencyProperty PressaoLinhaProperty = + DependencyProperty.Register(nameof(PressaoLinha), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); public double TempoAtuado { @@ -69,7 +66,8 @@ namespace OperationControl.Controls set => SetValue(TempoAtuadoProperty, value); } - public static readonly DependencyProperty TempoAtuadoProperty = DependencyProperty.Register(nameof(TempoAtuado), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); + public static readonly DependencyProperty TempoAtuadoProperty = + DependencyProperty.Register(nameof(TempoAtuado), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); public double VazaoInstantanea { @@ -77,7 +75,8 @@ namespace OperationControl.Controls set => SetValue(VazaoInstantaneaProperty, value); } - public static readonly DependencyProperty VazaoInstantaneaProperty = DependencyProperty.Register(nameof(VazaoInstantanea), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); + public static readonly DependencyProperty VazaoInstantaneaProperty = + DependencyProperty.Register(nameof(VazaoInstantanea), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); public int QtdBicos { @@ -85,7 +84,9 @@ namespace OperationControl.Controls set => SetValue(QtdBicosProperty, value); } - public static readonly DependencyProperty QtdBicosProperty = DependencyProperty.Register(nameof(QtdBicos), typeof(int), typeof(PulverizadorIndicator), new PropertyMetadata(VariaveisEquipamento.QuantidadeBicosPulverizadores, OnQtdBicosChanged)); + public static readonly DependencyProperty QtdBicosProperty = + DependencyProperty.Register(nameof(QtdBicos), typeof(int), typeof(PulverizadorIndicator), + new PropertyMetadata(VariaveisEquipamento.QuantidadeBicosPulverizadores, OnQtdBicosChanged)); public double LarguraRuaCm { @@ -93,8 +94,8 @@ namespace OperationControl.Controls set => SetValue(LarguraRuaCmProperty, value); } - public static readonly DependencyProperty LarguraRuaCmProperty = DependencyProperty.Register(nameof(LarguraRuaCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(150.0)); - + public static readonly DependencyProperty LarguraRuaCmProperty = + DependencyProperty.Register(nameof(LarguraRuaCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(150.0)); public double ErroLateralCm { @@ -102,22 +103,24 @@ namespace OperationControl.Controls set => SetValue(ErroLateralCmProperty, value); } - public static readonly DependencyProperty ErroLateralCmProperty = DependencyProperty.Register(nameof(ErroLateralCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); - + public static readonly DependencyProperty ErroLateralCmProperty = + DependencyProperty.Register(nameof(ErroLateralCm), typeof(double), typeof(PulverizadorIndicator), new PropertyMetadata(0.0)); public ObservableCollection Bicos { get; } = new(); private static void OnQtdBicosChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var ctl = (PulverizadorIndicator)d; - ctl.RebuildBicos(); + if (d is PulverizadorIndicator ctl) + ctl.RebuildBicos(); } private void RebuildBicos() { Bicos.Clear(); - for (int i = 0; i < QtdBicos; i++) + int quantidade = QtdBicos < 0 ? 0 : QtdBicos; + + for (int i = 0; i < quantidade; i++) { Bicos.Add(new BicoModel { @@ -132,10 +135,5 @@ namespace OperationControl.Controls }); } } - - - } - - -} +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Models/AppShell.cs b/AgroBase/OperationControl/Models/AppShell.cs index 3c2acf7df..f57d9d8a5 100644 --- a/AgroBase/OperationControl/Models/AppShell.cs +++ b/AgroBase/OperationControl/Models/AppShell.cs @@ -8,6 +8,8 @@ namespace OperationControl.Models { public class AppShell { + public static readonly bool Mock = true; + private readonly System.Timers.Timer tmrComunicacao = new(1000) { AutoReset = true }; private int tmrComunicacaoTicks = 0; private bool tmrComunicacaoTicking = false; @@ -33,6 +35,93 @@ namespace OperationControl.Models Main = new MainWindow(); Dock = new DockWindow(); Dock.Show(); + + if (Mock) + { + Task.Run(async () => + { + await Task.Delay(1000); + VariaveisControleOperacao.AdicionarNovoRoverNaRede("01", "192.168.1.100"); + VariaveisControleOperacao.RoversNaRede.FirstOrDefault().DadosLeitura = new AgroBase.Models.Operacoes.OperacaoParametrosDadosModel() + { + StatusRover = StatusModulo.Operante, + Operacao = new AgroBase.Models.Operacoes.OperacaoParametrosDadosOperacaoModel() + { + Status = StatusOperacao.Parametrizando + }, + Atuador = new AgroBase.Models.Operacoes.OperacaoParametrosDadosAtuadorModel() + { + Bombas = new List() + { + new AgroBase.Models.Operacoes.OperacaoParametrosDadosBombaModel() + { + ComandoEstado = true, + LeituraEstado = true, + ID = "BOMBA", + ID_Num = 10, + Inicializado = true, + Potencia = 50, + TempoAtuado = 3000 + } + }, + Bicos = new List() + { + new AgroBase.Models.Operacoes.OperacaoParametrosDadosBicoModel() + { + AnguloAbertura = 30, + ComandoAngulo = 0, + ComandoEstado = true, + ID = "B01", + ID_Num = 1, + Inicializado = true, + LeituraAngulo = 0, + LeituraEstado = true, + Posicao = 1, + QtdAtuacoes = 5, + TempoAtuado = 300, + VazaoInstantaneaMLs = 15, + VazaoMediaMLs = 30, + VolumeVazadoML = 325 + }, + new AgroBase.Models.Operacoes.OperacaoParametrosDadosBicoModel() + { + AnguloAbertura = 30, + ComandoAngulo = 0, + ComandoEstado = true, + ID = "B02", + ID_Num = 2, + Inicializado = true, + LeituraAngulo = 0, + LeituraEstado = false, + Posicao = 1, + QtdAtuacoes = 5, + TempoAtuado = 300, + VazaoInstantaneaMLs = 15, + VazaoMediaMLs = 30, + VolumeVazadoML = 325 + }, + new AgroBase.Models.Operacoes.OperacaoParametrosDadosBicoModel() + { + AnguloAbertura = 30, + ComandoAngulo = 0, + ComandoEstado = false, + ID = "B03", + ID_Num = 3, + Inicializado = true, + LeituraAngulo = 0, + LeituraEstado = true, + Posicao = 2, + QtdAtuacoes = 5, + TempoAtuado = 300, + VazaoInstantaneaMLs = 15, + VazaoMediaMLs = 30, + VolumeVazadoML = 325 + }, + } + } + }; + }); + } } private void tmrComunicacao_Elapsed() diff --git a/AgroBase/OperationControl/Models/Variaveis.cs b/AgroBase/OperationControl/Models/Variaveis.cs index e48ccb328..3c537ff1a 100644 --- a/AgroBase/OperationControl/Models/Variaveis.cs +++ b/AgroBase/OperationControl/Models/Variaveis.cs @@ -168,7 +168,7 @@ namespace OperationControl.Models Application.Current.Dispatcher.BeginInvoke(new Action(() => { - (((App)Application.Current).Shell.Dock?.DataContext as DockWindowViewModel)?.AtualizarDadosTela(rover); + ((App)Application.Current).Shell.Dock?._vm?.AtualizarDadosTela(rover); })); } catch (Exception ex) @@ -237,7 +237,7 @@ namespace OperationControl.Models } foreach (var rover in rovers_atual) { - rover.Alive = (DateTime.Now - rover.UltimoContato).TotalSeconds < TempoRoverVivo; + rover.Alive = AppShell.Mock || (DateTime.Now - rover.UltimoContato).TotalSeconds < TempoRoverVivo; if (!rover.Alive) { //((App)Application.Current).Shell.Main?.MAP?.markers?.UpdateMarkerInfo(rover.RoverId, status: AgroBase.Models.Enums.StatusOperacao.Erro); diff --git a/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs index 465e4dcb8..8f746d599 100644 --- a/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs @@ -221,8 +221,7 @@ namespace OperationControl.ViewModels // string fix = GnssService.FixMode(); // "No Fix", "3D", "RTK Float", "RTK Fixed" // double? hdop = GnssService.Hdop(); - bool conectado = Variaveis.GpsService?.IsConnected ?? false; // <-- seu método real aqui - conectado = true; + bool conectado = AppShell.Mock || (Variaveis.GpsService?.IsConnected ?? false); if (!conectado) { diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs index 1eb05c803..35003b1a8 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs @@ -126,6 +126,26 @@ namespace OperationControl.ViewModels.Views.Operacao SelecionarAlerta(alerta); } + + 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; + } + } + public void AtualizarListaAlertasRoverSelecionado(string rover_id) { var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == rover_id); @@ -133,25 +153,6 @@ namespace OperationControl.ViewModels.Views.Operacao var dados_leitura = rover?.DadosLeitura; if (dados_leitura?.ModulosSaude == null) return; - 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; - } - } - foreach (var mod in dados_leitura.ModulosSaude) { if (mod.saude_individual.Any()) @@ -181,6 +182,113 @@ namespace OperationControl.ViewModels.Views.Operacao } } } - + + public void AtualizarListaAlertasTodosRovers() + { + App.Current.Dispatcher.BeginInvoke(new Action(() => + { + var alertasEsperados = new List(); + + 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), + Mensagem = string.Join(", ", ind.motivos ?? new List()), + Timestamp = DateTime.Now + }); + } + } + 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), + Mensagem = string.Join(", ", mod.motivos ?? new List()), + Timestamp = DateTime.Now + }); + } + } + } + + string GerarChave(string roverId, T_Code modulo, string? modId) + => $"{roverId}|{modulo}|{modId ?? ""}"; + + var mapaEsperado = alertasEsperados.ToDictionary( + x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID), + x => x); + + var alertasAtuais = AlertasAtivos.ToList(); + + // Remove os alertas que não deveriam mais existir + foreach (var atual in alertasAtuais) + { + var chave = GerarChave(atual.Rover_ID, atual.Modulo, atual.Mod_ID); + if (!mapaEsperado.ContainsKey(chave)) + { + AlertasAtivos.Remove(atual); + + if (ToastAlerta == atual) + LimparToast(); + } + } + + // Adiciona ou atualiza os alertas esperados + foreach (var esperado in alertasEsperados) + { + var existente = AlertasAtivos.FirstOrDefault(x => + x.Rover_ID == esperado.Rover_ID && + x.Modulo == esperado.Modulo && + x.Mod_ID == esperado.Mod_ID); + + if (existente == null) + { + AlertasAtivos.Add(esperado); + MostrarToast(esperado); + } + else + { + bool mudou = + existente.Mensagem != esperado.Mensagem || + existente.Severidade != esperado.Severidade; + + existente.Mensagem = esperado.Mensagem; + existente.Severidade = esperado.Severidade; + + if (mudou) + { + existente.Timestamp = DateTime.Now; + MostrarToast(existente); + } + } + } + })); + } + } } \ No newline at end of file diff --git a/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs index 20f10ce73..b43f0440f 100644 --- a/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs @@ -94,16 +94,19 @@ namespace OperationControl.ViewModels public void AtualizarDados(GPSModel dados) { - LatitudeTexto = dados.Latitude.ToString("F6"); - LongitudeTexto = dados.Longitude.ToString("F6"); - AltitudeTexto = $"{dados.Altitude:F1} m"; - HeadingTexto = $"{dados.OrientacaoReal:F1}°"; - FixTexto = dados.QualidadeFix.ToString(); - PrecisaoTexto = $"{dados.PrecisaoCm:F2} cm"; - SatelitesTexto = dados.NumeroSatelites.ToString(); - IdadeCorrecaoTexto = $"{dados.IdadeCorrecao:F1} s"; + System.Windows.Application.Current.Dispatcher.Invoke(() => + { + LatitudeTexto = dados.Latitude.ToString("F6"); + LongitudeTexto = dados.Longitude.ToString("F6"); + AltitudeTexto = $"{dados.Altitude:F1} m"; + HeadingTexto = $"{dados.OrientacaoReal:F1}°"; + FixTexto = dados.QualidadeFix.ToString(); + PrecisaoTexto = $"{dados.PrecisaoCm:F2} cm"; + SatelitesTexto = dados.NumeroSatelites.ToString(); + IdadeCorrecaoTexto = $"{dados.IdadeCorrecao:F1} s"; - AtualizarCorFix(dados.QualidadeFix); + AtualizarCorFix(dados.QualidadeFix); + }); } private void AtualizarCorFix(TiposCorrecaoGPS fix) diff --git a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs index 375a18fd0..c61996c44 100644 --- a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs @@ -9,6 +9,7 @@ using AgroBase.Models.Operacoes; using Newtonsoft.Json.Linq; using OperationControl.ViewModels.Views.Operacao; using OperationControl.Controls; +using static OpenTK.Graphics.OpenGL.GL; namespace OperationControl.ViewModels { @@ -136,7 +137,7 @@ namespace OperationControl.ViewModels } } - private bool _baseFixada; + private bool _baseFixada = AppShell.Mock; public bool BaseFixada { get => _baseFixada; @@ -343,10 +344,8 @@ namespace OperationControl.ViewModels CenterContent = _viewOperacaoCenter; RightContent = _viewOperacaoRight; BottomContent = _viewOperacaoBottom; - CriarMarcadorBase(_viewOperacaoCenter?.MapaOperacao); - - if (!string.IsNullOrWhiteSpace(_ultimoMapaCarregado)) - _viewOperacaoCenter?.MapaOperacao?.LoadMapFile(_ultimoMapaCarregado); + CriarMarcadorBase(_viewOperacaoCenter?.Mapa); + AtualizarDadosMapa(null); break; } @@ -371,25 +370,18 @@ namespace OperationControl.ViewModels AtualizarProgressoFixacaoBase(bf.Progresso, bf.ProgressoStr); if (bf.Progresso >= 100) FinalizarFixacaoBase(true, bf.ProgressoStr); } - switch (CurrentStep) + MapViewControl? Mapa = CurrentStep == DockStep.PreparacaoMapa ? _viewPreparacaoMapa?.MapaPreparacao : CurrentStep == DockStep.ParametrizacaoRover ? _viewOperacaoCenter?.Mapa : null; + if (CurrentStep == DockStep.PreparacaoMapa) { - case DockStep.PreparacaoMapa: - System.Windows.Application.Current.Dispatcher.Invoke(() => - { - _viewPreparacaoMapaTop?._vm?.AtualizarDados(dados); - _viewPreparacaoMapa?.MapaPreparacao?.markers?.UpdateMarkerPosition(VariaveisControleOperacao.BaseMarkerID, lat: dados?.Latitude, lon: dados?.Longitude, heading: dados?.OrientacaoReal); - }); - break; - - case DockStep.ParametrizacaoRover: - _viewOperacaoCenter?.MapaOperacao?.markers?.UpdateMarkerPosition(VariaveisControleOperacao.BaseMarkerID, lat: dados?.Latitude, lon: dados?.Longitude, heading: dados?.OrientacaoReal); - break; + _viewPreparacaoMapaTop?._vm?.AtualizarDados(dados); } + Mapa?.markers?.UpdateMarkerPosition(VariaveisControleOperacao.BaseMarkerID, lat: dados?.Latitude, lon: dados?.Longitude, heading: dados?.OrientacaoReal); } - public void AtualizarDadosMapa(OperacaoParametrosModel dados) + public void AtualizarDadosMapa(OperacaoParametrosModel? dados) { - _viewOperacaoCenter?.MapaOperacao?.CarregarDadosMapa(dados?.Mapa, dados?.RuasPercorrer); + _viewOperacaoCenter?.Mapa?.LoadMapFile(_ultimoMapaCarregado); + _viewOperacaoCenter?.Mapa?.CarregarDadosMapa(dados?.Mapa, dados?.RuasPercorrer); } #region TELA 2 @@ -500,6 +492,131 @@ namespace OperationControl.ViewModels #endregion #region TELA 3 + public void AtualizarListaRovers(List rovers_atual) + { + var cards = rovers_atual.Select(x => new RoverCardModel() + { + EquipamentoId = x.IP, + NumeroSerie = x.RoverId, + Status = x.DadosLeitura?.StatusRover ?? AgroBase.Models.Enums.StatusModulo.Desconectado + }).ToArray(); + + _viewOperacaoRight?._vm?.AtualizarListaRovers(cards); + + foreach (var rover in cards) + { + if (!_viewOperacaoCenter?.Mapa?.markers?.Added(rover.NumeroSerie) ?? false) + { + var roverDados = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == rover.NumeroSerie); + if (roverDados != null) + { + _viewOperacaoCenter?.Mapa?.markers?.AddMarker( + rover.NumeroSerie, + false, + lat: roverDados.DadosLeitura?.Gnss?.Latitude ?? 0, + lon: roverDados.DadosLeitura?.Gnss?.Longitude ?? 0, + heading: roverDados.DadosLeitura?.Gnss?.OrientacaoReal ?? 0 + ); + } + } + } + } + + public void AtualizarDadosTela(OperacaoParametrosModel rover) + { + if (rover == null) return; + var obj = rover.DadosLeitura; + if (obj == null) return; + + var simulacao = obj.Controle?.SimulacaoMPC?.Select(x => new double[] { x.longitude, x.latitude })?.ToList(); + _viewOperacaoCenter?.Mapa?.markers.UpdateMarkerPosition(rover.RoverId, lat: obj.Gnss?.Latitude, lon: obj.Gnss?.Longitude, heading: obj.Gnss?.OrientacaoReal, predict: simulacao); + _viewOperacaoCenter?.Mapa?.markers.UpdateMarkerInfo(rover.RoverId, status: obj.Operacao?.Status ?? AgroBase.Models.Enums.StatusOperacao.NaoIniciado); + + //_viewOperacaoBottom?._vm?.AtualizarListaAlertasRoverSelecionado(rover.RoverId); + _viewOperacaoBottom?._vm?.AtualizarListaAlertasTodosRovers(); + + if (rover.RoverId != VariaveisControleOperacao.SelectedRoverId) return; + + var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == AgroBase.Models.Enums.T_Code.Ipb); + double c = 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; + string comunicacao = $"{conexao_latencia:0.00} ms {bwTot:0.00} Mbps"; + string comunicacao_extended = $"{conexao_latencia:0.00} ms {conexao_perda:F2}% {bwTot:0.00} Mbps"; + + _viewOperacaoLeft?._vm?.AtualizarRover( + rover?.Descricao ?? "", + obj.Controle?.PercentualVelocidadeSPKmh ?? 0, + obj.Controle?.Angulo ?? 0, + obj.Controle?.TipoMovimento ?? AgroBase.Models.Enums.TipoMovimentoDirecional.Diagnostico, + obj.Refrigeracao?.Temperatura ?? 0, + obj.Bateria?.PercentualBateria ?? 0, + obj.Atuador?.PercentualReservatorio ?? 0, + $"{c}%", + $"{obj.Gnss?.QualidadeFix ?? AgroBase.Models.Enums.TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm" + ); + + _viewOperacaoRight?._vm?.AtualizarResumo(r => + { + r.Operacao = (obj.Operacao?.Liberada ?? false) ? "🔓 Liberada" : "🔒 Bloqueada"; + r.Modo = obj.Operacao == null ? "-" : obj.Operacao.Modo.ToString(); + r.Status = obj.Operacao == null ? "-" : $"{obj.Operacao.Status}" + (obj.Operacao.Status == AgroBase.Models.Enums.StatusOperacao.Aguardando ? $" ({obj.Operacao.TempoAguardandoSegs:0}s)" : ""); + r.Carro = obj.Trajetoria == null ? "-" : obj.Trajetoria.StatusCarro.ToString(); + r.Temperatura = $"{(obj.PerformanceNcp?.CPU_temp ?? 0):0.00} °C"; + r.RuaAtual = obj.Trajetoria == null ? "-" : $"{obj.Trajetoria.CorredorAtualDistanciaPercorrida:0.00} m / " + $"{obj.Trajetoria.CorredorAtualDistanciaTotal:0.00} m " + $"({obj.Trajetoria.CorredorAtualIdx + 1})"; + r.AreaTotal = obj.Trajetoria == null ? "-" : $"{obj.Trajetoria.DistanciaPercorrida:0.00} m / " + $"{obj.Trajetoria.DistanciaTotal:0.00} m"; + r.Bateria = obj.Bateria == null ? "-" : $"{obj.Bateria.TensaoInstantanea:0.00} V " + $"({TimeSpan.FromMinutes(obj.Bateria.TempoEstimadoRestanteMinutos):dd\\.hh\\:mm\\:ss} " + $"{obj.Bateria.DistanciaEstimadaRestanteMetros:0.00} m)"; + r.Herbicida = obj.Atuador == null ? "-" : $"{obj.Atuador.VolumeReservatorioL:0.00} L " + $"({TimeSpan.FromMinutes(obj.Atuador.TempoEstimadoRestanteMinutos):hh\\:mm\\:ss} " + $"{obj.Atuador.DistanciaEstimadaRestanteMetros:0.00} m)"; + r.Infestacao = obj.Atuador == null ? "-" : $"{obj.Atuador.HerbicidaPorAtuacaoMl:0.00} mL/atuação"; + r.Conexao = comunicacao == null ? "-" : $"{comunicacao}"; + r.Duracao = obj.Operacao == null ? "00:00:00 / 00:00:00" : $"{TimeSpan.FromSeconds(obj.Operacao.TempoDecorridoSegs):hh\\:mm\\:ss} / " + $"{(string.IsNullOrWhiteSpace(obj.Trajetoria?.TempoEstimadoOperacao) ? "00:00:00" : obj.Trajetoria.TempoEstimadoOperacao)}"; + + // Opcionais para progress bars, caso queira evoluir o resumo + r.RuaAtualPercentual = obj.Trajetoria?.PercentualRuaAtual ?? 0; + r.AreaTotalPercentual = obj.Trajetoria?.PercentualOperacao ?? 0; + r.BateriaPercentual = obj.Bateria?.PercentualBateria ?? 0; + r.HerbicidaPercentual = obj.Atuador?.PercentualReservatorio ?? 0; + r.InfestacaoPercentual = obj.Atuador?.PercentualErvasTerreno ?? 0; + }); + + _viewOperacaoCenter?.Diagnostico?._vm?.AtualizarDados(rover); + _viewOperacaoCenter?.Diagnostico?.AtualizarGraficos(); + + var bomba = obj.Atuador?.Bombas?.FirstOrDefault(); + var ATU = _viewOperacaoCenter.Monitoramento.Pulverizador; + ATU.ComprimentoBarraCm = AgroBase.Models.VariaveisEquipamento.ComprimentoBarraPulverizadoraCm; + ATU.DistanciaEntreBicosCm = AgroBase.Models.VariaveisEquipamento.DistanciaEntreBicosCm; + ATU.AlturaBarra = obj?.Controle?.AlturaBarra ?? AgroBase.Models.VariaveisEquipamento.AlturaBarraPulverizadoraCm; + ATU.IsBombaOn = bomba?.LeituraEstado ?? false; + ATU.PressaoLinha = obj?.Atuador?.PressaoLinhaPsi ?? 0; + ATU.VazaoInstantanea = obj?.Atuador?.VazaoInstantaneaMLs ?? 0; + ATU.TempoAtuado = (bomba?.TempoAtuado ?? 0) / 1000.0; + + double erroLateral = ((obj?.Trajetoria?.DistanciaEsquerda ?? 0) - (obj?.Trajetoria?.DistanciaDireita ?? 0)) / 2.0 * 100.0; + ATU.LarguraRuaCm = 100.0; + ATU.ErroLateralCm = erroLateral; + + if (ATU.QtdBicos != obj?.Atuador?.Bicos?.Count) + { + ATU.QtdBicos = obj?.Atuador?.Bicos?.Count ?? AgroBase.Models.VariaveisEquipamento.QuantidadeBicosPulverizadores; + } + + foreach (var bico in ATU.Bicos) + { + var _bc = obj?.Atuador?.Bicos?.FirstOrDefault(x => (x.Posicao - 1) == bico.Index); + if (_bc == null) continue; + + bico.OpeningAngle = _bc.AnguloAbertura; + bico.AnguloControle = _bc.ComandoAngulo; + bico.IsCommandOn = _bc.ComandoEstado; + bico.IsSpraying = _bc.LeituraEstado; + bico.Vazao = _bc.VazaoInstantaneaMLs; + bico.Atuacoes = _bc.QtdAtuacoes; + bico.TempoAtuado = _bc.TempoAtuado / 1000.0; + } + } + public void AtualizarTopOperacaoBase() { @@ -558,117 +675,12 @@ namespace OperationControl.ViewModels } - public void AtualizarListaRovers(List rovers_atual) - { - var cards = rovers_atual.Select(x => new RoverCardModel() - { - EquipamentoId = x.IP, - NumeroSerie = x.RoverId, - Status = x.DadosLeitura?.StatusRover ?? AgroBase.Models.Enums.StatusModulo.Desconectado - }).ToArray(); - - _viewOperacaoRight?._vm?.AtualizarListaRovers(cards); - - foreach (var rover in cards) - { - if (!_viewOperacaoCenter?.MapaOperacao?.markers?.Added(rover.NumeroSerie) ?? false) - { - if (rover.NumeroSerie == VariaveisControleOperacao.BaseMarkerID) - { - _viewOperacaoCenter?.MapaOperacao?.markers?.AddMarker( - rover.NumeroSerie, - true, - lat: Variaveis.GpsService?.UltimaLeitura?.Latitude ?? 0, - lon: Variaveis.GpsService?.UltimaLeitura?.Longitude ?? 0, - heading: Variaveis.GpsService?.UltimaLeitura?.OrientacaoReal ?? 0, - label: "Base" - ); - } - else - { - var roverDados = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == rover.NumeroSerie); - if (roverDados != null) - { - _viewOperacaoCenter?.MapaOperacao?.markers?.AddMarker( - rover.NumeroSerie, - false, - lat: roverDados.DadosLeitura?.Gnss?.Latitude ?? 0, - lon: roverDados.DadosLeitura?.Gnss?.Longitude ?? 0, - heading: roverDados.DadosLeitura?.Gnss?.OrientacaoReal ?? 0 - ); - } - } - } - } - } - - public void AtualizarDadosTela(OperacaoParametrosModel rover) - { - if (rover == null) return; - var obj = rover.DadosLeitura; - if (obj == null) return; - - var simulacao = obj.Controle?.SimulacaoMPC?.Select(x => new double[] { x.longitude, x.latitude })?.ToList(); - _viewOperacaoCenter?.MapaOperacao?.markers.UpdateMarkerPosition(rover.RoverId, lat: obj.Gnss?.Latitude, lon: obj.Gnss?.Longitude, heading: obj.Gnss?.OrientacaoReal, predict: simulacao); - _viewOperacaoCenter?.MapaOperacao?.markers.UpdateMarkerInfo(rover.RoverId, status: obj.Operacao?.Status ?? AgroBase.Models.Enums.StatusOperacao.NaoIniciado); - - _viewOperacaoBottom?._vm?.AtualizarListaAlertasRoverSelecionado(rover.RoverId); - - if (rover.RoverId == VariaveisControleOperacao.SelectedRoverId) - { - var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == AgroBase.Models.Enums.T_Code.Ipb); - double c = 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; - string comunicacao = $"{conexao_latencia:0.00} ms {bwTot:0.00} Mbps"; - string comunicacao_extended = $"{conexao_latencia:0.00} ms {conexao_perda:F2}% {bwTot:0.00} Mbps"; - - _viewOperacaoLeft?._vm?.AtualizarRover( - rover?.Descricao ?? "", - obj.Controle?.PercentualVelocidadeSPKmh ?? 0, - obj.Controle?.Angulo ?? 0, - obj.Controle?.TipoMovimento ?? AgroBase.Models.Enums.TipoMovimentoDirecional.Diagnostico, - obj.Refrigeracao?.Temperatura ?? 0, - obj.Bateria?.PercentualBateria ?? 0, - obj.Atuador?.PercentualReservatorio ?? 0, - $"{c}%", - $"{obj.Gnss?.QualidadeFix ?? AgroBase.Models.Enums.TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm" - ); - - _viewOperacaoRight?._vm?.AtualizarResumo(r => - { - r.Operacao = (obj.Operacao?.Liberada ?? false) ? "🔓 Liberada" : "🔒 Bloqueada"; - r.Modo = obj.Operacao == null ? "-" : obj.Operacao.Modo.ToString(); - r.Status = obj.Operacao == null ? "-" : $"{obj.Operacao.Status}" + (obj.Operacao.Status == AgroBase.Models.Enums.StatusOperacao.Aguardando ? $" ({obj.Operacao.TempoAguardandoSegs:0}s)" : ""); - r.Carro = obj.Trajetoria == null ? "-" : obj.Trajetoria.StatusCarro.ToString(); - r.Temperatura = $"{(obj.PerformanceNcp?.CPU_temp ?? 0):0.00} °C"; - r.RuaAtual = obj.Trajetoria == null ? "-" : $"{obj.Trajetoria.CorredorAtualDistanciaPercorrida:0.00} m / " + $"{obj.Trajetoria.CorredorAtualDistanciaTotal:0.00} m " + $"({obj.Trajetoria.CorredorAtualIdx + 1})"; - r.AreaTotal = obj.Trajetoria == null ? "-" : $"{obj.Trajetoria.DistanciaPercorrida:0.00} m / " + $"{obj.Trajetoria.DistanciaTotal:0.00} m"; - r.Bateria = obj.Bateria == null ? "-" : $"{obj.Bateria.TensaoInstantanea:0.00} V " + $"({TimeSpan.FromMinutes(obj.Bateria.TempoEstimadoRestanteMinutos):dd\\.hh\\:mm\\:ss} " + $"{obj.Bateria.DistanciaEstimadaRestanteMetros:0.00} m)"; - r.Herbicida = obj.Atuador == null ? "-" : $"{obj.Atuador.VolumeReservatorioL:0.00} L " + $"({TimeSpan.FromMinutes(obj.Atuador.TempoEstimadoRestanteMinutos):hh\\:mm\\:ss} " + $"{obj.Atuador.DistanciaEstimadaRestanteMetros:0.00} m)"; - r.Infestacao = obj.Atuador == null ? "-" : $"{obj.Atuador.HerbicidaPorAtuacaoMl:0.00} mL/atuação"; - r.Conexao = comunicacao == null ? "-" : $"{comunicacao}"; - r.Duracao = obj.Operacao == null ? "00:00:00 / 00:00:00" : $"{TimeSpan.FromSeconds(obj.Operacao.TempoDecorridoSegs):hh\\:mm\\:ss} / " + $"{(string.IsNullOrWhiteSpace(obj.Trajetoria?.TempoEstimadoOperacao) ? "00:00:00" : obj.Trajetoria.TempoEstimadoOperacao)}"; - - // Opcionais para progress bars, caso queira evoluir o resumo - r.RuaAtualPercentual = obj.Trajetoria?.PercentualRuaAtual ?? 0; - r.AreaTotalPercentual = obj.Trajetoria?.PercentualOperacao ?? 0; - r.BateriaPercentual = obj.Bateria?.PercentualBateria ?? 0; - r.HerbicidaPercentual = obj.Atuador?.PercentualReservatorio ?? 0; - r.InfestacaoPercentual = obj.Atuador?.PercentualErvasTerreno ?? 0; - }); - - _viewOperacaoCenter?.Diagnostico?._vm?.AtualizarDados(rover); - _viewOperacaoCenter?.Diagnostico?.AtualizarGraficos(); - } - } - + + private void OnRoverSelecionadoChanged(string roverId) { _viewOperacaoRight?._vm?.DestacarRoverNoMapa(roverId); } - private void OnRoverSelecionadoChanged(RoverCardModel rover) { bool dados_base = rover == null; @@ -683,7 +695,7 @@ namespace OperationControl.ViewModels VariaveisControleOperacao.RoverEmFoco?.RuasPercorrer?.Clear(); AtualizarDadosMapa(VariaveisControleOperacao.RoverEmFoco); VariaveisControleOperacao.SelectedRoverId = VariaveisControleOperacao.BaseMarkerID; - _viewOperacaoCenter?.MapaOperacao?.markers?.SetMarkerFocused(VariaveisControleOperacao.SelectedRoverId, true); + _viewOperacaoCenter?.Mapa?.markers?.SetMarkerFocused(VariaveisControleOperacao.SelectedRoverId, true); _viewOperacaoCenter?._vm?.SelecionarRoverLista(rover); @@ -693,7 +705,7 @@ namespace OperationControl.ViewModels else { VariaveisControleOperacao.SelectedRoverId = rover.NumeroSerie.ToString(); - _viewOperacaoCenter?.MapaOperacao?.markers?.SetMarkerFocused(VariaveisControleOperacao.SelectedRoverId, true); + _viewOperacaoCenter?.Mapa?.markers?.SetMarkerFocused(VariaveisControleOperacao.SelectedRoverId, true); AtualizarDadosTela(VariaveisControleOperacao.RoverEmFoco); VariaveisControleOperacao.RequisitarParametrosOperacao(); @@ -703,12 +715,15 @@ namespace OperationControl.ViewModels _viewOperacaoCenter?._vm?.SelecionarRoverLista(rover); + AtualizarDadosMapa(VariaveisControleOperacao.RoverEmFoco); + AtualizarTopOperacaoRover(VariaveisControleOperacao.SelectedRoverId, ModoOperacaoAtual == ModoOperacao.Manual); } } private void OnSecaoSelecionadaChanged(SecaoRightBar secao) { _viewOperacaoCenter?._vm?.DefinirSecao(secao); + AtualizarDadosMapa(VariaveisControleOperacao.RoverEmFoco); } private void OnLerParametrosSolicitado() @@ -727,7 +742,7 @@ namespace OperationControl.ViewModels var rover = VariaveisControleOperacao.RoverEmFoco; - VariaveisControleOperacao.EnviarParametrosOperacao(new OperacaoParametrosModel() + var novosParametros = new OperacaoParametrosModel() { Modo = parametros.Modo ?? AgroBase.Models.Enums.ModoOperacao.NaoDefinido, Descricao = parametros.Descricao, @@ -740,9 +755,20 @@ namespace OperationControl.ViewModels ModulosMandatorios = rover?.ModulosMandatorios, ParametrosMandatorios = rover?.ParametrosMandatorios, - RuasPercorrer = _viewOperacaoCenter?.MapaOperacao?.RuasMapaCarregado?.Where(x => x.Selected)?.Select(x => x.Id)?.ToList(), - Mapa = _viewOperacaoCenter?.MapaOperacao?.CriarDadosMapa() - }); + RuasPercorrer = _viewOperacaoCenter?.Mapa?.RuasMapaCarregado?.Where(x => x.Selected)?.Select(x => x.Id)?.ToList(), + Mapa = _viewOperacaoCenter?.Mapa?.CriarDadosMapa() + }; + + VariaveisControleOperacao.EnviarParametrosOperacao(novosParametros); + + VariaveisControleOperacao.RoverEmFoco.Modo = novosParametros.Modo; + VariaveisControleOperacao.RoverEmFoco.Descricao = novosParametros.Descricao; + VariaveisControleOperacao.RoverEmFoco.QtdCamerasSolo = novosParametros.QtdCamerasSolo; + VariaveisControleOperacao.RoverEmFoco.QtdBicos = novosParametros.QtdBicos; + VariaveisControleOperacao.RoverEmFoco.CapacidadeReservatorio = novosParametros.CapacidadeReservatorio; + VariaveisControleOperacao.RoverEmFoco.Controle = novosParametros.Controle; + VariaveisControleOperacao.RoverEmFoco.RuasPercorrer = novosParametros.RuasPercorrer; + VariaveisControleOperacao.RoverEmFoco.Mapa = novosParametros.Mapa; } diff --git a/AgroBase/OperationControl/Views/Operacao/MonitoramentoView.xaml b/AgroBase/OperationControl/Views/Operacao/MonitoramentoView.xaml index ebb10f6df..6cf76f5b7 100644 --- a/AgroBase/OperationControl/Views/Operacao/MonitoramentoView.xaml +++ b/AgroBase/OperationControl/Views/Operacao/MonitoramentoView.xaml @@ -87,49 +87,23 @@ - + - + - + - + - + - + @@ -137,12 +111,7 @@ - + @@ -157,12 +126,12 @@ + Margin="0,0,0,6" + Padding="10" + CornerRadius="10" + Background="#202020" + BorderBrush="#353535" + BorderThickness="1"> @@ -171,27 +140,21 @@ + Text="Pulverizador" + Margin="0,0,0,8" + FontSize="12" + FontWeight="SemiBold" + Foreground="#E6E6E6"/> - - - + - + @@ -199,12 +162,7 @@ - +