From d0e58be55378f649d72670f65dd547485e43095c Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Tue, 10 Mar 2026 16:56:55 -0300 Subject: [PATCH] Implementado novo layout para OperationControl --- .../Operacoes/OperacaoParametrosModel.cs | 473 ++++++++++- .../Controls/MapViewControl.xaml.cs | 71 +- .../Converters/BoolToVisibility.cs | 27 + AgroBase/OperationControl/Models/AppShell.cs | 20 +- .../Models/Operacao/ResumoOperacionalModel.cs | 61 ++ AgroBase/OperationControl/Models/Variaveis.cs | 17 +- .../OperationControl.csproj.user | 52 +- .../OperationControl/Services/GpsService.cs | 6 +- .../ViewModels/DockWindowViewModel.cs | 118 --- .../ConfigConexao}/ConfigConexaoViewModel.cs | 81 +- .../ConfigConexao}/NetworkConfigViewModel.cs | 0 .../Views/Operacao/OperacaoBottomViewModel.cs | 249 ++++++ .../Views/Operacao/OperacaoCenterViewModel.cs | 149 ++++ .../Views/Operacao/OperacaoLeftViewModel.cs | 349 ++++++++ .../Views/Operacao/OperacaoRightViewModel.cs | 344 ++++++++ .../Views/Operacao/OperacaoTopViewModel.cs | 142 ++++ .../PreparacaoMapaBottomViewModel.cs | 148 ++++ .../PreparacaoMapaTopViewModel.cs | 121 +++ .../ViewModels/Windows/DockWindowViewModel.cs | 751 ++++++++++++++++++ .../ConfigConexao/ConfigConexaoView.xaml | 159 ++++ .../ConfigConexaoView.xaml.cs | 0 .../Views/ConfigConexaoView.xaml | 107 --- .../Views/Operacao/OperacaoBottomView.xaml | 203 +++++ .../Views/Operacao/OperacaoBottomView.xaml.cs | 68 ++ .../Views/Operacao/OperacaoCenterView.xaml | 74 ++ .../Views/Operacao/OperacaoCenterView.xaml.cs | 88 ++ .../Views/Operacao/OperacaoLeftView.xaml | 223 ++++++ .../Views/Operacao/OperacaoLeftView.xaml.cs | 35 + .../Views/Operacao/OperacaoRightView.xaml | 497 ++++++++++++ .../Views/Operacao/OperacaoRightView.xaml.cs | 40 + .../Views/Operacao/OperacaoTopView.xaml | 73 ++ .../Views/Operacao/OperacaoTopView.xaml.cs | 35 + .../PreparacaoMapaBottomView.xaml | 79 ++ .../PreparacaoMapaBottomView.xaml.cs | 33 + .../PreparacaoMapa/PreparacaoMapaTopView.xaml | 73 ++ .../PreparacaoMapaTopView.xaml.cs | 33 + .../PreparacaoMapa/PreparacaoMapaView.xaml | 14 + .../PreparacaoMapa/PreparacaoMapaView.xaml.cs | 31 + .../OperationControl/Windows/DockWindow.xaml | 67 +- .../Windows/DockWindow.xaml.cs | 4 +- 40 files changed, 4804 insertions(+), 311 deletions(-) create mode 100644 AgroBase/OperationControl/Converters/BoolToVisibility.cs create mode 100644 AgroBase/OperationControl/Models/Operacao/ResumoOperacionalModel.cs delete mode 100644 AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs rename AgroBase/OperationControl/ViewModels/{ => Views/ConfigConexao}/ConfigConexaoViewModel.cs (68%) rename AgroBase/OperationControl/ViewModels/{ => Views/ConfigConexao}/NetworkConfigViewModel.cs (100%) create mode 100644 AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoCenterViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoRightViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs create mode 100644 AgroBase/OperationControl/Views/ConfigConexao/ConfigConexaoView.xaml rename AgroBase/OperationControl/Views/{ => ConfigConexao}/ConfigConexaoView.xaml.cs (100%) delete mode 100644 AgroBase/OperationControl/Views/ConfigConexaoView.xaml create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoBottomView.xaml create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoBottomView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoCenterView.xaml create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoCenterView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoLeftView.xaml create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoLeftView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoRightView.xaml create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoRightView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoTopView.xaml create mode 100644 AgroBase/OperationControl/Views/Operacao/OperacaoTopView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaBottomView.xaml create mode 100644 AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaBottomView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaTopView.xaml create mode 100644 AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaTopView.xaml.cs create mode 100644 AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaView.xaml create mode 100644 AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaView.xaml.cs diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs index 253b34c55..6b6511b83 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs @@ -3,6 +3,7 @@ using AgroBase.Models.Operadores; using AgroBase.Services; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using static AgroBase.Models.Enums; @@ -10,66 +11,460 @@ namespace AgroBase.Models.Operacoes { public class OperacaoParametrosModel { - public string RoverId { get; set; } - public string IP { get; set; } - public bool Alive { get; set; } + private string _roverId; + public string RoverId + { + get => _roverId; + set + { + if (_roverId != value) + { + _roverId = value; + OnPropertyChanged(nameof(RoverId)); + } + } + } + + private string _ip; + public string IP + { + get => _ip; + set + { + if (_ip != value) + { + _ip = value; + OnPropertyChanged(nameof(IP)); + } + } + } + + private bool _alive; + public bool Alive + { + get => _alive; + set + { + if (_alive != value) + { + _alive = value; + OnPropertyChanged(nameof(Alive)); + } + } + } + + private ModoOperacao? _modo; + public ModoOperacao? Modo + { + get => _modo; + set + { + if (_modo != value) + { + _modo = value; + OnPropertyChanged(nameof(Modo)); + } + } + } + + private string _descricao; + public string Descricao + { + get => _descricao; + set + { + if (_descricao != value) + { + _descricao = value; + OnPropertyChanged(nameof(Descricao)); + } + } + } + + private int? _qtdCamerasSolo; + public int? QtdCamerasSolo + { + get => _qtdCamerasSolo; + set + { + if (_qtdCamerasSolo != value) + { + _qtdCamerasSolo = value; + OnPropertyChanged(nameof(QtdCamerasSolo)); + } + } + } + + private int? _qtdBicos; + public int? QtdBicos + { + get => _qtdBicos; + set + { + if (_qtdBicos != value) + { + _qtdBicos = value; + OnPropertyChanged(nameof(QtdBicos)); + } + } + } + + private double? _capacidadeReservatorio; + public double? CapacidadeReservatorio + { + get => _capacidadeReservatorio; + set + { + if (_capacidadeReservatorio != value) + { + _capacidadeReservatorio = value; + OnPropertyChanged(nameof(CapacidadeReservatorio)); + } + } + } + + private OperacaoParametrosControleModel _controle; + public OperacaoParametrosControleModel Controle + { + get => _controle; + set + { + if (_controle != value) + { + _controle = value; + OnPropertyChanged(nameof(Controle)); + } + } + } + + public DateTime UltimoContato { get; set; } public bool Configurado { get; set; } - public ModoOperacao? Modo { get; set; } - public string Descricao { get; set; } - public int? QtdCamerasSolo { get; set; } - public int? QtdBicos { get; set; } - public double? CapacidadeReservatorio { get; set; } public List ModulosMandatorios { get; set; } public List ParametrosMandatorios { get; set; } - public OperacaoParametrosControleModel Controle { get; set; } public MapaFeatureCollectionModel Mapa { get; set; } public List RuasPercorrer { get; set; } public OperacaoParametrosDadosModel DadosLeitura { get; set; } + + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } } - public class OperacaoParametrosControleModel + public class OperacaoParametrosControleModel : INotifyPropertyChanged { - public bool MovimentoAutomatico { get; set; } - public bool DirecionalAutomatico { get; set; } - public bool PulverizadorAutomatico { get; set; } - public bool SonarAtivado { get; set; } - public bool OakParadaPorObstaculo { get; set; } - public bool ImuParadaPorInclinacao { get; set; } - public bool FrenagemAutomaticaAoParar { get; set; } - - public bool RegistrarDadosPosProcessamento { get; set; } - - public int MovVelocidadeSErvasPercent { get; set; } - public int MovRpmMax + private bool _movimentoAutomatico; + public bool MovimentoAutomatico { - get + get => _movimentoAutomatico; + set { - return System.Convert.ToInt32(VariaveisEquipamento.RPM_Max_Roda * (MovVelocidadeSErvasPercent / 100.0)); + if (_movimentoAutomatico != value) + { + _movimentoAutomatico = value; + OnPropertyChanged(nameof(MovimentoAutomatico)); + } } } - public int MovVelocidadeCErvasPercent { get; set; } - public int MovRpmMin + + private bool _direcionalAutomatico; + public bool DirecionalAutomatico { - get + get => _direcionalAutomatico; + set { - return System.Convert.ToInt32(VariaveisEquipamento.RPM_Max_Roda * (MovVelocidadeCErvasPercent / 100.0)); + if (_direcionalAutomatico != value) + { + _direcionalAutomatico = value; + OnPropertyChanged(nameof(DirecionalAutomatico)); + } } } - public int DirAnguloMaximo { get; set; } - public double DirVelocidadeMovimento { get; set; } - public TiposControladorDirecional DirTipoMovimento { get; set; } - public double AtuPressaoLinha { get; set; } - public int AtuDuracaoAtuacao { get; set; } - public double AtuPercentualInicioPulverizacao { get; set; } - public double AtuAlturaAreaPulverizacao { get; set; } - public double AtuPercentualErvasBicoOn { get; set; } - public double AtuPercentualErvasBicoOff { get; set; } - public bool MpcMatrizCusto { get; set; } - public double MpcHorizonte { get; set; } = 4.0; + private bool _pulverizadorAutomatico; + public bool PulverizadorAutomatico + { + get => _pulverizadorAutomatico; + set + { + if (_pulverizadorAutomatico != value) + { + _pulverizadorAutomatico = value; + OnPropertyChanged(nameof(PulverizadorAutomatico)); + } + } + } + + private bool _sonarAtivado; + public bool SonarAtivado + { + get => _sonarAtivado; + set + { + if (_sonarAtivado != value) + { + _sonarAtivado = value; + OnPropertyChanged(nameof(SonarAtivado)); + } + } + } + + private bool _oakParadaPorObstaculo; + public bool OakParadaPorObstaculo + { + get => _oakParadaPorObstaculo; + set + { + if (_oakParadaPorObstaculo != value) + { + _oakParadaPorObstaculo = value; + OnPropertyChanged(nameof(OakParadaPorObstaculo)); + } + } + } + + private bool _imuParadaPorInclinacao; + public bool ImuParadaPorInclinacao + { + get => _imuParadaPorInclinacao; + set + { + if (_imuParadaPorInclinacao != value) + { + _imuParadaPorInclinacao = value; + OnPropertyChanged(nameof(ImuParadaPorInclinacao)); + } + } + } + + private bool _frenagemAutomaticaAoParar; + public bool FrenagemAutomaticaAoParar + { + get => _frenagemAutomaticaAoParar; + set + { + if (_frenagemAutomaticaAoParar != value) + { + _frenagemAutomaticaAoParar = value; + OnPropertyChanged(nameof(FrenagemAutomaticaAoParar)); + } + } + } + + private bool _registrarDadosPosProcessamento; + public bool RegistrarDadosPosProcessamento + { + get => _registrarDadosPosProcessamento; + set + { + if (_registrarDadosPosProcessamento != value) + { + _registrarDadosPosProcessamento = value; + OnPropertyChanged(nameof(RegistrarDadosPosProcessamento)); + } + } + } + + private int _movVelocidadeSErvasPercent; + public int MovVelocidadeSErvasPercent + { + get => _movVelocidadeSErvasPercent; + set + { + if (_movVelocidadeSErvasPercent != value) + { + _movVelocidadeSErvasPercent = value; + OnPropertyChanged(nameof(MovVelocidadeSErvasPercent)); + OnPropertyChanged(nameof(MovRpmMax)); + } + } + } + + public int MovRpmMax => System.Convert.ToInt32(VariaveisEquipamento.RPM_Max_Roda * (MovVelocidadeSErvasPercent / 100.0)); + + private int _movVelocidadeCErvasPercent; + public int MovVelocidadeCErvasPercent + { + get => _movVelocidadeCErvasPercent; + set + { + if (_movVelocidadeCErvasPercent != value) + { + _movVelocidadeCErvasPercent = value; + OnPropertyChanged(nameof(MovVelocidadeCErvasPercent)); + OnPropertyChanged(nameof(MovRpmMin)); + } + } + } + + public int MovRpmMin => System.Convert.ToInt32(VariaveisEquipamento.RPM_Max_Roda * (MovVelocidadeCErvasPercent / 100.0)); + + private int _dirAnguloMaximo; + public int DirAnguloMaximo + { + get => _dirAnguloMaximo; + set + { + if (_dirAnguloMaximo != value) + { + _dirAnguloMaximo = value; + OnPropertyChanged(nameof(DirAnguloMaximo)); + } + } + } + + private double _dirVelocidadeMovimento; + public double DirVelocidadeMovimento + { + get => _dirVelocidadeMovimento; + set + { + if (_dirVelocidadeMovimento != value) + { + _dirVelocidadeMovimento = value; + OnPropertyChanged(nameof(DirVelocidadeMovimento)); + } + } + } + + private TiposControladorDirecional _dirTipoMovimento; + public TiposControladorDirecional DirTipoMovimento + { + get => _dirTipoMovimento; + set + { + if (_dirTipoMovimento != value) + { + _dirTipoMovimento = value; + OnPropertyChanged(nameof(DirTipoMovimento)); + } + } + } + + private double _atuPressaoLinha; + public double AtuPressaoLinha + { + get => _atuPressaoLinha; + set + { + if (_atuPressaoLinha != value) + { + _atuPressaoLinha = value; + OnPropertyChanged(nameof(AtuPressaoLinha)); + } + } + } + + private int _atuDuracaoAtuacao; + public int AtuDuracaoAtuacao + { + get => _atuDuracaoAtuacao; + set + { + if (_atuDuracaoAtuacao != value) + { + _atuDuracaoAtuacao = value; + OnPropertyChanged(nameof(AtuDuracaoAtuacao)); + } + } + } + + private double _atuPercentualInicioPulverizacao; + public double AtuPercentualInicioPulverizacao + { + get => _atuPercentualInicioPulverizacao; + set + { + if (_atuPercentualInicioPulverizacao != value) + { + _atuPercentualInicioPulverizacao = value; + OnPropertyChanged(nameof(AtuPercentualInicioPulverizacao)); + } + } + } + + private double _atuAlturaAreaPulverizacao; + public double AtuAlturaAreaPulverizacao + { + get => _atuAlturaAreaPulverizacao; + set + { + if (_atuAlturaAreaPulverizacao != value) + { + _atuAlturaAreaPulverizacao = value; + OnPropertyChanged(nameof(AtuAlturaAreaPulverizacao)); + } + } + } + + private double _atuPercentualErvasBicoOn; + public double AtuPercentualErvasBicoOn + { + get => _atuPercentualErvasBicoOn; + set + { + if (_atuPercentualErvasBicoOn != value) + { + _atuPercentualErvasBicoOn = value; + OnPropertyChanged(nameof(AtuPercentualErvasBicoOn)); + } + } + } + + private double _atuPercentualErvasBicoOff; + public double AtuPercentualErvasBicoOff + { + get => _atuPercentualErvasBicoOff; + set + { + if (_atuPercentualErvasBicoOff != value) + { + _atuPercentualErvasBicoOff = value; + OnPropertyChanged(nameof(AtuPercentualErvasBicoOff)); + } + } + } + + private bool _mpcMatrizCusto; + public bool MpcMatrizCusto + { + get => _mpcMatrizCusto; + set + { + if (_mpcMatrizCusto != value) + { + _mpcMatrizCusto = value; + OnPropertyChanged(nameof(MpcMatrizCusto)); + } + } + } + + private double _mpcHorizonte = 4.0; + public double MpcHorizonte + { + get => _mpcHorizonte; + set + { + if (_mpcHorizonte != value) + { + _mpcHorizonte = value; + OnPropertyChanged(nameof(MpcHorizonte)); + } + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } } public class OperacaoParametrosDadosModel diff --git a/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs b/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs index 93c834330..625c86d10 100644 --- a/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs +++ b/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs @@ -31,6 +31,7 @@ using Mapsui.Manipulations; using Mapsui.Extensions; using OperationControl.Models; using static AgroBase.Models.Enums; +using System.Windows.Input; namespace OperationControl.Controls @@ -66,6 +67,34 @@ namespace OperationControl.Controls set => SetValue(ScaleProperty, value); } + + public static readonly DependencyProperty MarkerClickedCommandProperty = + DependencyProperty.Register( + nameof(MarkerClickedCommand), + typeof(ICommand), + typeof(MapViewControl), + new PropertyMetadata(null)); + + public ICommand MarkerClickedCommand + { + get => (ICommand)GetValue(MarkerClickedCommandProperty); + set => SetValue(MarkerClickedCommandProperty, value); + } + + public static readonly DependencyProperty StreetMapClickedCommandProperty = + DependencyProperty.Register( + nameof(StreetMapClickedCommand), + typeof(ICommand), + typeof(MapViewControl), + new PropertyMetadata(null)); + + public ICommand StreetMapClickedCommand + { + get => (ICommand)GetValue(StreetMapClickedCommandProperty); + set => SetValue(StreetMapClickedCommandProperty, value); + } + + #region MARCADORES public MapMarkerManager markers; @@ -77,22 +106,36 @@ namespace OperationControl.Controls MarkerClicked?.Invoke(this, new MapMarkerManager.MarkerClickedEventArgs(markerId)); } + private void OnMarkerClickedInternal(MapMarkerManager.MarkerClickedEventArgs e) + { + MarkerClicked?.Invoke(this, e); + + if (MarkerClickedCommand?.CanExecute(e) == true) + MarkerClickedCommand.Execute(e); + } + #endregion - - - #region RUAS PLANTACAO private Layer MapaPlantacao_Layer; 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; protected virtual void OnStreetMapClicked(string streetId) { StreetMapClicked?.Invoke(this, new StreetMapClickedEventArgs(streetId, RuasMapaCarregado.Where(x => x.Selected).Select(x => x.Id).ToList())); } + private void OnStreetMapClickedInternal(StreetMapClickedEventArgs e) + { + StreetMapClicked?.Invoke(this, e); + + if (StreetMapClickedCommand?.CanExecute(e) == true) + StreetMapClickedCommand.Execute(e); + } + private void HighlightRoad(MIFeature? feature = null, string? id = null) { if (string.IsNullOrEmpty(id)) @@ -487,7 +530,7 @@ namespace OperationControl.Controls } else if (info?.Layer == MapaPlantacao_Layer && feat is MIFeature roadFeature) { - if (!(VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Iniciada ?? false)) + if (ParametrizandoOperacao) HighlightRoad(roadFeature); } else @@ -509,7 +552,7 @@ namespace OperationControl.Controls if (feat != null && markers != null && markers.TryGetIdFromFeature(feat, out _)) Mapa.Cursor = System.Windows.Input.Cursors.Hand; - else if (feat != null && info?.Layer == MapaPlantacao_Layer && feat is MIFeature roadFeature) + else if (feat != null && info?.Layer == MapaPlantacao_Layer && feat is MIFeature roadFeature && ParametrizandoOperacao) Mapa.Cursor = System.Windows.Input.Cursors.Hand; else Mapa.Cursor = System.Windows.Input.Cursors.Arrow; @@ -756,6 +799,24 @@ namespace OperationControl.Controls NavigateToView(); } + + public void LoadMapFile(string path) + { + if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) + return; + + if (path.EndsWith(".shp", StringComparison.OrdinalIgnoreCase)) + { + var geojson = ConvertShpToGeoJson(path); + AddGeoJsonLayer(geojson); + } + else + { + var geojson = File.ReadAllText(path); + AddGeoJsonLayer(geojson); + } + } + #endregion diff --git a/AgroBase/OperationControl/Converters/BoolToVisibility.cs b/AgroBase/OperationControl/Converters/BoolToVisibility.cs new file mode 100644 index 000000000..2743fc04a --- /dev/null +++ b/AgroBase/OperationControl/Converters/BoolToVisibility.cs @@ -0,0 +1,27 @@ +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.Converters +{ + public class BoolToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is bool b && b) + return Visibility.Visible; + + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is Visibility v && v == Visibility.Visible; + } + } +} diff --git a/AgroBase/OperationControl/Models/AppShell.cs b/AgroBase/OperationControl/Models/AppShell.cs index 1278e170f..3c2acf7df 100644 --- a/AgroBase/OperationControl/Models/AppShell.cs +++ b/AgroBase/OperationControl/Models/AppShell.cs @@ -1,8 +1,8 @@ using AgroBase.Models; using AgroBase.Services; -using OperationControl.Services; +using OperationControl.ViewModels; using OperationControl.Windows; -using System.Diagnostics; +using static AgroBase.Models.Enums; namespace OperationControl.Models { @@ -55,5 +55,21 @@ namespace OperationControl.Models } } + public void AdicionarAlertaDock(string roverId, T_Code modulo, SeveridadeAlerta severidade, string mensagem, string modId = null) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + (Dock?.DataContext as DockWindowViewModel)?.AdicionarAlerta(roverId, modulo, severidade, mensagem, modId); + })); + } + + public void RemoverAlertaDock(string roverId, T_Code modulo, SeveridadeAlerta? severidade = null, string modId = null) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + (Dock?.DataContext as DockWindowViewModel)?.RemoverAlerta(roverId, modulo, severidade, modId); + })); + } + } } diff --git a/AgroBase/OperationControl/Models/Operacao/ResumoOperacionalModel.cs b/AgroBase/OperationControl/Models/Operacao/ResumoOperacionalModel.cs new file mode 100644 index 000000000..48acbf315 --- /dev/null +++ b/AgroBase/OperationControl/Models/Operacao/ResumoOperacionalModel.cs @@ -0,0 +1,61 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace OperationControl.Models.Operacao +{ + public class ResumoOperacionalModel : INotifyPropertyChanged + { + private string _operacao; + private string _modo; + private string _status; + private string _carro; + private string _temperatura; + private string _ruaAtual; + private string _areaTotal; + private string _bateria; + private string _herbicida; + private string _infestacao; + private string _conexao; + private string _duracao; + + private double _ruaAtualPercentual; + private double _areaTotalPercentual; + private double _bateriaPercentual; + private double _herbicidaPercentual; + private double _infestacaoPercentual; + + public string Operacao { get => _operacao; set => SetField(ref _operacao, value); } + public string Modo { get => _modo; set => SetField(ref _modo, value); } + public string Status { get => _status; set => SetField(ref _status, value); } + public string Carro { get => _carro; set => SetField(ref _carro, value); } + public string Temperatura { get => _temperatura; set => SetField(ref _temperatura, value); } + public string RuaAtual { get => _ruaAtual; set => SetField(ref _ruaAtual, value); } + public string AreaTotal { get => _areaTotal; set => SetField(ref _areaTotal, value); } + public string Bateria { get => _bateria; set => SetField(ref _bateria, value); } + public string Herbicida { get => _herbicida; set => SetField(ref _herbicida, value); } + public string Infestacao { get => _infestacao; set => SetField(ref _infestacao, value); } + public string Conexao { get => _conexao; set => SetField(ref _conexao, value); } + public string Duracao { get => _duracao; set => SetField(ref _duracao, value); } + + public double RuaAtualPercentual { get => _ruaAtualPercentual; set => SetField(ref _ruaAtualPercentual, value); } + public double AreaTotalPercentual { get => _areaTotalPercentual; set => SetField(ref _areaTotalPercentual, value); } + public double BateriaPercentual { get => _bateriaPercentual; set => SetField(ref _bateriaPercentual, value); } + public double HerbicidaPercentual { get => _herbicidaPercentual; set => SetField(ref _herbicidaPercentual, value); } + public double InfestacaoPercentual { get => _infestacaoPercentual; set => SetField(ref _infestacaoPercentual, value); } + + public event PropertyChangedEventHandler PropertyChanged; + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + } +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Models/Variaveis.cs b/AgroBase/OperationControl/Models/Variaveis.cs index 88c955974..e48ccb328 100644 --- a/AgroBase/OperationControl/Models/Variaveis.cs +++ b/AgroBase/OperationControl/Models/Variaveis.cs @@ -5,6 +5,7 @@ using OperationControl.Services; using AgroMonitor; using Application = System.Windows.Application; using AgroBase.Models.Operacoes; +using OperationControl.ViewModels; namespace OperationControl.Models { @@ -150,9 +151,10 @@ namespace OperationControl.Models if (string.IsNullOrEmpty(json)) return; var obj = JsonConvert.DeserializeObject(json); if (obj == null) return; + OperacaoParametrosModel rover; lock (_RoversLock) { - var rover = RoversNaRede.FirstOrDefault(x => x.RoverId == device_id); + rover = RoversNaRede.FirstOrDefault(x => x.RoverId == device_id); if (rover != null) { obj.Momento = DateTime.Now; @@ -163,6 +165,11 @@ namespace OperationControl.Models } } ((App)Application.Current).Shell.Main?.AtualizarDadosTela_Telemetria(device_id); + + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + (((App)Application.Current).Shell.Dock?.DataContext as DockWindowViewModel)?.AtualizarDadosTela(rover); + })); } catch (Exception ex) { @@ -191,6 +198,7 @@ namespace OperationControl.Models } ((App)Application.Current).Shell.Main?.AtualizarDadosTela_ParametrosOperacao(obj.Controle); ((App)Application.Current).Shell.Main?.MAP?.CarregarDadosMapa(obj.Mapa, obj.RuasPercorrer); + ((App)Application.Current).Shell.Dock?._vm?.AtualizarDadosMapa(obj); } catch (Exception ex) { @@ -239,12 +247,19 @@ namespace OperationControl.Models rover.DadosLeitura.Operacao.Status = AgroBase.Models.Enums.StatusOperacao.Erro; ((App)Application.Current).Shell.Main?.AtualizarDadosTela_Telemetria(rover.RoverId); ((App)Application.Current).Shell.Main?.AdicionarAlerta(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb, SeveridadeAlerta.Critical, "Perda de comunicação com o equipamento!"); + ((App)Application.Current).Shell?.AdicionarAlertaDock(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb, SeveridadeAlerta.Critical, "Perda de comunicação com o equipamento!"); } else { ((App)Application.Current).Shell.Main?.RemoverAlerta(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb); + ((App)Application.Current).Shell.RemoverAlertaDock(rover.RoverId, AgroBase.Models.Enums.T_Code.Ipb); } } + + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + ((App)Application.Current).Shell.Dock?._vm?.AtualizarListaRovers(rovers_atual); + })); } finally { diff --git a/AgroBase/OperationControl/OperationControl.csproj.user b/AgroBase/OperationControl/OperationControl.csproj.user index 0ef63b7ba..a493baac4 100644 --- a/AgroBase/OperationControl/OperationControl.csproj.user +++ b/AgroBase/OperationControl/OperationControl.csproj.user @@ -43,7 +43,31 @@ Code - + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + Code @@ -90,7 +114,31 @@ Designer - + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + + Designer + + Designer diff --git a/AgroBase/OperationControl/Services/GpsService.cs b/AgroBase/OperationControl/Services/GpsService.cs index 1b4d2b411..489e98f3d 100644 --- a/AgroBase/OperationControl/Services/GpsService.cs +++ b/AgroBase/OperationControl/Services/GpsService.cs @@ -9,6 +9,8 @@ using AgroBase.Models; using static AgroBase.Models.Enums; using OperationControl.Models; using Application = System.Windows.Application; +using Mapsui.Animations; +using OperationControl.ViewModels; namespace OperationControl.Services { @@ -245,7 +247,7 @@ namespace OperationControl.Services Models.Variaveis.MostrarLog("Iniciando configuração do módulo GPS..."); LeverArm = new GeoLeverArm(VariaveisControleOperacao.LeverArmFrontal, VariaveisControleOperacao.LeverArmLateral); BaseFix = new BaseFixService(PortaGps, this); - BaseFix.FixLiberado = true; + BaseFix.FixLiberado = fixar; if (fixar) { bool sucesso = await BaseFix.FixarBaseViaNtripAsync( @@ -1137,6 +1139,8 @@ namespace OperationControl.Services try { ((App)Application.Current).Shell.Main?.AtualizarDadosTela_Telemetria(VariaveisControleOperacao.BaseMarkerID); + + ((App)Application.Current).Shell.Dock?._vm?.AtualizarDadosGnss(UltimaLeitura); } catch (Exception exUi) { diff --git a/AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs deleted file mode 100644 index d3c9316f4..000000000 --- a/AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.ComponentModel; -using System.Windows.Input; -using System.Windows; -using OperationControl.Views; -using OperationControl.Helpers; -using OperationControl.Models; - -namespace OperationControl.ViewModels -{ - public class DockWindowViewModel : INotifyPropertyChanged - { - private readonly ConfigConexaoView _viewConfig; - - public DockWindowViewModel() - { - // 1) Cria a view do passo 1 - _viewConfig = new ConfigConexaoView(); - - // 2) Escuta quando RedeOk && GnssOk mudar - _viewConfig._vm.TudoOkChanged += OnTudoOkChanged; - - // 3) Conteúdo inicial - CenterContent = _viewConfig; - TopContent = null; - BottomContent = null; - - // 4) Commands já usando CanGoBack / CanGoNext - BackCommand = new RelayCommand(_ => GoBack(), _ => CanGoBack); - NextCommand = new RelayCommand(_ => GoNext(), _ => CanGoNext); - - // 5) Estado inicial do Next (caso já venha tudo ok no boot) - ConfigConexaoOk = _viewConfig._vm.TudoOk; - } - - private void OnTudoOkChanged(bool ok) - { - ConfigConexaoOk = ok; - - // avisa bindings - OnPropertyChanged(nameof(CanGoNext)); - - // força o WPF a reavaliar CanExecute dos botões - CommandManager.InvalidateRequerySuggested(); - } - - // ====== ESTADO ====== - private bool _configConexaoOk; - public bool ConfigConexaoOk - { - get => _configConexaoOk; - set - { - if (_configConexaoOk != value) - { - _configConexaoOk = value; - OnPropertyChanged(nameof(ConfigConexaoOk)); - OnPropertyChanged(nameof(CanGoNext)); - } - } - } - - public bool CanGoBack => false; // passo 1 não volta - public bool CanGoNext => ConfigConexaoOk; // libera quando tudo ok - - // ====== CONTEÚDOS ====== - private object _centerContent; - public object CenterContent - { - get => _centerContent; - set { _centerContent = value; OnPropertyChanged(nameof(CenterContent)); } - } - - private object _topContent; - public object TopContent - { - get => _topContent; - set { _topContent = value; OnPropertyChanged(nameof(TopContent)); } - } - - private object _bottomContent; - public object BottomContent - { - get => _bottomContent; - set { _bottomContent = value; OnPropertyChanged(nameof(BottomContent)); } - } - - // ====== COMMANDS ====== - public ICommand BackCommand { get; } - public ICommand NextCommand { get; } - - private void GoBack() - { - // depois você implementa quando tiver passo 2/3 - } - - private void GoNext() - { - if (!CanGoNext) return; - - // Aqui depois você troca para a próxima tela: - // CenterContent = new FixacaoBaseView(); - // e atualiza CanGoBack/CanGoNext conforme o step - - //System.Windows.MessageBox.Show("Avançou! Próximo passo: Fixação da Base (ainda vamos implementar)."); - - ((App)System.Windows.Application.Current).Shell.Dock?.Hide(); - ((App)System.Windows.Application.Current).Shell.Main?.Show(); - } - - // ====== INotifyPropertyChanged ====== - public event PropertyChangedEventHandler PropertyChanged; - - private void OnPropertyChanged(string nome) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); - } - } -} \ No newline at end of file diff --git a/AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs similarity index 68% rename from AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs rename to AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs index 8188526d7..871e855fa 100644 --- a/AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/ConfigConexaoViewModel.cs @@ -18,6 +18,9 @@ namespace OperationControl.ViewModels AbrirConfigRedeCommand = new RelayCommand(_ => AbrirConfigRede()); AbrirDetalhesGnssCommand = new RelayCommand(_ => AbrirDetalhesGnss()); + SelecionarModoAutomaticoCommand = new RelayCommand(_ => TipoOperacaoSelecionado = ModoOperacao.Automatico); + SelecionarModoManualCommand = new RelayCommand(_ => TipoOperacaoSelecionado = ModoOperacao.Manual); + // aqui você já pode chamar um serviço real depois CarregarStatusInicial(); @@ -27,6 +30,42 @@ namespace OperationControl.ViewModels _timerGnss.Start(); } + // ====== PROPRIEDADES DE MODO DE OPERACAO ====== + + private ModoOperacao _tipoOperacaoSelecionado = ModoOperacao.Automatico; + public ModoOperacao TipoOperacaoSelecionado + { + get => _tipoOperacaoSelecionado; + set + { + if (_tipoOperacaoSelecionado != value) + { + _tipoOperacaoSelecionado = value; + OnPropertyChanged(nameof(TipoOperacaoSelecionado)); + OnPropertyChanged(nameof(OperacaoAutomaticaSelecionada)); + OnPropertyChanged(nameof(OperacaoManualSelecionada)); + OnPropertyChanged(nameof(DescricaoModoOperacao)); + OnPropertyChanged(nameof(MensagemGeral)); + OnPropertyChanged(nameof(PodeAvancar)); + OnTudoOkChanged(); + } + } + } + + public bool OperacaoAutomaticaSelecionada => TipoOperacaoSelecionado == ModoOperacao.Automatico; + public bool OperacaoManualSelecionada => TipoOperacaoSelecionado == ModoOperacao.Manual; + + public bool PodeAvancar => + TipoOperacaoSelecionado == ModoOperacao.Automatico + ? RedeOk && GnssOk + : RedeOk; + public string DescricaoModoOperacao => + TipoOperacaoSelecionado == ModoOperacao.Automatico + ? "Operação autônoma. Exige rede e GNSS conectados. Na próxima etapa, será necessário carregar o mapa e fixar a base." + : "Operação manual. Exige apenas rede conectada. O sistema pulará a preparação de mapa e base, liberando apenas funções manuais."; + + + // ====== PROPRIEDADES DE REDE ====== private bool _redeOk; @@ -100,14 +139,34 @@ namespace OperationControl.ViewModels private string _mensagemGeral; public string MensagemGeral { - get => _mensagemGeral; - set { _mensagemGeral = value; OnPropertyChanged(nameof(MensagemGeral)); } + get + { + if (TipoOperacaoSelecionado == ModoOperacao.Automatico) + { + if (!RedeOk && !GnssOk) + return "Conecte a rede e o GNSS para iniciar uma operação automática."; + if (!RedeOk) + return "Ajuste a rede para avançar."; + if (!GnssOk) + return "Conecte o GNSS para avançar na operação automática."; + + return "Tudo OK. Você pode avançar para a preparação do mapa e fixação da base."; + } + else + { + if (!RedeOk) + return "Ajuste a rede para habilitar o modo manual."; + return "Rede OK. Você pode avançar diretamente para a parametrização manual do rover."; + } + } } // ====== COMANDOS ====== public ICommand AbrirConfigRedeCommand { get; } public ICommand AbrirDetalhesGnssCommand { get; } + public ICommand SelecionarModoAutomaticoCommand { get; } + public ICommand SelecionarModoManualCommand { get; } private void AbrirConfigRede() { @@ -118,12 +177,15 @@ namespace OperationControl.ViewModels private void AbrirDetalhesGnss() { - MessageBox.Show("Aqui vai abrir uma tela com detalhes do GNSS/UM982."); + string msg = Variaveis.GpsService.IsConnected ? + $"Conectado à porta {Variaveis.GpsService.PortName}" : + $"Não conectado"; + MessageBox.Show(msg); } // ====== STATUS GERAL PARA AVANÇAR ====== - public bool TudoOk => RedeOk && GnssOk; + public bool TudoOk => PodeAvancar; // Evento para o DockWindow saber quando ficou tudo ok public event Action TudoOkChanged; @@ -158,13 +220,12 @@ namespace OperationControl.ViewModels // double? hdop = GnssService.Hdop(); bool conectado = Variaveis.GpsService?.IsConnected ?? false; // <-- seu método real aqui - //conectado = true; + conectado = true; if (!conectado) { GnssOk = false; DetalhesGnss = "UM982 não detectado. Verifique cabo USB e alimentação."; - MensagemGeral = "Conecte o GNSS e ajuste a rede para avançar."; return; } @@ -174,17 +235,11 @@ namespace OperationControl.ViewModels // Se você tiver mais infos, mostra aqui: // DetalhesGnss = $"Conectado ({porta}) | Fix: {fix} | HDOP: {hdop:0.0}"; DetalhesGnss = "Conectado. Recebendo dados GNSS."; - - // Mensagem geral pode ficar mais positiva se Rede também estiver ok - MensagemGeral = TudoOk - ? "Tudo OK. Você pode avançar para a fixação da base." - : "GNSS OK. Agora ajuste a rede para avançar."; } catch (Exception ex) { GnssOk = false; DetalhesGnss = "Erro ao verificar GNSS: " + ex.Message; - MensagemGeral = "Erro ao ler GNSS. Verifique conexões."; } } @@ -206,7 +261,7 @@ namespace OperationControl.ViewModels // 2) Define RedeOk do jeito que faz sentido pra você: // exemplo simples: tem IP válido + gateway válido - RedeOk = !string.IsNullOrWhiteSpace(ip) && !string.IsNullOrWhiteSpace(gw); + RedeOk = !string.IsNullOrWhiteSpace(ip) && ip != "0.0.0.0"; } catch (Exception ex) { diff --git a/AgroBase/OperationControl/ViewModels/NetworkConfigViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/ConfigConexao/NetworkConfigViewModel.cs similarity index 100% rename from AgroBase/OperationControl/ViewModels/NetworkConfigViewModel.cs rename to AgroBase/OperationControl/ViewModels/Views/ConfigConexao/NetworkConfigViewModel.cs diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs new file mode 100644 index 000000000..09a87d13c --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs @@ -0,0 +1,249 @@ +using OperationControl.Models; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Windows.Threading; +using static AgroBase.Models.Enums; +using Application = System.Windows.Application; + +namespace OperationControl.ViewModels.Views.Operacao +{ + public class OperacaoBottomViewModel : INotifyPropertyChanged + { + private AlertaModel _toastAlerta; + private AlertaModel _alertaSelecionado; + private readonly DispatcherTimer _toastTimer; + + public OperacaoBottomViewModel() + { + AlertasAtivos = new ObservableCollection(); + + _toastTimer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(5) + }; + + _toastTimer.Tick += (s, e) => + { + _toastTimer.Stop(); + ToastAlerta = null; + }; + } + + public ObservableCollection AlertasAtivos { get; } + + public AlertaModel ToastAlerta + { + get => _toastAlerta; + set + { + if (_toastAlerta != value) + { + _toastAlerta = value; + OnPropertyChanged(nameof(ToastAlerta)); + } + } + } + + public AlertaModel AlertaSelecionado + { + get => _alertaSelecionado; + set + { + if (_alertaSelecionado != value) + { + _alertaSelecionado = value; + OnPropertyChanged(nameof(AlertaSelecionado)); + } + } + } + + public event PropertyChangedEventHandler PropertyChanged; + public event Action AlertaSelecionadoChanged; + + public void SelecionarAlerta(AlertaModel alerta) + { + if (alerta == null) return; + + AlertaSelecionado = alerta; + AlertaSelecionadoChanged?.Invoke(alerta); + } + + public void AdicionarAlerta(string roverId, T_Code modulo, SeveridadeAlerta severidade, string mensagem, string modId = null) + { + if (!Application.Current.Dispatcher.CheckAccess()) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + AdicionarAlerta(roverId, modulo, severidade, mensagem, modId))); + return; + } + + var existente = AlertasAtivos.FirstOrDefault(x => + x.Rover_ID == roverId && + x.Modulo == modulo && + x.Mod_ID == modId); + + if (existente == null) + { + var alerta = new AlertaModel + { + Rover_ID = roverId, + Modulo = modulo, + Mod_ID = modId, + Severidade = severidade, + Mensagem = mensagem, + Timestamp = DateTime.Now + }; + + AlertasAtivos.Insert(0, alerta); + MostrarToast(alerta); + } + else + { + existente.Mensagem = mensagem; + existente.Severidade = severidade; + existente.Timestamp = DateTime.Now; + + // caso o model nao implemente INotifyPropertyChanged, + // forçamos um refresh simples: + RecarregarAlerta(existente); + } + + OnPropertyChanged(nameof(AlertasAtivos)); + } + + public void RemoverAlerta(string roverId, T_Code modulo, SeveridadeAlerta? severidade = null, string modId = null) + { + if (!Application.Current.Dispatcher.CheckAccess()) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + RemoverAlerta(roverId, modulo, severidade, modId))); + return; + } + + var alerta = AlertasAtivos.FirstOrDefault(x => + x.Rover_ID == roverId && + x.Modulo == modulo && + x.Mod_ID == modId && + (severidade != null ? x.Severidade == severidade : true)); + + if (alerta != null) + { + AlertasAtivos.Remove(alerta); + + if (ReferenceEquals(ToastAlerta, alerta)) + ToastAlerta = null; + + if (ReferenceEquals(AlertaSelecionado, alerta)) + AlertaSelecionado = null; + } + + OnPropertyChanged(nameof(AlertasAtivos)); + } + + public void MostrarToast(AlertaModel alerta) + { + ToastAlerta = alerta; + _toastTimer.Stop(); + _toastTimer.Start(); + } + + public void LimparToast() + { + _toastTimer.Stop(); + ToastAlerta = null; + } + + public void DefinirAlertas(params AlertaModel[] alertas) + { + if (!Application.Current.Dispatcher.CheckAccess()) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => DefinirAlertas(alertas))); + return; + } + + AlertasAtivos.Clear(); + + if (alertas != null) + { + foreach (var alerta in alertas) + AlertasAtivos.Add(alerta); + } + + OnPropertyChanged(nameof(AlertasAtivos)); + } + + private void RecarregarAlerta(AlertaModel alerta) + { + var idx = AlertasAtivos.IndexOf(alerta); + if (idx >= 0) + { + AlertasAtivos.RemoveAt(idx); + AlertasAtivos.Insert(idx, alerta); + } + } + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + + + + public void AtualizarListaAlertasRoverSelecionado(string rover_id) + { + var rover = VariaveisControleOperacao.RoversNaRede.FirstOrDefault(x => x.RoverId == rover_id); + if (rover == null) return; + 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()) + { + foreach (var ind in mod.saude_individual) + { + if (ind.status != StatusModulo.Operante) + { + AdicionarAlerta(rover.RoverId, mod.modulo, DeParaStatus(ind.status, ind.em_uso), string.Join(", ", ind.motivos), modId: ind.label); + } + else + { + RemoverAlerta(rover.RoverId, mod.modulo, modId: ind.label); + } + } + } + else + { + if (mod.status != 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); + } + } + } + } + + } +} \ No newline at end of file diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoCenterViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoCenterViewModel.cs new file mode 100644 index 000000000..ea4b994e9 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoCenterViewModel.cs @@ -0,0 +1,149 @@ +using OperationControl.Controls; +using OperationControl.Models; +using System.ComponentModel; + +namespace OperationControl.ViewModels.Views.Operacao +{ + public class OperacaoCenterViewModel : INotifyPropertyChanged + { + + + public OperacaoCenterViewModel() + { + + } + + public event Action RoverSelecionadoChanged; + + public event PropertyChangedEventHandler? PropertyChanged; + + private RoverCardModel? roverSelecionado; + private bool _comRoverSelecionado; + public bool ComRoverSelecionado + { + get => _comRoverSelecionado; + set + { + if (_comRoverSelecionado != value) + { + _comRoverSelecionado = value; + OnPropertyChanged(nameof(ComRoverSelecionado)); + } + } + } + + private OperacaoCenterConteudo _conteudoAtual = OperacaoCenterConteudo.Mapa; + public OperacaoCenterConteudo ConteudoAtual + { + get => _conteudoAtual; + set + { + _conteudoAtual = value; + OnPropertyChanged(nameof(ConteudoAtual)); + OnPropertyChanged(nameof(ExibirMapa)); + OnPropertyChanged(nameof(ExibirDiagnostico)); + OnPropertyChanged(nameof(ExibirMonitoramento)); + OnPropertyChanged(nameof(TituloCentro)); + OnPropertyChanged(nameof(SubtituloCentro)); + } + } + + public bool ExibirMapa => ConteudoAtual == OperacaoCenterConteudo.Mapa; + public bool ExibirDiagnostico => ConteudoAtual == OperacaoCenterConteudo.Diagnostico; + public bool ExibirMonitoramento => ConteudoAtual == OperacaoCenterConteudo.Monitoramento; + + public string TituloCentro + { + get + { + if (!ComRoverSelecionado) + return "Nenhum rover selecionado"; + + return ConteudoAtual switch + { + OperacaoCenterConteudo.Diagnostico => "Diagnóstico do Rover", + OperacaoCenterConteudo.Monitoramento => "Monitoramento do Rover", + OperacaoCenterConteudo.Mapa => "Mapa da Operação", + _ => "Área Principal da Operação" + }; + } + } + + public string SubtituloCentro + { + get + { + if (!ComRoverSelecionado) + return "Selecione um rover para visualizar os dados."; + + return ConteudoAtual switch + { + OperacaoCenterConteudo.Diagnostico => "Aqui serão exibidos os dados de diagnóstico do rover selecionado.", + OperacaoCenterConteudo.Monitoramento => "Aqui serão exibidos os dados de monitoramento em tempo real do rover.", + OperacaoCenterConteudo.Mapa => "Aqui será exibido o mapa e os dados operacionais do rover selecionado.", + _ => "Conteúdo da operação." + }; + } + } + + public void SelecionarRoverLista(RoverCardModel? rover) + { + roverSelecionado = rover; + ComRoverSelecionado = roverSelecionado != null; + DefinirSecao(ComRoverSelecionado ? SecaoRightBar.Menu : SecaoRightBar.SemRoverSelecionado); + } + + public void DefinirSecao(SecaoRightBar secao) + { + ConteudoAtual = secao switch + { + SecaoRightBar.Diagnostico => OperacaoCenterConteudo.Diagnostico, + + SecaoRightBar.Resumo => OperacaoCenterConteudo.Monitoramento, + SecaoRightBar.Controle => OperacaoCenterConteudo.Monitoramento, + SecaoRightBar.Parametrizacao => OperacaoCenterConteudo.Mapa, + SecaoRightBar.Menu => OperacaoCenterConteudo.Monitoramento, + + _ => OperacaoCenterConteudo.Mapa + }; + } + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + + + + public void ProcessarMarkerClicked(MapMarkerManager.MarkerClickedEventArgs e) + { + if (string.IsNullOrEmpty(e.MarkerId)) + { + Variaveis.MostrarLog("Sem rover em foco"); + RoverSelecionadoChanged?.Invoke(null); + } + else + { + Variaveis.MostrarLog($"Rover em foco: {e.MarkerId}"); + RoverSelecionadoChanged?.Invoke(e.MarkerId); + } + } + + public void ProcessarStreetMapClicked(MapViewControl.StreetMapClickedEventArgs e) + { + Variaveis.MostrarLog($"Rua clicada: {e.StreetId}. Ruas selecionadas: {string.Join(",", e.SelectedStreetsIds)}"); + } + + + + + + } + + public enum OperacaoCenterConteudo + { + Mapa = 1, + Diagnostico = 2, + Monitoramento = 3 + } +} diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs new file mode 100644 index 000000000..00ece4955 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs @@ -0,0 +1,349 @@ +using OperationControl.Helpers; +using OperationControl.Models; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; +using static AgroBase.Models.Enums; + +namespace OperationControl.ViewModels.Views.Operacao +{ + public class OperacaoLeftViewModel : INotifyPropertyChanged + { + public ICommand AlternarOperacaoCommand { get; } + public ICommand ParadaEmergenciaCommand { get; } + public ICommand CancelarOperacaoCommand { get; } + private bool _operacaoEmExecucao; + public bool OperacaoEmExecucao + { + get => _operacaoEmExecucao; + set + { + if (_operacaoEmExecucao != value) + { + _operacaoEmExecucao = value; + OnPropertyChanged(nameof(OperacaoEmExecucao)); + OnPropertyChanged(nameof(TextoBotaoOperacao)); + OnPropertyChanged(nameof(CorBotaoOperacao)); + OnPropertyChanged(nameof(BordaBotaoOperacao)); + } + } + } + + public string TextoBotaoOperacao => OperacaoEmExecucao ? "⏸ Pausar Operação" : "▶ Iniciar Operação"; + + public System.Windows.Media.Brush CorBotaoOperacao => + OperacaoEmExecucao + ? new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(120, 88, 20)) + : new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(30, 90, 58)); + + public System.Windows.Media.Brush BordaBotaoOperacao => + OperacaoEmExecucao + ? new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(170, 130, 40)) + : new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(74, 138, 91)); + + + private bool _roverSelecionado; + public bool RoverSelecionado + { + get => _roverSelecionado; + set + { + if (_roverSelecionado != value) + { + _roverSelecionado = value; + OnPropertyChanged(nameof(RoverSelecionado)); + OnPropertyChanged(nameof(NomeRover)); + OnPropertyChanged(nameof(OpacityPainel)); + } + } + } + + private string _nomeRover = "Nenhum rover selecionado"; + public string NomeRover + { + get => RoverSelecionado ? _nomeRover : "Nenhum rover selecionado"; + set + { + if (_nomeRover != value) + { + _nomeRover = value; + OnPropertyChanged(nameof(NomeRover)); + } + } + } + + public double OpacityPainel => RoverSelecionado ? 1.0 : 0.45; + + private string _velocidade = "--"; + public string Velocidade + { + get => VelocidadeValor(_velocidade); + set + { + if (_velocidade != value) + { + _velocidade = value; + OnPropertyChanged(nameof(Velocidade)); + } + } + } + + private string _anguloDirecional = "--"; + public string AnguloDirecional + { + get => RoverSelecionado ? $"{_anguloDirecional}°" : "--"; + set + { + if (_anguloDirecional != value) + { + _anguloDirecional = value; + OnPropertyChanged(nameof(AnguloDirecional)); + } + } + } + + private TipoMovimentoDirecional _tipoMovimento = TipoMovimentoDirecional.Diagnostico; + public TipoMovimentoDirecional TipoMovimento + { + get => _tipoMovimento; + set + { + if (_tipoMovimento != value) + { + _tipoMovimento = value; + OnPropertyChanged(nameof(TipoMovimento)); + } + } + } + + private string _temperatura = "--"; + public string Temperatura + { + get => TemperaturaValor(_temperatura); + set + { + if (_temperatura != value) + { + _temperatura = value; + OnPropertyChanged(nameof(Temperatura)); + } + } + } + + private string _bateria = "--"; + public string Bateria + { + get => PercentualOuTracos(_bateria); + set + { + if (_bateria != value) + { + _bateria = value; + OnPropertyChanged(nameof(Bateria)); + } + } + } + + private string _reservatorio = "--"; + public string Reservatorio + { + get => PercentualOuTracos(_reservatorio); + set + { + if (_reservatorio != value) + { + _reservatorio = value; + OnPropertyChanged(nameof(Reservatorio)); + } + } + } + + private string _saudeRede = "--"; + public string SaudeRede + { + get => ValorOuTracos(_saudeRede); + set + { + if (_saudeRede != value) + { + _saudeRede = value; + OnPropertyChanged(nameof(SaudeRede)); + } + } + } + + private string _saudeGnss = "--"; + public string SaudeGnss + { + get => ValorOuTracos(_saudeGnss); + set + { + if (_saudeGnss != value) + { + _saudeGnss = value; + OnPropertyChanged(nameof(SaudeGnss)); + } + } + } + + public Brush CorTemperatura => + !RoverSelecionado ? Brushes.Gray : + _temperaturaNumerica >= 55 ? Brushes.OrangeRed : + _temperaturaNumerica >= 45 ? Brushes.Gold : + Brushes.LightGreen; + + public Brush CorBateria => + !RoverSelecionado ? Brushes.Gray : + _bateriaNumerica <= 20 ? Brushes.OrangeRed : + _bateriaNumerica <= 40 ? Brushes.Gold : + Brushes.LightGreen; + + public Brush CorReservatorio => + !RoverSelecionado ? Brushes.Gray : + _reservatorioNumerico <= 15 ? Brushes.OrangeRed : + _reservatorioNumerico <= 35 ? Brushes.Gold : + Brushes.LightGreen; + + public Brush CorRede => + !RoverSelecionado ? Brushes.Gray : + CorPorTexto(_saudeRede); + + public Brush CorGnss => + !RoverSelecionado ? Brushes.Gray : + CorPorTexto(_saudeGnss); + + private double _temperaturaNumerica; + private double _bateriaNumerica; + private double _reservatorioNumerico; + + public OperacaoLeftViewModel() + { + AlternarOperacaoCommand = new RelayCommand(_ => AlternarOperacao()); + ParadaEmergenciaCommand = new RelayCommand(_ => ExecutarParadaEmergencia()); + CancelarOperacaoCommand = new RelayCommand(_ => CancelarOperacao()); + + DefinirSemRover(); + } + + public void DefinirSemRover() + { + RoverSelecionado = false; + NomeRover = "Nenhum rover selecionado"; + _temperaturaNumerica = 0; + _bateriaNumerica = 0; + _reservatorioNumerico = 0; + + Velocidade = "--"; + AnguloDirecional = "--"; + TipoMovimento = TipoMovimentoDirecional.Diagnostico; + Temperatura = "--"; + Bateria = "--"; + Reservatorio = "--"; + SaudeRede = "--"; + SaudeGnss = "--"; + + OnPropertyChanged(nameof(CorTemperatura)); + OnPropertyChanged(nameof(CorBateria)); + OnPropertyChanged(nameof(CorReservatorio)); + OnPropertyChanged(nameof(CorRede)); + OnPropertyChanged(nameof(CorGnss)); + } + + public void AtualizarRover(string nomeRover, double velocidadeKmh, double anguloDirecionalGraus, TipoMovimentoDirecional tipoMovimento, double temperaturaC, double bateriaPct, double reservatorioPct, string saudeRede, string saudeGnss) + { + RoverSelecionado = true; + NomeRover = nomeRover; + + _temperaturaNumerica = temperaturaC; + _bateriaNumerica = bateriaPct; + _reservatorioNumerico = reservatorioPct; + + Velocidade = velocidadeKmh.ToString("0.0"); + AnguloDirecional = anguloDirecionalGraus.ToString("0.0"); + TipoMovimento = tipoMovimento; + Temperatura = temperaturaC.ToString("0.0"); + Bateria = bateriaPct.ToString("0"); + Reservatorio = reservatorioPct.ToString("0"); + SaudeRede = saudeRede; + SaudeGnss = saudeGnss; + + OnPropertyChanged(nameof(CorTemperatura)); + OnPropertyChanged(nameof(CorBateria)); + OnPropertyChanged(nameof(CorReservatorio)); + OnPropertyChanged(nameof(CorRede)); + OnPropertyChanged(nameof(CorGnss)); + } + + private void AlternarOperacao() + { + if (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Iniciada ?? false) + { + bool PauseActivated = VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Pausa ?? false; + PauseActivated = !PauseActivated; + VariaveisControleOperacao.EnviarComandoParada(pausa: PauseActivated); + } + else + { + if (!((VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Modo ?? AgroBase.Models.Enums.ModoOperacao.NaoDefinido) == AgroBase.Models.Enums.ModoOperacao.Manual) && !(VariaveisControleOperacao.RoverEmFoco?.RuasPercorrer?.Any() ?? false)) + { + System.Windows.MessageBox.Show("Para iniciar a operação automática, é necessário carregar um mapa e selecionar as ruas onde o equipamento irá operar!", "Parametrização", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + if (System.Windows.MessageBox.Show("Deseja iniciar a operação com os parâmetros atuais?", "Iniciar Operação", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) + { + bool simulador = VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Gnss?.Iniciado != true; + VariaveisControleOperacao.EnviarComandoIniciarOperacao(true, simulador); + } + } + + OperacaoEmExecucao = VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Iniciada ?? false; + } + + private void ExecutarParadaEmergencia() + { + bool EmergencyActivated = VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Emergencia ?? false; + EmergencyActivated = !EmergencyActivated; + //VariaveisControleOperacao.EnviarComandoParada(emergencia: EmergencyActivated); + _ = VariaveisControleOperacao.EnviarComandoParadaUDP(emergencia: EmergencyActivated); + } + + private void CancelarOperacao() + { + if (System.Windows.MessageBox.Show("Deseja finalizar a operação em andamento?", "Finalizar Operação", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) + { + VariaveisControleOperacao.EnviarComandoIniciarOperacao(false); + OperacaoEmExecucao = false; + } + } + + + + private string ValorOuTracos(string valor) => RoverSelecionado ? valor : "--"; + private string VelocidadeValor(string valor) => RoverSelecionado ? $"{valor} km/h" : "--"; + private string TemperaturaValor(string valor) => RoverSelecionado ? $"{valor} °C" : "--"; + private string PercentualOuTracos(string valor) => RoverSelecionado ? $"{valor}%" : "--"; + + private Brush CorPorTexto(string valor) + { + var txt = (valor ?? "").Trim().ToLower(); + + if (txt.Contains("excelente") || txt.Contains("ótima") || txt.Contains("otima") || txt.Contains("boa") || txt.Contains("rtk fixed")) + return Brushes.LightGreen; + + if (txt.Contains("média") || txt.Contains("media") || txt.Contains("regular") || txt.Contains("float") || txt.Contains("3d")) + return Brushes.Gold; + + if (txt.Contains("ruim") || txt.Contains("sem") || txt.Contains("offline") || txt.Contains("falha")) + return Brushes.OrangeRed; + + return Brushes.LightGray; + } + + public event PropertyChangedEventHandler? PropertyChanged; + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + } +} diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoRightViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoRightViewModel.cs new file mode 100644 index 000000000..89824def3 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoRightViewModel.cs @@ -0,0 +1,344 @@ +using AgroBase.Models.Operacoes; +using OperationControl.Helpers; +using OperationControl.Models; +using OperationControl.Models.Operacao; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; +using static AgroBase.Models.Enums; + +namespace OperationControl.ViewModels.Views.Operacao +{ + public class OperacaoRightViewModel : INotifyPropertyChanged + { + private bool _isCollapsed; + private RoverCardModel _roverSelecionado; + public ResumoOperacionalModel Resumo { get; } = new ResumoOperacionalModel(); + public OperacaoParametrosModel Parametros { get; } = new OperacaoParametrosModel(); + + public OperacaoRightViewModel() + { + Rovers = new ObservableCollection(); + + ToggleCollapseCommand = new RelayCommand(_ => ToggleCollapse()); + SelecionarRoverCommand = new RelayCommand(p => SelecionarRover(p as RoverCardModel)); + SelecionarSecaoCommand = new RelayCommand(p => + { + if (p is SecaoRightBar secao) + SelecionarSecao(secao); + else if (p != null && Enum.TryParse(p.ToString(), out var secaoParseada)) + SelecionarSecao(secaoParseada); + }); + AcaoBotaoTopoCommand = new RelayCommand(_ => ExecutarAcaoBotaoTopo()); + LerParametrosCommand = new RelayCommand(_ => LerParametrosSolicitado?.Invoke()); + SalvarParametrosCommand = new RelayCommand(_ => SalvarParametrosSolicitado?.Invoke(Parametros)); + } + + public event PropertyChangedEventHandler PropertyChanged; + public event Action SecaoSelecionadaChanged; + public event Action RoverSelecionadoChanged; + public event Action? LerParametrosSolicitado; + public event Action? SalvarParametrosSolicitado; + + public ObservableCollection Rovers { get; } + + public ICommand ToggleCollapseCommand { get; } + public ICommand SelecionarRoverCommand { get; } + public ICommand SelecionarSecaoCommand { get; } + public ICommand AcaoBotaoTopoCommand { get; } + public ICommand LerParametrosCommand { get; } + public ICommand SalvarParametrosCommand { get; } + + public bool IsCollapsed + { + get => _isCollapsed; + set + { + if (_isCollapsed != value) + { + _isCollapsed = value; + OnPropertyChanged(nameof(IsCollapsed)); + } + } + } + + public RoverCardModel RoverSelecionado + { + get => _roverSelecionado; + set + { + if (_roverSelecionado != value) + { + _roverSelecionado = value; + OnPropertyChanged(nameof(RoverSelecionado)); + OnPropertyChanged(nameof(SemRoverSelecionado)); + OnPropertyChanged(nameof(ComRoverSelecionado)); + CommandManager.InvalidateRequerySuggested(); + } + } + } + + public bool SemRoverSelecionado => RoverSelecionado == null; + public bool ComRoverSelecionado => RoverSelecionado != null; + + public string QuantidadeRoversTexto => $"{Rovers.Count} rover(s) encontrado(s)"; + + private SecaoRightBar _secaoAtual = SecaoRightBar.Menu; + public SecaoRightBar SecaoAtual + { + get => _secaoAtual; + set + { + if (_secaoAtual != value) + { + _secaoAtual = value; + OnPropertyChanged(nameof(SecaoAtual)); + OnPropertyChanged(nameof(EmMenu)); + OnPropertyChanged(nameof(EmResumo)); + OnPropertyChanged(nameof(EmParametrizacao)); + OnPropertyChanged(nameof(EmControle)); + OnPropertyChanged(nameof(BotaoTopoEhVoltar)); + OnPropertyChanged(nameof(TextoBotaoTopo)); + } + } + } + + public bool EmMenu => SecaoAtual == SecaoRightBar.Menu || SecaoAtual == SecaoRightBar.Diagnostico; + public bool EmResumo => SecaoAtual == SecaoRightBar.Resumo; + public bool EmParametrizacao => SecaoAtual == SecaoRightBar.Parametrizacao; + public bool EmControle => SecaoAtual == SecaoRightBar.Controle; + + public bool BotaoTopoEhVoltar => EmResumo || EmControle || EmParametrizacao; + + public string TextoBotaoTopo => BotaoTopoEhVoltar ? "◀ Voltar" : "Trocar Rover"; + + private void ExecutarAcaoBotaoTopo() + { + if (BotaoTopoEhVoltar) + { + SelecionarSecao(SecaoRightBar.Menu); + } + else + { + SelecionarRover(null); + } + } + + + public void ToggleCollapse() + { + IsCollapsed = !IsCollapsed; + } + + public void SelecionarRover(RoverCardModel rover) + { + RoverSelecionado = rover; + + if (RoverSelecionado != null) + { + VariaveisControleOperacao.RequisitarParametrosOperacao(); + SelecionarSecao(SecaoRightBar.Menu); + } + else + { + SelecionarSecao(SecaoRightBar.SemRoverSelecionado); + } + RoverSelecionadoChanged?.Invoke(rover); + } + + public void SelecionarSecao(SecaoRightBar secao) + { + switch (secao) + { + case SecaoRightBar.Parametrizacao: + AtualizarParametros(VariaveisControleOperacao.RoverEmFoco); + break; + } + + SecaoAtual = secao; + + SecaoSelecionadaChanged?.Invoke(secao); + } + + public void DestacarRoverNoMapa(string? roverId) + { + foreach (var rover in Rovers) + rover.IsDestacadoNoMapa = !string.IsNullOrEmpty(roverId) && rover.NumeroSerie == roverId; + } + + public void AtualizarListaRovers(params RoverCardModel[] rovers) + { + var origem = (rovers ?? Array.Empty()).ToList(); + + // Remove os que não existem mais + for (int i = Rovers.Count - 1; i >= 0; i--) + { + var atual = Rovers[i]; + bool aindaExiste = origem.Any(x => x.NumeroSerie == atual.NumeroSerie); + + if (!aindaExiste) + Rovers.RemoveAt(i); + } + + // Adiciona ou atualiza os existentes + foreach (var roverNovo in origem) + { + var roverExistente = Rovers.FirstOrDefault(x => x.NumeroSerie == roverNovo.NumeroSerie); + + if (roverExistente == null) + { + Rovers.Add(roverNovo); + } + else + { + roverExistente.EquipamentoId = roverNovo.EquipamentoId; + roverExistente.Status = roverNovo.Status; + + // se quiser refletir mudança de texto também: + // roverExistente.NumeroSerie = roverNovo.NumeroSerie; // normalmente não precisa se NS é a chave + + // dispara props derivadas se necessário + // no caso de EquipamentoId, como é auto-property hoje, melhor virar property notificável se isso puder mudar + } + } + + OnPropertyChanged(nameof(QuantidadeRoversTexto)); + OnPropertyChanged(nameof(SemRoverSelecionado)); + OnPropertyChanged(nameof(ComRoverSelecionado)); + } + + public void AtualizarResumo(Action updateAction) + { + if (!System.Windows.Application.Current.Dispatcher.CheckAccess()) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => updateAction?.Invoke(Resumo))); + return; + } + + updateAction?.Invoke(Resumo); + } + + + public void AtualizarParametros(OperacaoParametrosModel origem) + { + if (origem == null) return; + + Parametros.Modo = origem.Modo; + Parametros.Descricao = origem.Descricao; + Parametros.QtdBicos = origem.QtdBicos; + Parametros.QtdCamerasSolo = origem.QtdCamerasSolo; + Parametros.CapacidadeReservatorio = origem.CapacidadeReservatorio; + + var controle_o = origem.Controle; + if (Parametros.Controle == null) Parametros.Controle = new OperacaoParametrosControleModel(); + var controle_p = Parametros.Controle; + + controle_p.MovimentoAutomatico = controle_o.MovimentoAutomatico; + controle_p.DirecionalAutomatico = controle_o.DirecionalAutomatico; + controle_p.PulverizadorAutomatico = controle_o.PulverizadorAutomatico; + controle_p.SonarAtivado = controle_o.SonarAtivado; + controle_p.OakParadaPorObstaculo = controle_o.OakParadaPorObstaculo; + controle_p.ImuParadaPorInclinacao = controle_o.ImuParadaPorInclinacao; + controle_p.FrenagemAutomaticaAoParar = controle_o.FrenagemAutomaticaAoParar; + controle_p.RegistrarDadosPosProcessamento = controle_o.RegistrarDadosPosProcessamento; + + controle_p.MovVelocidadeSErvasPercent = controle_o.MovVelocidadeSErvasPercent; + controle_p.MovVelocidadeCErvasPercent = controle_o.MovVelocidadeCErvasPercent; + + controle_p.DirAnguloMaximo = controle_o.DirAnguloMaximo; + controle_p.DirVelocidadeMovimento = controle_o.DirVelocidadeMovimento; + controle_p.DirTipoMovimento = controle_o.DirTipoMovimento; + + controle_p.AtuPressaoLinha = controle_o.AtuPressaoLinha; + controle_p.AtuDuracaoAtuacao = controle_o.AtuDuracaoAtuacao; + controle_p.AtuPercentualInicioPulverizacao = controle_o.AtuPercentualInicioPulverizacao; + controle_p.AtuAlturaAreaPulverizacao = controle_o.AtuAlturaAreaPulverizacao; + controle_p.AtuPercentualErvasBicoOn = controle_o.AtuPercentualErvasBicoOn; + controle_p.AtuPercentualErvasBicoOff = controle_o.AtuPercentualErvasBicoOff; + + controle_p.MpcMatrizCusto = controle_o.MpcMatrizCusto; + controle_p.MpcHorizonte = controle_o.MpcHorizonte; + + OnPropertyChanged(nameof(Parametros)); + } + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + } + + public class RoverCardModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler? PropertyChanged; + + public string EquipamentoId { get; set; } + public string NumeroSerie { get; set; } + + private StatusModulo _status; + public StatusModulo Status + { + get => _status; + set + { + if (_status != value) + { + _status = value; + OnPropertyChanged(nameof(Status)); + OnPropertyChanged(nameof(StatusBrush)); + OnPropertyChanged(nameof(StatusTexto)); + } + } + } + + private bool _isDestacadoNoMapa; + public bool IsDestacadoNoMapa + { + get => _isDestacadoNoMapa; + set + { + if (_isDestacadoNoMapa != value) + { + _isDestacadoNoMapa = value; + OnPropertyChanged(nameof(IsDestacadoNoMapa)); + OnPropertyChanged(nameof(BordaCardBrush)); + OnPropertyChanged(nameof(BordaCardThickness)); + OnPropertyChanged(nameof(CardBackground)); + } + } + } + + public Brush StatusBrush => + Status == StatusModulo.Operante ? Brushes.Green : + Status == StatusModulo.Desconectado ? Brushes.Gray : + Status == StatusModulo.Falha ? Brushes.Red : + Status == StatusModulo.Alerta ? Brushes.Yellow : + Status == StatusModulo.Conectado ? Brushes.Blue : + Brushes.Black; + + public System.Windows.Media.Brush CardBackground => IsDestacadoNoMapa ? new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(58, 42, 18)) : new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(38, 38, 38)); + public Brush BordaCardBrush => IsDestacadoNoMapa ? Brushes.Orange : StatusBrush; + public Thickness BordaCardThickness => IsDestacadoNoMapa ? new Thickness(3) : new Thickness(2); + + public string EquipamentoTexto => $"Equipamento: {EquipamentoId}"; + public string NumeroSerieTexto => $"NS: {NumeroSerie}"; + public string StatusTexto => $"Status: {Status}"; + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + } + + public enum SecaoRightBar + { + SemRoverSelecionado = 0, + Menu = 1, + Resumo = 2, + Diagnostico = 3, + Parametrizacao = 4, + Controle = 5 + } + +} diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs new file mode 100644 index 000000000..49eef8135 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs @@ -0,0 +1,142 @@ +using System.ComponentModel; +using System.Windows.Media; +using Brush = System.Windows.Media.Brush; +using Brushes = System.Windows.Media.Brushes; +using Color = System.Windows.Media.Color; +using ColorConverter = System.Windows.Media.ColorConverter; + +namespace OperationControl.ViewModels.Views.Operacao +{ + public class OperacaoTopViewModel : INotifyPropertyChanged + { + private string _contextoSelecionado = "BASE"; + public string ContextoSelecionado + { + get => _contextoSelecionado; + set + { + if (_contextoSelecionado != value) + { + _contextoSelecionado = value; + OnPropertyChanged(nameof(ContextoSelecionado)); + } + } + } + + private string _mensagemStatus = "Sistema inicializado."; + public string MensagemStatus + { + get => _mensagemStatus; + set + { + if (_mensagemStatus != value) + { + _mensagemStatus = value; + OnPropertyChanged(nameof(MensagemStatus)); + } + } + } + + private string _badgeTexto = "PRONTO"; + public string BadgeTexto + { + get => _badgeTexto; + set + { + if (_badgeTexto != value) + { + _badgeTexto = value; + OnPropertyChanged(nameof(BadgeTexto)); + } + } + } + + private Brush _corFundo = Brushes.DimGray; + public Brush CorFundo + { + get => _corFundo; + set + { + if (_corFundo != value) + { + _corFundo = value; + OnPropertyChanged(nameof(CorFundo)); + } + } + } + + private Brush _corBadge = Brushes.SteelBlue; + public Brush CorBadge + { + get => _corBadge; + set + { + if (_corBadge != value) + { + _corBadge = value; + OnPropertyChanged(nameof(CorBadge)); + } + } + } + + private Brush _corTexto = Brushes.White; + public Brush CorTexto + { + get => _corTexto; + set + { + if (_corTexto != value) + { + _corTexto = value; + OnPropertyChanged(nameof(CorTexto)); + } + } + } + + public void DefinirStatusNormal(string contexto, string mensagem) + { + ContextoSelecionado = contexto; + MensagemStatus = mensagem; + BadgeTexto = "NORMAL"; + CorFundo = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1F3A2D")); + CorBadge = Brushes.LightGreen; + CorTexto = Brushes.White; + } + + public void DefinirStatusManual(string contexto, string mensagem) + { + ContextoSelecionado = contexto; + MensagemStatus = mensagem; + BadgeTexto = "MANUAL"; + CorFundo = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#2D3440")); + CorBadge = Brushes.Gold; + CorTexto = Brushes.White; + } + + public void DefinirStatusAtencao(string contexto, string mensagem) + { + ContextoSelecionado = contexto; + MensagemStatus = mensagem; + BadgeTexto = "ATENÇÃO"; + CorFundo = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A3315")); + CorBadge = Brushes.Orange; + CorTexto = Brushes.White; + } + + public void DefinirStatusCritico(string contexto, string mensagem) + { + ContextoSelecionado = contexto; + MensagemStatus = mensagem; + BadgeTexto = "CRÍTICO"; + CorFundo = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A1F1F")); + CorBadge = Brushes.OrangeRed; + CorTexto = Brushes.White; + } + + public event PropertyChangedEventHandler? PropertyChanged; + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + } +} diff --git a/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs new file mode 100644 index 000000000..48d2dbe51 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs @@ -0,0 +1,148 @@ +using OperationControl.Helpers; +using System.ComponentModel; +using System.Windows.Input; + +namespace OperationControl.ViewModels +{ + public class PreparacaoMapaBottomViewModel : INotifyPropertyChanged + { + public event Action? CarregarMapaSolicitado; + public event Action? FixarBaseSolicitado; + + private bool _fixacaoEmAndamento; + public bool FixacaoEmAndamento + { + get => _fixacaoEmAndamento; + set + { + if (_fixacaoEmAndamento != value) + { + _fixacaoEmAndamento = value; + OnPropertyChanged(nameof(FixacaoEmAndamento)); + OnPropertyChanged(nameof(PodeCarregarMapa)); + OnPropertyChanged(nameof(PodeFixarBase)); + } + } + } + + public bool PodeCarregarMapa => !FixacaoEmAndamento; + public bool PodeFixarBase => true; + + private double _progressoFixacao; + public double ProgressoFixacao + { + get => _progressoFixacao; + set + { + if (_progressoFixacao != value) + { + _progressoFixacao = value; + OnPropertyChanged(nameof(ProgressoFixacao)); + OnPropertyChanged(nameof(ProgressoFixacaoTexto)); + } + } + } + + public string ProgressoFixacaoTexto => $"{ProgressoFixacao:F0}%"; + + private string _statusTexto = "Pronto para carregar o mapa e fixar a base."; + public string StatusTexto + { + get => _statusTexto; + set + { + if (_statusTexto != value) + { + _statusTexto = value; + OnPropertyChanged(nameof(StatusTexto)); + } + } + } + + private string _textoBotaoFixarBase = "Fixar posição da base"; + public string TextoBotaoFixarBase + { + get => _textoBotaoFixarBase; + set + { + if (_textoBotaoFixarBase != value) + { + _textoBotaoFixarBase = value; + OnPropertyChanged(nameof(TextoBotaoFixarBase)); + } + } + } + + public ICommand CarregarMapaCommand { get; } + public ICommand FixarBaseCommand { get; } + + public PreparacaoMapaBottomViewModel() + { + CarregarMapaCommand = new RelayCommand(_ => ExecutarCarregarMapa(), _ => PodeCarregarMapa); + FixarBaseCommand = new RelayCommand(_ => ExecutarFixarBase(), _ => PodeFixarBase); + } + + private void ExecutarCarregarMapa() + { + var dlg = new OpenFileDialog + { + Filter = "Arquivos de mapa|*.geojson;*.json;*.shp", + Title = "Selecione o arquivo de mapa" + }; + + if (dlg.ShowDialog() == DialogResult.OK) + { + StatusTexto = $"Mapa selecionado: {System.IO.Path.GetFileName(dlg.FileName)}"; + CarregarMapaSolicitado?.Invoke(dlg.FileName); + } + } + + private void ExecutarFixarBase() + { + FixarBaseSolicitado?.Invoke(); + } + + public void AtualizarProgresso(double progresso, string status) + { + ProgressoFixacao = progresso; + StatusTexto = status; + } + + public void IniciarFixacao() + { + FixacaoEmAndamento = true; + TextoBotaoFixarBase = "Parar"; + ProgressoFixacao = 0; + StatusTexto = "Iniciando fixação da posição da base..."; + } + + public void FinalizarFixacao(bool sucesso, string? mensagem = null) + { + FixacaoEmAndamento = false; + TextoBotaoFixarBase = "Fixar posição da base"; + ProgressoFixacao = sucesso ? 100 : ProgressoFixacao; + + if (!string.IsNullOrWhiteSpace(mensagem)) + StatusTexto = mensagem; + else + StatusTexto = sucesso + ? "Fixação da base concluída com sucesso." + : "Fixação da base encerrada."; + } + + public void CancelarFixacao(string? mensagem = null) + { + FixacaoEmAndamento = false; + TextoBotaoFixarBase = "Fixar posição da base"; + StatusTexto = mensagem ?? "Fixação da base cancelada."; + } + + public event PropertyChangedEventHandler? PropertyChanged; + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + System.Windows.Input.CommandManager.InvalidateRequerySuggested(); + } + } +} diff --git a/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs new file mode 100644 index 000000000..e801743b2 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaTopViewModel.cs @@ -0,0 +1,121 @@ +using AgroBase.Models; +using System.ComponentModel; +using static AgroBase.Models.Enums; + +namespace OperationControl.ViewModels +{ + public class PreparacaoMapaTopViewModel : INotifyPropertyChanged + { + private string _latitudeTexto = "-"; + public string LatitudeTexto + { + get => _latitudeTexto; + set { _latitudeTexto = value; OnPropertyChanged(nameof(LatitudeTexto)); } + } + + private string _longitudeTexto = "-"; + public string LongitudeTexto + { + get => _longitudeTexto; + set { _longitudeTexto = value; OnPropertyChanged(nameof(LongitudeTexto)); } + } + + private string _altitudeTexto = "-"; + public string AltitudeTexto + { + get => _altitudeTexto; + set { _altitudeTexto = value; OnPropertyChanged(nameof(AltitudeTexto)); } + } + + private string _headingTexto = "-"; + public string HeadingTexto + { + get => _headingTexto; + set { _headingTexto = value; OnPropertyChanged(nameof(HeadingTexto)); } + } + + private string _fixTexto = "Sem fix"; + public string FixTexto + { + get => _fixTexto; + set { _fixTexto = value; OnPropertyChanged(nameof(FixTexto)); } + } + + private Brush _fixCor = Brushes.OrangeRed; + public Brush FixCor + { + get => _fixCor; + set { _fixCor = value; OnPropertyChanged(nameof(FixCor)); } + } + + private string _precisaoTexto = "-"; + public string PrecisaoTexto + { + get => _precisaoTexto; + set { _precisaoTexto = value; OnPropertyChanged(nameof(PrecisaoTexto)); } + } + + private string _satelitesTexto = "-"; + public string SatelitesTexto + { + get => _satelitesTexto; + set { _satelitesTexto = value; OnPropertyChanged(nameof(SatelitesTexto)); } + } + + private string _idadeCorrecaoTexto = "-"; + public string IdadeCorrecaoTexto + { + get => _idadeCorrecaoTexto; + set { _idadeCorrecaoTexto = value; OnPropertyChanged(nameof(IdadeCorrecaoTexto)); } + } + + public PreparacaoMapaTopViewModel() + { + // Mock inicial para visualização + LatitudeTexto = "-22.172657"; + LongitudeTexto = "-47.395216"; + AltitudeTexto = "612.4 m"; + HeadingTexto = "183°"; + FixTexto = "RTK FIX"; + FixCor = Brushes.LimeGreen; + PrecisaoTexto = "1.2 cm"; + SatelitesTexto = "24"; + IdadeCorrecaoTexto = "0.8 s"; + } + + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + + 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"; + + AtualizarCorFix(dados.QualidadeFix); + } + + private void AtualizarCorFix(TiposCorrecaoGPS fix) + { + var fixUpper = fix.ToString().ToUpperInvariant(); + + if (fixUpper.Contains("FIX")) + FixCor = Brushes.LimeGreen; + else if (fixUpper.Contains("FLOAT")) + FixCor = Brushes.Gold; + else if (fixUpper.Contains("DGPS")) + FixCor = Brushes.DodgerBlue; + else + FixCor = Brushes.OrangeRed; + } + } +} diff --git a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs new file mode 100644 index 000000000..d32d510f6 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs @@ -0,0 +1,751 @@ +using System.ComponentModel; +using System.Windows.Input; +using OperationControl.Views; +using OperationControl.Helpers; +using System.Windows; +using OperationControl.Models; +using OperationControl.Views.Operacao; +using AgroBase.Models.Operacoes; +using Newtonsoft.Json.Linq; +using OperationControl.ViewModels.Views.Operacao; + +namespace OperationControl.ViewModels +{ + public class DockWindowViewModel : INotifyPropertyChanged + { + private readonly ConfigConexaoView _viewConfig; + + private readonly PreparacaoMapaView _viewPreparacaoMapa; + public readonly PreparacaoMapaTopView _viewPreparacaoMapaTop; + private readonly PreparacaoMapaBottomView _viewPreparacaoMapaBottom; + + private readonly OperacaoTopView _viewOperacaoTop; + private readonly OperacaoCenterView _viewOperacaoCenter; + public readonly OperacaoLeftView _viewOperacaoLeft; + public readonly OperacaoRightView _viewOperacaoRight; + public readonly OperacaoBottomView _viewOperacaoBottom; + + private DockStep _currentStep; + + public DockWindowViewModel() + { + // Views + _viewConfig = new ConfigConexaoView(); + _viewConfig._vm.TudoOkChanged += OnTudoOkChanged; + + _viewPreparacaoMapa = new PreparacaoMapaView(); + + _viewPreparacaoMapaTop = new PreparacaoMapaTopView(); + + _viewPreparacaoMapaBottom = new PreparacaoMapaBottomView(); + _viewPreparacaoMapaBottom._vm.CarregarMapaSolicitado += OnCarregarMapaSolicitado; + _viewPreparacaoMapaBottom._vm.FixarBaseSolicitado += OnFixarBaseSolicitado; + + _viewOperacaoTop = new OperacaoTopView(); + + _viewOperacaoCenter = new OperacaoCenterView(); + _viewOperacaoCenter._vm.RoverSelecionadoChanged += OnRoverSelecionadoChanged; + + _viewOperacaoLeft = new OperacaoLeftView(); + + _viewOperacaoRight = new OperacaoRightView(); + _viewOperacaoRight._vm.PropertyChanged += OperacaoRightVm_PropertyChanged; + _viewOperacaoRight._vm.RoverSelecionadoChanged += OnRoverSelecionadoChanged; + _viewOperacaoRight._vm.SecaoSelecionadaChanged += OnSecaoSelecionadaChanged; + _viewOperacaoRight._vm.LerParametrosSolicitado += OnLerParametrosSolicitado; + _viewOperacaoRight._vm.SalvarParametrosSolicitado += OnSalvarParametrosSolicitado; + + _viewOperacaoBottom = new OperacaoBottomView(); + _viewOperacaoBottom._vm.AlertaSelecionadoChanged += OnAlertaSelecionadoChanged; + + // Commands + BackCommand = new RelayCommand(_ => GoBack(), _ => CanGoBack); + NextCommand = new RelayCommand(_ => GoNext(), _ => CanGoNext); + + // Estado inicial + ConfigConexaoOk = _viewConfig._vm.TudoOk; + CurrentStep = DockStep.ConfigConexao; + } + + private void OnTudoOkChanged(bool ok) + { + ConfigConexaoOk = ok; + + OnPropertyChanged(nameof(CanGoNext)); + CommandManager.InvalidateRequerySuggested(); + } + + // ====== ETAPA ATUAL ====== + public ModoOperacao ModoOperacaoAtual => _viewConfig._vm.TipoOperacaoSelecionado; + + public DockStep CurrentStep + { + get => _currentStep; + set + { + if (_currentStep != value) + { + _currentStep = value; + OnPropertyChanged(nameof(CurrentStep)); + OnPropertyChanged(nameof(CanGoBack)); + OnPropertyChanged(nameof(CanGoNext)); + OnPropertyChanged(nameof(MostrarPainelEsquerdo)); + OnPropertyChanged(nameof(MostrarBotaoVoltarEsquerda)); + OnPropertyChanged(nameof(RightPanelWidth)); + OnPropertyChanged(nameof(LeftPanelWidth)); + + AtualizarConteudo(); + CommandManager.InvalidateRequerySuggested(); + } + } + } + + // ====== ESTADO ====== + private bool _configConexaoOk; + public bool ConfigConexaoOk + { + get => _configConexaoOk; + set + { + if (_configConexaoOk != value) + { + _configConexaoOk = value; + OnPropertyChanged(nameof(ConfigConexaoOk)); + OnPropertyChanged(nameof(CanGoNext)); + CommandManager.InvalidateRequerySuggested(); + } + } + } + + private bool _mapaCarregado; + public bool MapaCarregado + { + get => _mapaCarregado; + set + { + if (_mapaCarregado != value) + { + _mapaCarregado = value; + OnPropertyChanged(nameof(MapaCarregado)); + OnPropertyChanged(nameof(CanGoNext)); + CommandManager.InvalidateRequerySuggested(); + } + } + } + + private bool _baseFixada = true; + public bool BaseFixada + { + get => _baseFixada; + set + { + if (_baseFixada != value) + { + _baseFixada = value; + OnPropertyChanged(nameof(BaseFixada)); + OnPropertyChanged(nameof(CanGoNext)); + CommandManager.InvalidateRequerySuggested(); + } + } + } + + public bool CanGoBack => CurrentStep != DockStep.ConfigConexao; + + public bool CanGoNext + { + get + { + return CurrentStep switch + { + DockStep.ConfigConexao => PodeAvancarConfigConexao(), + DockStep.PreparacaoMapa => MapaCarregado && BaseFixada, + _ => false + }; + } + } + private bool PodeAvancarConfigConexao() + { + return ModoOperacaoAtual switch + { + ModoOperacao.Automatico => _viewConfig._vm.RedeOk && _viewConfig._vm.GnssOk, + ModoOperacao.Manual => _viewConfig._vm.RedeOk, + _ => false + }; + } + + + public bool MostrarPainelEsquerdo => CurrentStep == DockStep.ParametrizacaoRover; + public bool MostrarBotaoVoltarEsquerda => CurrentStep != DockStep.ParametrizacaoRover; + + public GridLength LeftPanelWidth + { + get + { + return CurrentStep switch + { + DockStep.ParametrizacaoRover => new GridLength(280), + _ => new GridLength(60) + }; + } + } + public GridLength RightPanelWidth + { + get + { + return CurrentStep switch + { + DockStep.ParametrizacaoRover => (_viewOperacaoRight?.DataContext as OperacaoRightViewModel)?.IsCollapsed == true + ? new GridLength(44) + : new GridLength(320), + + _ => new GridLength(60) + }; + } + } + private void OperacaoRightVm_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(OperacaoRightViewModel.IsCollapsed)) + { + OnPropertyChanged(nameof(RightPanelWidth)); + } + } + + // ====== CONTEÚDOS ====== + private object _centerContent; + public object CenterContent + { + get => _centerContent; + set + { + _centerContent = value; + OnPropertyChanged(nameof(CenterContent)); + } + } + + private object _topContent; + public object TopContent + { + get => _topContent; + set + { + _topContent = value; + OnPropertyChanged(nameof(TopContent)); + } + } + + private object _bottomContent; + public object BottomContent + { + get => _bottomContent; + set + { + _bottomContent = value; + OnPropertyChanged(nameof(BottomContent)); + } + } + private object _leftContent; + public object LeftContent + { + get => _leftContent; + set + { + _leftContent = value; + OnPropertyChanged(nameof(LeftContent)); + } + } + private object _rightContent; + public object RightContent + { + get => _rightContent; + set + { + _rightContent = value; + OnPropertyChanged(nameof(RightContent)); + } + } + + // ====== COMMANDS ====== + public ICommand BackCommand { get; } + public ICommand NextCommand { get; } + + private void GoBack() + { + switch (CurrentStep) + { + case DockStep.PreparacaoMapa: + CurrentStep = DockStep.ConfigConexao; + break; + } + } + + private void GoNext() + { + if (!CanGoNext) return; + + switch (CurrentStep) + { + case DockStep.ConfigConexao: + if (ModoOperacaoAtual == ModoOperacao.Manual) + CurrentStep = DockStep.ParametrizacaoRover; + else + CurrentStep = DockStep.PreparacaoMapa; + break; + + case DockStep.PreparacaoMapa: + CurrentStep = DockStep.ParametrizacaoRover; + break; + } + } + + private FrameworkElement CriarBotaoNextSimples() + { + return new System.Windows.Controls.Button + { + Content = "⮞", + FontSize = 24, + Width = 40, + Height = 40, + HorizontalAlignment = System.Windows.HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Command = NextCommand + }; + } + + private void AtualizarConteudo() + { + TopContent = null; + BottomContent = null; + LeftContent = null; + RightContent = null; + CenterContent = null; + + switch (CurrentStep) + { + case DockStep.ConfigConexao: + CenterContent = _viewConfig; + RightContent = CriarBotaoNextSimples(); + break; + + case DockStep.PreparacaoMapa: + TopContent = _viewPreparacaoMapaTop; + CenterContent = _viewPreparacaoMapa; + BottomContent = _viewPreparacaoMapaBottom; + RightContent = CriarBotaoNextSimples(); + break; + + case DockStep.ParametrizacaoRover: + TopContent = _viewOperacaoTop; + LeftContent = _viewOperacaoLeft; + CenterContent = _viewOperacaoCenter; + RightContent = _viewOperacaoRight; + BottomContent = _viewOperacaoBottom; + + if (!string.IsNullOrWhiteSpace(_ultimoMapaCarregado)) + _viewOperacaoCenter?.MapaOperacao?.LoadMapFile(_ultimoMapaCarregado); + break; + } + + OnPropertyChanged(nameof(MostrarPainelEsquerdo)); + OnPropertyChanged(nameof(MostrarBotaoVoltarEsquerda)); + } + + // ====== INotifyPropertyChanged ====== + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + + + public void AtualizarDadosGnss(AgroBase.Models.GPSModel dados) + { + var bf = Variaveis.GpsService.BaseFix; + if (bf.CorrecaoEmAndamento) + { + AtualizarProgressoFixacaoBase(bf.Progresso, bf.ProgressoStr); + if (bf.Progresso >= 100) FinalizarFixacaoBase(true, bf.ProgressoStr); + } + switch (CurrentStep) + { + case DockStep.PreparacaoMapa: + System.Windows.Application.Current.Dispatcher.Invoke(() => + { + _viewPreparacaoMapaTop?._vm?.AtualizarDados(dados); + }); + break; + + case DockStep.ParametrizacaoRover: + _viewOperacaoCenter?.MapaOperacao?.markers?.UpdateMarkerPosition(VariaveisControleOperacao.BaseMarkerID, lat: dados?.Latitude, lon: dados?.Longitude, heading: dados?.OrientacaoReal); + break; + } + } + + public void AtualizarDadosMapa(OperacaoParametrosModel dados) + { + _viewOperacaoCenter?.MapaOperacao?.CarregarDadosMapa(dados?.Mapa, dados?.RuasPercorrer); + } + + #region TELA 2 + + private string? _ultimoMapaCarregado; + + private void OnCarregarMapaSolicitado(string caminhoArquivo) + { + try + { + _ultimoMapaCarregado = caminhoArquivo; + _viewPreparacaoMapa?.MapControl?.LoadMapFile(caminhoArquivo); + MapaCarregado = true; + _viewPreparacaoMapaBottom?._vm.AtualizarProgresso(0, $"Mapa carregado: {System.IO.Path.GetFileName(caminhoArquivo)}"); + } + catch (Exception ex) + { + MapaCarregado = false; + _viewPreparacaoMapaBottom?._vm.AtualizarProgresso(0, $"Erro ao carregar mapa: {ex.Message}"); + } + } + + private async void OnFixarBaseSolicitado() + { + try + { + if (!Variaveis.GpsService.BaseFix.FixLiberado) + { + bool fixar = false; + + if (Variaveis.GpsService.BaseFix.PosicaoBaseFixada) + { + var res = System.Windows.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) + fixar = true; + } + else + { + fixar = true; + } + + if (fixar) + { + _viewPreparacaoMapaBottom?._vm.IniciarFixacao(); + await Task.Run(async () => await Variaveis.GpsService.ConfigurarModulo(true)); + } + } + else + { + Variaveis.GpsService.BaseFix.ReiniciarFix(); + BaseFixada = false; + _viewPreparacaoMapaBottom?._vm.CancelarFixacao("Fixação da base interrompida pelo operador."); + } + } + catch (Exception ex) + { + _viewPreparacaoMapaBottom?._vm.FinalizarFixacao(false, $"Erro na fixação da base: {ex.Message}"); + } + } + + public void AtualizarProgressoFixacaoBase(double progresso, string status) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + _viewPreparacaoMapaBottom?._vm.AtualizarProgresso(progresso, status); + })); + } + + public void FinalizarFixacaoBase(bool sucesso, string mensagem) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + BaseFixada = sucesso; + _viewPreparacaoMapaBottom?._vm.FinalizarFixacao(sucesso, mensagem); + })); + } + + public void CancelarFixacaoBase(string mensagem) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + BaseFixada = false; + _viewPreparacaoMapaBottom?._vm.CancelarFixacao(mensagem); + })); + } + + #endregion + + #region TELA 3 + + public void AtualizarTopOperacaoBase() + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + _viewOperacaoTop?._vm.DefinirStatusNormal("BASE", "Base selecionada. Sistema pronto."); + })); + } + + public void AtualizarTopOperacaoRover(string nomeRover, bool modoManual) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + if (modoManual) + _viewOperacaoTop?._vm.DefinirStatusManual($"{nomeRover}", "Rover em modo manual. Comandos locais liberados."); + else + _viewOperacaoTop?._vm.DefinirStatusNormal($"{nomeRover}", "Rover selecionado. Operação pronta."); + })); + } + + public void AtualizarTopOperacaoAlerta(string contexto, string mensagem) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + _viewOperacaoTop?._vm.DefinirStatusAtencao(contexto, mensagem); + })); + } + + public void AtualizarTopOperacaoCritico(string contexto, string mensagem) + { + System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + _viewOperacaoTop?._vm.DefinirStatusCritico(contexto, mensagem); + })); + } + + + private void OnAlertaSelecionadoChanged(AlertaModel alerta) + { + if (alerta == null) return; + + AtualizarTopOperacaoAlerta( + $"ALERTA {alerta.Rover_ID}", + $"{alerta.Titulo}: {alerta.Mensagem}" + ); + + // depois você pode navegar para diagnostico, abrir popup, focar modulo, etc + } + + public void AdicionarAlerta(string roverId, AgroBase.Models.Enums.T_Code modulo, SeveridadeAlerta severidade, string mensagem, string modId = null) + { + (_viewOperacaoBottom.DataContext as OperacaoBottomViewModel)?.AdicionarAlerta(roverId, modulo, severidade, mensagem, modId); + } + + public void RemoverAlerta(string roverId, AgroBase.Models.Enums.T_Code modulo, SeveridadeAlerta? severidade = null, string modId = null) + { + (_viewOperacaoBottom.DataContext as OperacaoBottomViewModel)?.RemoverAlerta(roverId, modulo, severidade, modId); + } + + + 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; + }); + } + } + + private void OnRoverSelecionadoChanged(string roverId) + { + _viewOperacaoRight?._vm?.DestacarRoverNoMapa(roverId); + } + + private void OnRoverSelecionadoChanged(RoverCardModel rover) + { + bool dados_base = rover == null; + if (dados_base) + { + if (!VariaveisControleOperacao.BaseEmFoco) + { + _viewOperacaoCenter?.PararStreamCameraFrontal(); + _viewOperacaoCenter?.PararStreamCameraErvas(); + VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco?.IP, false); + } + VariaveisControleOperacao.RoverEmFoco?.RuasPercorrer?.Clear(); + AtualizarDadosMapa(VariaveisControleOperacao.RoverEmFoco); + VariaveisControleOperacao.SelectedRoverId = VariaveisControleOperacao.BaseMarkerID; + _viewOperacaoCenter?.MapaOperacao?.markers?.SetMarkerFocused(VariaveisControleOperacao.SelectedRoverId, true); + + _viewOperacaoCenter?._vm?.SelecionarRoverLista(rover); + + AtualizarTopOperacaoRover(VariaveisControleOperacao.SelectedRoverId, true); + _viewOperacaoLeft?._vm?.DefinirSemRover(); + } + else + { + VariaveisControleOperacao.SelectedRoverId = rover.NumeroSerie.ToString(); + _viewOperacaoCenter?.MapaOperacao?.markers?.SetMarkerFocused(VariaveisControleOperacao.SelectedRoverId, true); + AtualizarDadosTela(VariaveisControleOperacao.RoverEmFoco); + + VariaveisControleOperacao.RequisitarParametrosOperacao(); + _viewOperacaoCenter?.IniciarStreamCameraFrontal(); + _viewOperacaoCenter?.IniciarStreamCameraErvas(); + VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco?.IP, true); + + _viewOperacaoCenter?._vm?.SelecionarRoverLista(rover); + + AtualizarTopOperacaoRover(VariaveisControleOperacao.SelectedRoverId, ModoOperacaoAtual == ModoOperacao.Manual); + } + } + private void OnSecaoSelecionadaChanged(SecaoRightBar secao) + { + _viewOperacaoCenter?._vm?.DefinirSecao(secao); + } + + private void OnLerParametrosSolicitado() + { + if (VariaveisControleOperacao.RoverEmFoco == null) + return; + + VariaveisControleOperacao.RequisitarParametrosOperacao(); + + _viewOperacaoRight?._vm?.AtualizarParametros(VariaveisControleOperacao.RoverEmFoco); + } + private void OnSalvarParametrosSolicitado(OperacaoParametrosModel parametros) + { + if (VariaveisControleOperacao.RoverEmFoco == null) + return; + + var rover = VariaveisControleOperacao.RoverEmFoco; + + VariaveisControleOperacao.EnviarParametrosOperacao(new OperacaoParametrosModel() + { + Modo = parametros.Modo ?? AgroBase.Models.Enums.ModoOperacao.NaoDefinido, + Descricao = parametros.Descricao, + QtdCamerasSolo = parametros.QtdCamerasSolo, + QtdBicos = parametros.QtdBicos, + CapacidadeReservatorio = parametros.CapacidadeReservatorio, + + Controle = parametros.Controle, + + ModulosMandatorios = rover?.ModulosMandatorios, + ParametrosMandatorios = rover?.ParametrosMandatorios, + + RuasPercorrer = _viewOperacaoCenter?.MapaOperacao?.RuasMapaCarregado?.Where(x => x.Selected)?.Select(x => x.Id)?.ToList(), + Mapa = _viewOperacaoCenter?.MapaOperacao?.CriarDadosMapa() + }); + } + + + + + + + + #endregion + + + + } + + + public enum ModoOperacao + { + Automatico = 1, + Manual = 2 + } + + public enum DockStep + { + ConfigConexao = 1, + PreparacaoMapa = 2, + ParametrizacaoRover = 3 + } + +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Views/ConfigConexao/ConfigConexaoView.xaml b/AgroBase/OperationControl/Views/ConfigConexao/ConfigConexaoView.xaml new file mode 100644 index 000000000..5972ddf9d --- /dev/null +++ b/AgroBase/OperationControl/Views/ConfigConexao/ConfigConexaoView.xaml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +