Implementado novo layout para OperationControl

This commit is contained in:
Diego Freitas 2026-03-10 16:56:55 -03:00
parent 7ff339eaf6
commit d0e58be553
40 changed files with 4804 additions and 311 deletions

View File

@ -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<OperacaoModulosMandatoriosModel> ModulosMandatorios { get; set; }
public List<OperacaoParametrosMandatoriosModel> ParametrosMandatorios { get; set; }
public OperacaoParametrosControleModel Controle { get; set; }
public MapaFeatureCollectionModel Mapa { get; set; }
public List<string> 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

View File

@ -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<StreetMapDictionaryModel> RuasMapaCarregado;
public event EventHandler<StreetMapClickedEventArgs>? 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

View File

@ -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;
}
}
}

View File

@ -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);
}));
}
}
}

View File

@ -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<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
}

View File

@ -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<OperacaoParametrosDadosModel>(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
{

View File

@ -43,7 +43,31 @@
<Compile Update="Controls\TemperatureIndicator.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\ConfigConexaoView.xaml.cs">
<Compile Update="Views\ConfigConexao\ConfigConexaoView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Operacao\OperacaoBottomView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Operacao\OperacaoCenterView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Operacao\OperacaoLeftView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Operacao\OperacaoRightView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Operacao\OperacaoTopView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\PreparacaoMapa\PreparacaoMapaBottomView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\PreparacaoMapa\PreparacaoMapaTopView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\PreparacaoMapa\PreparacaoMapaView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Windows\DockWindow.xaml.cs">
@ -90,7 +114,31 @@
<Page Update="Controls\TemperatureIndicator.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\ConfigConexaoView.xaml">
<Page Update="Views\ConfigConexao\ConfigConexaoView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Operacao\OperacaoBottomView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Operacao\OperacaoCenterView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Operacao\OperacaoLeftView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Operacao\OperacaoRightView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Operacao\OperacaoTopView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\PreparacaoMapa\PreparacaoMapaBottomView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\PreparacaoMapa\PreparacaoMapaTopView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\PreparacaoMapa\PreparacaoMapaView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Windows\DockWindow.xaml">

View File

@ -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)
{

View File

@ -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));
}
}
}

View File

@ -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<bool> 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)
{

View File

@ -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<AlertaModel>();
_toastTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(5)
};
_toastTimer.Tick += (s, e) =>
{
_toastTimer.Stop();
ToastAlerta = null;
};
}
public ObservableCollection<AlertaModel> 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<AlertaModel> 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);
}
}
}
}
}
}

View File

@ -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<string> 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
}
}

View File

@ -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));
}
}
}

View File

@ -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<RoverCardModel>();
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<SecaoRightBar>(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<SecaoRightBar> SecaoSelecionadaChanged;
public event Action<RoverCardModel> RoverSelecionadoChanged;
public event Action? LerParametrosSolicitado;
public event Action<OperacaoParametrosModel>? SalvarParametrosSolicitado;
public ObservableCollection<RoverCardModel> 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<RoverCardModel>()).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<ResumoOperacionalModel> 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
}
}

View File

@ -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));
}
}
}

View File

@ -0,0 +1,148 @@
using OperationControl.Helpers;
using System.ComponentModel;
using System.Windows.Input;
namespace OperationControl.ViewModels
{
public class PreparacaoMapaBottomViewModel : INotifyPropertyChanged
{
public event Action<string>? 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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<OperacaoParametrosModel> 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<double>() ?? 0.0;
double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value<double>() ?? 0.0;
double bwTot = conexao?.detalhes?["bw_total_mbps"]?.Value<double>() ?? 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
}
}

View File

@ -0,0 +1,159 @@
<UserControl x:Class="OperationControl.Views.ConfigConexaoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="#161616">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Cards principais -->
<Grid Grid.Row="0" Margin="12,12,12,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Rede -->
<Border Grid.Column="0"
Margin="8"
Padding="18"
CornerRadius="10"
BorderThickness="2"
Background="#202020"
BorderBrush="{Binding CorBordaRede}">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="REDE"
FontSize="22"
FontWeight="Bold"
HorizontalAlignment="Center"
Foreground="White"/>
<TextBlock Text="🌐"
FontSize="52"
HorizontalAlignment="Center"
Margin="0,8,0,8"/>
<TextBlock Text="{Binding StatusRedeTexto}"
FontSize="16"
FontWeight="SemiBold"
HorizontalAlignment="Center"
Foreground="{Binding CorStatusRede}"
Margin="0,0,0,8"/>
<TextBlock Text="{Binding DetalhesRede}"
TextWrapping="Wrap"
TextAlignment="Center"
Foreground="#DDDDDD"
Margin="0,0,0,12"/>
<Button Content="Configurar rede"
Width="180"
HorizontalAlignment="Center"
Command="{Binding AbrirConfigRedeCommand}"/>
</StackPanel>
</Border>
<!-- GNSS -->
<Border Grid.Column="1"
Margin="8"
Padding="18"
CornerRadius="10"
BorderThickness="2"
Background="#202020"
BorderBrush="{Binding CorBordaGnss}">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="GNSS"
FontSize="22"
FontWeight="Bold"
HorizontalAlignment="Center"
Foreground="White"/>
<TextBlock Text="📡"
FontSize="52"
HorizontalAlignment="Center"
Margin="0,8,0,8"/>
<TextBlock Text="{Binding StatusGnssTexto}"
FontSize="16"
FontWeight="SemiBold"
HorizontalAlignment="Center"
Foreground="{Binding CorStatusGnss}"
Margin="0,0,0,8"/>
<TextBlock Text="{Binding DetalhesGnss}"
TextWrapping="Wrap"
TextAlignment="Center"
Foreground="#DDDDDD"
Margin="0,0,0,12"/>
<Button Content="Ver detalhes GNSS"
Width="180"
HorizontalAlignment="Center"
Command="{Binding AbrirDetalhesGnssCommand}"/>
</StackPanel>
</Border>
</Grid>
<!-- Seleção do modo -->
<Border Grid.Row="1"
Margin="20,0,20,8"
Padding="16"
Background="#202020"
CornerRadius="10"
BorderBrush="#3A3A3A"
BorderThickness="1">
<StackPanel>
<TextBlock Text="Tipo de operação"
FontSize="18"
FontWeight="Bold"
Foreground="White"
Margin="0,0,0,10"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<RadioButton Content="Automático"
GroupName="ModoOperacao"
Margin="0,0,20,0"
FontSize="15"
Foreground="White"
IsChecked="{Binding OperacaoAutomaticaSelecionada, Mode=OneWay}"
Command="{Binding SelecionarModoAutomaticoCommand}"/>
<RadioButton Content="Manual"
GroupName="ModoOperacao"
FontSize="15"
Foreground="White"
IsChecked="{Binding OperacaoManualSelecionada, Mode=OneWay}"
Command="{Binding SelecionarModoManualCommand}"/>
</StackPanel>
<TextBlock Text="{Binding DescricaoModoOperacao}"
Margin="0,12,0,0"
TextWrapping="Wrap"
TextAlignment="Center"
Foreground="#D0D0D0"
FontSize="13"/>
</StackPanel>
</Border>
<!-- Rodapé / resumo -->
<Border Grid.Row="2"
Margin="20,0,20,12"
Padding="12"
Background="#252525"
CornerRadius="8"
BorderBrush="#3A3A3A"
BorderThickness="1">
<TextBlock Text="{Binding MensagemGeral}"
FontSize="14"
Foreground="White"
TextAlignment="Center"
TextWrapping="Wrap"/>
</Border>
</Grid>
</UserControl>

View File

@ -1,107 +0,0 @@
<UserControl x:Class="OperationControl.Views.ConfigConexaoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Blocos principais -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Bloco Rede -->
<Border Grid.Column="0"
Margin="16"
Padding="16"
CornerRadius="8"
BorderThickness="2"
BorderBrush="{Binding CorBordaRede}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="REDE"
FontSize="24"
FontWeight="Bold"
Margin="0,0,0,8"
HorizontalAlignment="Center"/>
<!-- Ícone grandão - pode trocar por Path/Image depois -->
<TextBlock Text="🌐"
FontSize="64"
HorizontalAlignment="Center"
Margin="0,0,0,8"/>
<TextBlock Text="{Binding StatusRedeTexto}"
FontSize="16"
FontWeight="SemiBold"
HorizontalAlignment="Center"
Margin="0,0,0,4"
Foreground="{Binding CorStatusRede}"/>
<TextBlock Text="{Binding DetalhesRede}"
TextWrapping="Wrap"
TextAlignment="Center"
Margin="0,0,0,8"/>
<Button Content="Configurar rede..."
Width="180"
Margin="0,8,0,0"
Command="{Binding AbrirConfigRedeCommand}"/>
</StackPanel>
</Border>
<!-- Bloco GNSS -->
<Border Grid.Column="1"
Margin="16"
Padding="16"
CornerRadius="8"
BorderThickness="2"
BorderBrush="{Binding CorBordaGnss}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="GNSS / UM982"
FontSize="24"
FontWeight="Bold"
Margin="0,0,0,8"
HorizontalAlignment="Center"/>
<!-- Ícone -->
<TextBlock Text="📡"
FontSize="64"
HorizontalAlignment="Center"
Margin="0,0,0,8"/>
<TextBlock Text="{Binding StatusGnssTexto}"
FontSize="16"
FontWeight="SemiBold"
HorizontalAlignment="Center"
Margin="0,0,0,4"
Foreground="{Binding CorStatusGnss}"/>
<TextBlock Text="{Binding DetalhesGnss}"
TextWrapping="Wrap"
TextAlignment="Center"
Margin="0,0,0,8"/>
<Button Content="Ver detalhes GNSS..."
Width="180"
Margin="0,8,0,0"
Command="{Binding AbrirDetalhesGnssCommand}"/>
</StackPanel>
</Border>
</Grid>
<!-- Linha embaixo para uma mensagem geral -->
<TextBlock Grid.Row="1"
Text="{Binding MensagemGeral}"
Margin="16"
FontSize="14"
TextWrapping="Wrap"
TextAlignment="Center"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,203 @@
<UserControl x:Class="OperationControl.Views.Operacao.OperacaoBottomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views.Operacao"
xmlns:conv="clr-namespace:OperationControl.Converters"
xmlns:models="clr-namespace:OperationControl.Models"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<conv:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
<Style x:Key="ThinScrollBar" TargetType="ScrollBar">
<Setter Property="Height" Value="6"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollBar">
<Grid Background="Transparent">
<Track x:Name="PART_Track" Height="6">
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.LineLeftCommand" Visibility="Collapsed" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.LineRightCommand" Visibility="Collapsed" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Height="6"
Background="#4B5563"
Margin="0"
BorderThickness="0"/>
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Background="Transparent">
<!-- Barra inferior -->
<Border x:Name="brdBottom"
Height="80"
VerticalAlignment="Top"
BorderBrush="#222"
BorderThickness="1"
Background="#11161C">
<Grid>
<ScrollViewer x:Name="svAlertas"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled"
CanContentScroll="False"
Margin="6,0,6,0">
<ScrollViewer.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource ThinScrollBar}" />
</ScrollViewer.Resources>
<ItemsControl ItemsSource="{Binding AlertasAtivos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:AlertaModel}">
<Border Margin="6,4,0,4"
Padding="8,4"
CornerRadius="6"
Cursor="Hand"
MouseLeftButtonUp="Alerta_MouseLeftButtonUp">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#1C1F23"/>
<Setter Property="BorderBrush" Value="#3A424F"/>
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Severidade}" Value="Warning">
<Setter Property="BorderBrush" Value="#EFBF2F"/>
</DataTrigger>
<DataTrigger Binding="{Binding Severidade}" Value="Error">
<Setter Property="BorderBrush" Value="#FF5656"/>
</DataTrigger>
<DataTrigger Binding="{Binding Severidade}" Value="Critical">
<Setter Property="BorderBrush" Value="#FF5656"/>
<Setter Property="Background" Value="#3A171C"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Icone}"
FontSize="14"
Margin="0,0,6,0"/>
<StackPanel>
<TextBlock Text="{Binding Titulo}"
FontWeight="Bold"
Foreground="#F5F7FA"
FontSize="11"/>
<TextBlock Text="{Binding Mensagem}"
Foreground="#B7C0CC"
FontSize="10"
TextTrimming="CharacterEllipsis"
MaxWidth="260"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<TextBlock Text="Nenhum alerta ativo"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="#636C7A"
FontSize="11">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AlertasAtivos.Count}" Value="0">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
<!-- Toast -->
<Border x:Name="brdToastAlerta"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="0,-78,20,0"
Padding="10,8"
CornerRadius="8"
Background="#1C1F23"
BorderBrush="#3A424F"
BorderThickness="1"
Cursor="Hand"
Visibility="{Binding ToastAlerta, Converter={StaticResource NullToVisibilityConverter}}"
MouseLeftButtonUp="ToastAlerta_MouseLeftButtonUp">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Opacity" Value="0.95"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ToastAlerta.Severidade}" Value="Warning">
<Setter Property="BorderBrush" Value="#EFBF2F"/>
</DataTrigger>
<DataTrigger Binding="{Binding ToastAlerta.Severidade}" Value="Error">
<Setter Property="BorderBrush" Value="#FF5656"/>
</DataTrigger>
<DataTrigger Binding="{Binding ToastAlerta.Severidade}" Value="Critical">
<Setter Property="BorderBrush" Value="#FF5656"/>
<Setter Property="Background" Value="#3A171C"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ToastAlerta.Icone}"
FontSize="16"
Margin="0,0,6,0"/>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Border Background="#2F3B4A"
CornerRadius="4"
Padding="4,1"
Margin="0,0,6,0">
<TextBlock Text="{Binding ToastAlerta.Rover_ID}"
Foreground="White"
FontSize="10"
FontWeight="Bold"/>
</Border>
<TextBlock Text="{Binding ToastAlerta.Titulo}"
FontWeight="Bold"
Foreground="#F5F7FA"
FontSize="11"/>
</StackPanel>
<TextBlock Text="{Binding ToastAlerta.Mensagem}"
Foreground="#B7C0CC"
FontSize="10"
TextWrapping="Wrap"
MaxWidth="320"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,68 @@
using OperationControl.Models;
using OperationControl.ViewModels.Views.Operacao;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace OperationControl.Views.Operacao
{
/// <summary>
/// Interaction logic for OperacaoBottomView.xaml
/// </summary>
public partial class OperacaoBottomView : System.Windows.Controls.UserControl
{
public OperacaoBottomViewModel _vm;
public OperacaoBottomView()
{
InitializeComponent();
_vm = new OperacaoBottomViewModel();
DataContext = _vm;
}
public OperacaoBottomView(OperacaoBottomViewModel vm)
{
InitializeComponent();
_vm = vm;
DataContext = _vm;
}
private void Alerta_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (sender is Border border && border.DataContext is AlertaModel alerta)
{
_vm?.SelecionarAlerta(alerta);
System.Windows.MessageBox.Show(
$"Rover: {alerta.Rover_ID}\n" +
$"Módulo: {alerta.Modulo}\n" +
$"Severidade: {alerta.Severidade}\n" +
$"Mensagem: {alerta.Mensagem}\n\n" +
$"Aqui depois podemos navegar para a ação correspondente.",
"Alerta selecionado",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
}
private void ToastAlerta_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var alerta = _vm?.ToastAlerta;
if (alerta == null)
return;
_vm.LimparToast();
_vm.SelecionarAlerta(alerta);
System.Windows.MessageBox.Show(
$"Toast clicado\n\n" +
$"Rover: {alerta.Rover_ID}\n" +
$"Módulo: {alerta.Modulo}\n" +
$"Severidade: {alerta.Severidade}\n" +
$"Mensagem: {alerta.Mensagem}\n\n" +
$"Aqui depois podemos disparar a navegação ou foco no diagnóstico.",
"Toast de alerta",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
}
}

View File

@ -0,0 +1,74 @@
<UserControl x:Class="OperationControl.Views.Operacao.OperacaoCenterView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views.Operacao"
xmlns:controls="clr-namespace:OperationControl.Controls"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</UserControl.Resources>
<Grid Background="#181818">
<!-- MAPA -->
<Grid Visibility="{Binding ExibirMapa, Converter={StaticResource BoolToVisibilityConverter}}">
<Border Margin="12" Padding="0" CornerRadius="10" Background="#202020" BorderBrush="#353535" BorderThickness="1" ClipToBounds="True">
<controls:MapViewControl x:Name="MapaOperacao" MarkerClicked="MAP_MarkerClicked" StreetMapClicked="MAP_StreetMapClicked" Latitude="-22.1726572492617" Longitude="-47.3952163870556" Scale="5000" />
</Border>
</Grid>
<!-- DIAGNÓSTICO -->
<Grid Visibility="{Binding ExibirDiagnostico, Converter={StaticResource BoolToVisibilityConverter}}">
<Border Margin="12" Padding="20" CornerRadius="10" Background="#202020" BorderBrush="#353535" BorderThickness="1">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding TituloCentro}" HorizontalAlignment="Center" FontSize="24" FontWeight="SemiBold" Foreground="#DDDDDD" TextAlignment="Center"/>
<TextBlock Text="{Binding SubtituloCentro}" HorizontalAlignment="Center" Margin="0,14,0,0" FontSize="14" Foreground="#AAAAAA" TextAlignment="Center" TextWrapping="Wrap" MaxWidth="500"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- MONITORAMENTO -->
<Grid Visibility="{Binding ExibirMonitoramento, Converter={StaticResource BoolToVisibilityConverter}}">
<Border Margin="12" Padding="20" CornerRadius="10" Background="#202020" BorderBrush="#353535" BorderThickness="1">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding TituloCentro}" HorizontalAlignment="Center" FontSize="24" FontWeight="SemiBold" Foreground="#DDDDDD" TextAlignment="Center"/>
<TextBlock Text="{Binding SubtituloCentro}" HorizontalAlignment="Center" Margin="0,14,0,0" FontSize="14" Foreground="#AAAAAA" TextAlignment="Center" TextWrapping="Wrap" MaxWidth="500"/>
</StackPanel>
</Grid>
</Border>
<Border x:Name="brdCameraFrontal" BorderBrush="Black" BorderThickness="1" CornerRadius="8" Margin="1453,0,7,735">
<Canvas>
<Label Content="Câmera frontal | 12 fps | 230 kbps | 0,00% cana | 43 cm - 27 cm | 12,95° | CaminhandoRua" FontSize="10" HorizontalAlignment="Center" VerticalAlignment="Top" Canvas.Top="-3"/>
<!--<vlc:VideoView x:Name="VideoFront" Background="Black" Height="237" Width="438" HorizontalAlignment="Center" Canvas.Top="25" VerticalAlignment="Top" Canvas.Left="10"/>-->
<Grid Width="438" Height="237" Canvas.Top="25" Canvas.Left="10" Background="Black">
<TextBlock Text="SEM SINAL" Foreground="Gray" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image x:Name="imgVideoFront" Stretch="Uniform" RenderOptions.BitmapScalingMode="LowQuality" />
</Grid>
<ComboBox x:Name="cmbCameraFrontalFrameTipo" Canvas.Left="10" Canvas.Top="25" HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" SelectionChanged="cmbCameraFrontalFrameTipo_SelectionChanged" />
</Canvas>
</Border>
<Border x:Name="brdCameraTraseira" BorderBrush="Black" BorderThickness="1" CornerRadius="8" Margin="1453,281,7,491">
<Canvas>
<Label Content="Câmera traseira | 10 fps | 193 kbps | 5,41% erva | 0,00% cana" FontSize="10" HorizontalAlignment="Center" VerticalAlignment="Top" Canvas.Top="-3"/>
<!--<vlc:VideoView x:Name="VideoWeed" Background="Black" Height="200" Width="438" HorizontalAlignment="Center" Canvas.Top="25" VerticalAlignment="Top" Canvas.Left="10"/>-->
<Grid Width="438" Height="200" Canvas.Top="25" Canvas.Left="10" Background="Black">
<TextBlock Text="SEM SINAL" Foreground="Gray" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image x:Name="imgVideoWeed" Stretch="Uniform" RenderOptions.BitmapScalingMode="LowQuality" />
</Grid>
<ComboBox x:Name="cmbCameraTraseiraFrameTipo" Canvas.Left="10" Canvas.Top="25" HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" SelectionChanged="cmbCameraTraseiraFrameTipo_SelectionChanged" />
</Canvas>
</Border>
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,88 @@
using OperationControl.Controls;
using OperationControl.Models;
using OperationControl.ViewModels.Views.Operacao;
using System.Windows.Controls;
namespace OperationControl.Views.Operacao
{
/// <summary>
/// Interaction logic for OperacaoCenterView.xaml
/// </summary>
public partial class OperacaoCenterView : System.Windows.Controls.UserControl
{
public OperacaoCenterViewModel _vm;
public OperacaoCenterView()
{
InitializeComponent();
_vm = new OperacaoCenterViewModel();
DataContext = _vm;
videoFront = new TcpVideoReceiver(5000, imgVideoFront);
videoWeed = new TcpVideoReceiver(5002, imgVideoWeed);
}
private TcpVideoReceiver videoFront;
private TcpVideoReceiver videoWeed;
#region CAMERA FRONTAL
private void cmbCameraFrontalFrameTipo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (VariaveisControleOperacao.SelectedRoverId == VariaveisControleOperacao.BaseMarkerID) return;
PararStreamCameraFrontal();
IniciarStreamCameraFrontal();
}
public void IniciarStreamCameraFrontal()
{
videoFront.Iniciar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex));
}
public void PararStreamCameraFrontal(bool finalizar = false)
{
videoFront.Parar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex));
}
#endregion
#region CAMERA ERVAS
private void cmbCameraTraseiraFrameTipo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (VariaveisControleOperacao.SelectedRoverId == VariaveisControleOperacao.BaseMarkerID) return;
PararStreamCameraErvas();
IniciarStreamCameraErvas();
}
public void IniciarStreamCameraErvas()
{
videoWeed.Iniciar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex));
}
public void PararStreamCameraErvas(bool finalizar = false)
{
videoWeed.Parar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex));
}
#endregion
private void MAP_MarkerClicked(object? sender, MapMarkerManager.MarkerClickedEventArgs e)
{
_vm.ProcessarMarkerClicked(e);
}
private void MAP_StreetMapClicked(object? sender, MapViewControl.StreetMapClickedEventArgs e)
{
_vm.ProcessarStreetMapClicked(e);
}
}
}

View File

@ -0,0 +1,223 @@
<UserControl x:Class="OperationControl.Views.Operacao.OperacaoLeftView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views.Operacao"
mc:Ignorable="d"
d:DesignHeight="700" d:DesignWidth="280">
<UserControl.Resources>
<Style x:Key="LeftInfoCardStyle" TargetType="Border">
<Setter Property="Background" Value="#2A2A2A"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="10,8"/>
<Setter Property="Margin" Value="0,0,0,8"/>
<Setter Property="BorderBrush" Value="#3A3A3A"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
<Style x:Key="LeftActionButtonStyle" TargetType="Button">
<Setter Property="Height" Value="42"/>
<Setter Property="Margin" Value="0,0,0,8"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Background="#181818">
<Border Margin="10"
Padding="12"
CornerRadius="10"
Background="#202020"
BorderBrush="#343434"
BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- CONTEÚDO SUPERIOR -->
<ScrollViewer Grid.Row="0"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Opacity="{Binding OpacityPainel}">
<TextBlock Text="Operação em Andamento"
FontSize="18"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
Margin="0,0,0,6"/>
<TextBlock Text="{Binding NomeRover}"
FontSize="14"
FontWeight="SemiBold"
Foreground="#D0D0D0"
HorizontalAlignment="Center"
TextAlignment="Center"
Margin="0,0,0,14"/>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="🚜" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Velocidade" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding Velocidade}" Foreground="White" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="↩" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Ângulo direcional" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding AnguloDirecional}" Foreground="White" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="⇄" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Movimento" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding TipoMovimento}" Foreground="White" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="🌡" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Temperatura" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding Temperatura}" Foreground="{Binding CorTemperatura}" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="🔋" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Bateria" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding Bateria}" Foreground="{Binding CorBateria}" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="🧪" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Reservatório" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding Reservatorio}" Foreground="{Binding CorReservatorio}" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="📶" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Rede" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding SaudeRede}" Foreground="{Binding CorRede}" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
<Border Style="{StaticResource LeftInfoCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="📡" FontSize="18" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="GNSS" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{Binding SaudeGnss}" Foreground="{Binding CorGnss}" FontWeight="Bold" VerticalAlignment="Center"/>
</Grid>
</Border>
</StackPanel>
</ScrollViewer>
<!-- RODAPÉ FIXO -->
<Border Grid.Row="1"
Margin="0,12,0,0"
Padding="10"
CornerRadius="10"
Background="#1A1A1A"
BorderBrush="#343434"
BorderThickness="1"
IsEnabled="{Binding RoverSelecionado}"
Opacity="{Binding OpacityPainel}">
<StackPanel>
<TextBlock Text="Ações principais"
Foreground="#CFCFCF"
FontSize="13"
FontWeight="SemiBold"
Margin="0,0,0,10"/>
<Button Content="{Binding TextoBotaoOperacao}"
Command="{Binding AlternarOperacaoCommand}"
Style="{StaticResource LeftActionButtonStyle}"
Background="{Binding CorBotaoOperacao}"
BorderBrush="{Binding BordaBotaoOperacao}"/>
<Button Content="⛔ Parada de Emergência"
Command="{Binding ParadaEmergenciaCommand}"
Style="{StaticResource LeftActionButtonStyle}"
Background="#7A1E1E"
BorderBrush="#B83A3A"/>
<Button Content="✖ Cancelar Operação"
Command="{Binding CancelarOperacaoCommand}"
Style="{StaticResource LeftActionButtonStyle}"
Background="#3A3A3A"
BorderBrush="#666666"
Margin="0,0,0,0"/>
</StackPanel>
</Border>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,35 @@
using OperationControl.ViewModels.Views.Operacao;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace OperationControl.Views.Operacao
{
/// <summary>
/// Interaction logic for OperacaoLeftView.xaml
/// </summary>
public partial class OperacaoLeftView : System.Windows.Controls.UserControl
{
public OperacaoLeftViewModel _vm;
public OperacaoLeftView()
{
InitializeComponent();
_vm = new OperacaoLeftViewModel();
DataContext = _vm;
_vm.DefinirSemRover();
}
}
}

View File

@ -0,0 +1,497 @@
<UserControl x:Class="OperationControl.Views.Operacao.OperacaoRightView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views.Operacao"
mc:Ignorable="d">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<Style x:Key="MenuButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="0,0,0,10"/>
<Setter Property="Height" Value="52"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Background" Value="#2A2A2A"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#4A4A4A"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="10"
Padding="12">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#343434"/>
<Setter Property="BorderBrush" Value="#6A6A6A"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="RoverCardButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="0,0,0,12"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ThinScrollBar" TargetType="ScrollBar">
<Setter Property="Width" Value="8"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollBar">
<Grid Width="8" Background="Transparent">
<Border Margin="2,0,2,0" Background="#2F2F2F" CornerRadius="3"/>
<Track x:Name="PART_Track" Margin="2,0,2,0" IsDirectionReversed="True">
<Track.DecreaseRepeatButton>
<RepeatButton Opacity="0" IsHitTestVisible="False"/>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb>
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Border Background="#6B7280" CornerRadius="3" MinHeight="30"/>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Opacity="0" IsHitTestVisible="False"/>
</Track.IncreaseRepeatButton>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Background="#1C1C1C">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Cabeçalho -->
<Border Grid.Row="0" Background="#222222" BorderBrush="#343434" BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="44"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Painel do Operador" VerticalAlignment="Center" Margin="16,0,0,0" Foreground="White" FontSize="18" FontWeight="SemiBold"/>
<Button Grid.Column="1" Content="" Width="32" Height="32" Margin="0,0,8,0" HorizontalAlignment="Center" VerticalAlignment="Center" Command="{Binding ToggleCollapseCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#2E2E2E"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#505050"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="8">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsCollapsed}" Value="True">
<Setter Property="Content" Value=""/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Border>
<!-- Conteúdo -->
<Grid Grid.Row="1" Margin="14">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsCollapsed}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<!-- Sem rover selecionado -->
<Grid Visibility="{Binding SemRoverSelecionado, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="0,0,0,12">
<TextBlock Text="Selecione um rover disponível na rede" Foreground="#D0D0D0" FontSize="14" Margin="0,0,0,4"/>
<TextBlock Text="{Binding QuantidadeRoversTexto}" Foreground="#8E8E8E" FontSize="12"/>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Rovers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource RoverCardButtonStyle}" Command="{Binding DataContext.SelecionarRoverCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}">
<Border Background="{Binding CardBackground}" BorderBrush="{Binding BordaCardBrush}" BorderThickness="{Binding BordaCardThickness}" CornerRadius="12" Padding="14" SnapsToDevicePixels="True">
<StackPanel>
<TextBlock Text="{Binding EquipamentoTexto}" Foreground="White" FontSize="16" FontWeight="Bold"/>
<TextBlock Text="{Binding NumeroSerieTexto}" Foreground="#CFCFCF" Margin="0,8,0,0" FontSize="13"/>
<TextBlock Text="{Binding StatusTexto}" Foreground="{Binding StatusBrush}" Margin="0,8,0,0" FontSize="13" FontWeight="SemiBold"/>
</StackPanel>
</Border>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
<!-- Com rover selecionado -->
<Grid Visibility="{Binding ComRoverSelecionado, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="#262626" BorderBrush="#4E8EF7" BorderThickness="1" CornerRadius="12" Padding="14" Margin="0,0,0,14">
<StackPanel>
<TextBlock Text="{Binding RoverSelecionado.EquipamentoTexto}" Foreground="White" FontSize="16" FontWeight="Bold"/>
<TextBlock Text="{Binding RoverSelecionado.NumeroSerieTexto}" Foreground="#CFCFCF" Margin="0,8,0,0" FontSize="13"/>
<TextBlock Text="{Binding RoverSelecionado.StatusTexto}" Foreground="{Binding RoverSelecionado.StatusBrush}" Margin="0,8,0,0" FontSize="13" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Button Grid.Row="1" Content="{Binding TextoBotaoTopo}" Command="{Binding AcaoBotaoTopoCommand}" Margin="0,0,0,14" Height="42" Background="#2E2E2E" Foreground="White" BorderBrush="#555555" BorderThickness="1"/>
<Grid Grid.Row="2">
<!-- MENU -->
<StackPanel Visibility="{Binding EmMenu, Converter={StaticResource BoolToVisibilityConverter}}">
<Button Content="Diagnóstico" Style="{StaticResource MenuButtonStyle}" Command="{Binding SelecionarSecaoCommand}" CommandParameter="Diagnostico"/>
<Button Content="Parametrização ▶" Style="{StaticResource MenuButtonStyle}" Command="{Binding SelecionarSecaoCommand}" CommandParameter="Parametrizacao"/>
<Button Content="Controle ▶" Style="{StaticResource MenuButtonStyle}" Command="{Binding SelecionarSecaoCommand}" CommandParameter="Controle"/>
<Button Content="Resumo ▶" Style="{StaticResource MenuButtonStyle}" Command="{Binding SelecionarSecaoCommand}" CommandParameter="Resumo"/>
</StackPanel>
<!-- RESUMO -->
<ScrollViewer Visibility="{Binding EmResumo, Converter={StaticResource BoolToVisibilityConverter}}" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource ThinScrollBar}" />
</ScrollViewer.Resources>
<StackPanel>
<!-- CARD ESTADO GERAL -->
<Border Background="#262626" BorderBrush="#3E3E3E" BorderThickness="1" CornerRadius="10" Padding="12" Margin="0,0,0,10">
<StackPanel>
<TextBlock Text="Estado Geral" Foreground="White" FontSize="16" FontWeight="Bold" Margin="0,0,0,12"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Operação" Foreground="#CFCFCF" Margin="0,0,0,8"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Resumo.Operacao}" Foreground="White" Margin="12,0,0,8"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Modo" Foreground="#CFCFCF" Margin="0,0,0,8"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Resumo.Modo}" Foreground="White" Margin="12,0,0,8"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Status" Foreground="#CFCFCF" Margin="0,0,0,8"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Resumo.Status}" Foreground="White" Margin="12,0,0,8" TextWrapping="Wrap"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Carro" Foreground="#CFCFCF" Margin="0,0,0,8"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Resumo.Carro}" Foreground="White" Margin="12,0,0,8"/>
<TextBlock Grid.Row="4" Grid.Column="0" Text="Temperatura" Foreground="#CFCFCF" Margin="0,0,0,8"/>
<TextBlock Grid.Row="4" Grid.Column="1" Text="{Binding Resumo.Temperatura}" Foreground="White" Margin="12,0,0,8"/>
<TextBlock Grid.Row="5" Grid.Column="0" Text="Conexão" Foreground="#CFCFCF" Margin="0,0,0,8"/>
<TextBlock Grid.Row="5" Grid.Column="1" Text="{Binding Resumo.Conexao}" Foreground="White" Margin="12,0,0,8"/>
<TextBlock Grid.Row="6" Grid.Column="0" Text="Duração" Foreground="#CFCFCF" Margin="0,0,0,0"/>
<TextBlock Grid.Row="6" Grid.Column="1" Text="{Binding Resumo.Duracao}" Foreground="White" Margin="12,0,0,0"/>
</Grid>
</StackPanel>
</Border>
<!-- CARD PROGRESSO -->
<Border Background="#262626" BorderBrush="#3E3E3E" BorderThickness="1" CornerRadius="10" Padding="12" Margin="0,0,0,10">
<StackPanel>
<TextBlock Text="Progresso e Recursos" Foreground="White" FontSize="16" FontWeight="Bold" Margin="0,0,0,12"/>
<!-- Rua Atual -->
<TextBlock Text="Rua Atual" Foreground="#CFCFCF"/>
<ProgressBar Value="{Binding Resumo.RuaAtualPercentual}" Maximum="100" Height="12" Margin="0,6,0,4"/>
<TextBlock Text="{Binding Resumo.RuaAtual}" Foreground="White" Margin="0,0,0,12"/>
<!-- Área Total -->
<TextBlock Text="Área Total" Foreground="#CFCFCF"/>
<ProgressBar Value="{Binding Resumo.AreaTotalPercentual}" Maximum="100" Height="12" Margin="0,6,0,4"/>
<TextBlock Text="{Binding Resumo.AreaTotal}" Foreground="White" Margin="0,0,0,12"/>
<!-- Bateria -->
<TextBlock Text="Bateria" Foreground="#CFCFCF"/>
<ProgressBar Value="{Binding Resumo.BateriaPercentual}" Maximum="100" Height="12" Margin="0,6,0,4"/>
<TextBlock Text="{Binding Resumo.Bateria}" Foreground="White" Margin="0,0,0,12"/>
<!-- Herbicida -->
<TextBlock Text="Herbicida" Foreground="#CFCFCF"/>
<ProgressBar Value="{Binding Resumo.HerbicidaPercentual}" Maximum="100" Height="12" Margin="0,6,0,4"/>
<TextBlock Text="{Binding Resumo.Herbicida}" Foreground="White" Margin="0,0,0,12"/>
<!-- Infestação -->
<TextBlock Text="Infestação" Foreground="#CFCFCF"/>
<ProgressBar Value="{Binding Resumo.InfestacaoPercentual}" Maximum="100" Height="12" Margin="0,6,0,4"/>
<TextBlock Text="{Binding Resumo.Infestacao}" Foreground="White"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
<!-- PARAMETRIZAÇÃO -->
<ScrollViewer Visibility="{Binding EmParametrizacao, Converter={StaticResource BoolToVisibilityConverter}}" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource ThinScrollBar}" />
<Style x:Key="ParamLabelStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="#D0D0D0"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,0,6"/>
</Style>
<Style x:Key="ParamTextBoxStyle" TargetType="TextBox">
<Setter Property="Height" Value="34"/>
<Setter Property="Margin" Value="0,0,0,12"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="Background" Value="#2B2B2B"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="#5A5A5A"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
<Style x:Key="SectionCardStyle" TargetType="Border">
<Setter Property="Background" Value="#262626"/>
<Setter Property="BorderBrush" Value="#3E3E3E"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="10"/>
<Setter Property="Padding" Value="12"/>
<Setter Property="Margin" Value="0,0,0,12"/>
</Style>
<Style x:Key="ActionButtonStyle" TargetType="Button">
<Setter Property="Height" Value="40"/>
<Setter Property="Margin" Value="0,0,0,10"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="8">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ParamCheckBoxStyle" TargetType="CheckBox">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Margin" Value="0,0,0,10"/>
</Style>
</ScrollViewer.Resources>
<StackPanel Margin="0,0,2,0">
<TextBlock Text="Parametrização" Foreground="White" FontSize="18" FontWeight="Bold" Margin="0,0,0,4"/>
<TextBlock Text="Configure os parâmetros operacionais do rover." Foreground="#A8A8A8" FontSize="12" Margin="0,0,0,14"/>
<!-- OPERACAO -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="Operação" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<TextBlock Text="Descrição" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Descricao}" Style="{StaticResource ParamTextBoxStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
<!-- AUTOMAÇÃO GERAL -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="Automação Geral" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<CheckBox Content="Movimento Automático" IsChecked="{Binding Parametros.Controle.MovimentoAutomatico}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Direcional Automático" IsChecked="{Binding Parametros.Controle.DirecionalAutomatico}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Pulverizador Automático" IsChecked="{Binding Parametros.Controle.PulverizadorAutomatico}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Sonar Ativado" IsChecked="{Binding Parametros.Controle.SonarAtivado}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Parada por Obstáculo (OAK)" IsChecked="{Binding Parametros.Controle.OakParadaPorObstaculo}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Parada por Inclinação (IMU)" IsChecked="{Binding Parametros.Controle.ImuParadaPorInclinacao}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Frenagem Automática ao Parar" IsChecked="{Binding Parametros.Controle.FrenagemAutomaticaAoParar}" Style="{StaticResource ParamCheckBoxStyle}"/>
<CheckBox Content="Registrar Dados Pós-Processamento" IsChecked="{Binding Parametros.Controle.RegistrarDadosPosProcessamento}" Style="{StaticResource ParamCheckBoxStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
<!-- MOVIMENTO -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="Movimento" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<TextBlock Text="Velocidade sem Ervas (%)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.MovVelocidadeSErvasPercent, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Velocidade com Ervas (%)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.MovVelocidadeCErvasPercent, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="RPM Máx Calculado" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.MovRpmMax, Mode=OneWay}" IsReadOnly="True" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="RPM Mín Calculado" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.MovRpmMin, Mode=OneWay}" IsReadOnly="True" Style="{StaticResource ParamTextBoxStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
<!-- DIRECIONAL -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="Direcional" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<TextBlock Text="Ângulo Máximo (°)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.DirAnguloMaximo, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Velocidade Movimento" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.DirVelocidadeMovimento, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Tipo de Movimento" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.DirTipoMovimento, Mode=OneWay}" IsReadOnly="True" Style="{StaticResource ParamTextBoxStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
<!-- PULVERIZAÇÃO -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="Pulverização" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<TextBlock Text="Quantidade de Bicos" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.QtdBicos, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Capacidade do Reservatório (L)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.CapacidadeReservatorio, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Pressão Linha" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.AtuPressaoLinha, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Duração Atuação (ms)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.AtuDuracaoAtuacao, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Início Pulverização (%)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.AtuPercentualInicioPulverizacao, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Altura Área Pulverização" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.AtuAlturaAreaPulverizacao, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Ervas Bico On (%)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.AtuPercentualErvasBicoOn, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}"/>
<TextBlock Text="Ervas Bico Off (%)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.AtuPercentualErvasBicoOff, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
<!-- MPC -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="MPC" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<CheckBox Content="Matriz de Custo" IsChecked="{Binding Parametros.Controle.MpcMatrizCusto}" Style="{StaticResource ParamCheckBoxStyle}"/>
<TextBlock Text="Horizonte (m)" Style="{StaticResource ParamLabelStyle}"/>
<TextBox Text="{Binding Parametros.Controle.MpcHorizonte, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ParamTextBoxStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
<!-- AÇÕES -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<TextBlock Text="Ações" Foreground="White" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,12"/>
<Button Content="↻ Ler do Rover" Command="{Binding LerParametrosCommand}" Background="#2E2E2E" BorderBrush="#5C5C5C" Style="{StaticResource ActionButtonStyle}"/>
<Button Content="💾 Salvar no Rover" Command="{Binding SalvarParametrosCommand}" Background="#2F5D3A" BorderBrush="#4A8A5B" Style="{StaticResource ActionButtonStyle}" Margin="0,0,0,0"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
<!-- CONTROLE -->
<ScrollViewer Visibility="{Binding EmControle, Converter={StaticResource BoolToVisibilityConverter}}" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource ThinScrollBar}" />
</ScrollViewer.Resources>
<StackPanel>
<TextBlock Text="Controle" Foreground="White" FontSize="16" FontWeight="Bold"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,40 @@
using OperationControl.ViewModels.Views.Operacao;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace OperationControl.Views.Operacao
{
/// <summary>
/// Interaction logic for OperacaoRightView.xaml
/// </summary>
public partial class OperacaoRightView : System.Windows.Controls.UserControl
{
public OperacaoRightViewModel _vm;
public OperacaoRightView()
{
InitializeComponent();
_vm = new OperacaoRightViewModel();
DataContext = _vm;
}
public OperacaoRightView(OperacaoRightViewModel vm)
{
InitializeComponent();
_vm = vm;
DataContext = _vm;
}
}
}

View File

@ -0,0 +1,73 @@
<UserControl x:Class="OperationControl.Views.Operacao.OperacaoTopView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views.Operacao"
mc:Ignorable="d"
d:DesignHeight="70" d:DesignWidth="1200">
<Border Background="{Binding CorFundo}"
CornerRadius="10"
Padding="14,10"
Margin="10,8,10,6">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="260"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<!-- Contexto -->
<Border Grid.Column="0"
Background="#22000000"
CornerRadius="8"
Padding="12,8"
Margin="0,0,12,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="Selecionado: "
Foreground="#D8D8D8"
FontSize="14"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding ContextoSelecionado}"
Foreground="{Binding CorTexto}"
FontSize="16"
FontWeight="Bold"
VerticalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Mensagem principal -->
<Border Grid.Column="1"
Background="#16000000"
CornerRadius="8"
Padding="14,8"
Margin="0,0,12,0">
<TextBlock Text="{Binding MensagemStatus}"
Foreground="{Binding CorTexto}"
FontSize="17"
FontWeight="SemiBold"
TextWrapping="Wrap"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"/>
</Border>
<!-- Badge -->
<Border Grid.Column="2"
Background="{Binding CorBadge}"
CornerRadius="18"
Padding="12,8"
HorizontalAlignment="Right"
VerticalAlignment="Center"
MinWidth="120">
<TextBlock Text="{Binding BadgeTexto}"
Foreground="Black"
FontWeight="Bold"
FontSize="14"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center"/>
</Border>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,35 @@
using OperationControl.ViewModels.Views.Operacao;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace OperationControl.Views.Operacao
{
/// <summary>
/// Interaction logic for OperacaoTopView.xaml
/// </summary>
public partial class OperacaoTopView : System.Windows.Controls.UserControl
{
public OperacaoTopViewModel _vm;
public OperacaoTopView()
{
InitializeComponent();
_vm = new OperacaoTopViewModel();
DataContext = _vm;
_vm.DefinirStatusNormal("BASE", "Sistema pronto para operação.");
}
}
}

View File

@ -0,0 +1,79 @@
<UserControl x:Class="OperationControl.Views.PreparacaoMapaBottomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#181818" Margin="0">
<!-- Duas linhas -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Linha 1 -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="320"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Botão carregar mapa -->
<Button Grid.Column="0"
Content="Carregar mapa"
Margin="12,12,8,6"
Padding="18,10"
MinWidth="150"
Command="{Binding CarregarMapaCommand}"
IsEnabled="{Binding PodeCarregarMapa}"/>
<!-- Botão fixar base -->
<Button Grid.Column="1"
Content="{Binding TextoBotaoFixarBase}"
Margin="8,12,12,6"
Padding="18,10"
MinWidth="180"
Command="{Binding FixarBaseCommand}"/>
<!-- Barra de progresso -->
<ProgressBar Grid.Column="2"
Margin="12,20,8,8"
Height="22"
Minimum="0"
Maximum="100"
Value="{Binding ProgressoFixacao}"/>
<!-- Texto % -->
<TextBlock Grid.Column="3"
Text="{Binding ProgressoFixacaoTexto}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="White"
FontWeight="SemiBold"
FontSize="14"
Margin="0,0,8,0"/>
</Grid>
<!-- Linha 2 : Status -->
<Border Grid.Row="1"
Margin="12,4,12,10"
Padding="8"
Background="#222"
CornerRadius="6">
<StackPanel>
<TextBlock Text="Status da operação" Foreground="#AAAAAA" FontSize="11"/>
<TextBlock Text="{Binding StatusTexto}" Foreground="White" FontSize="14" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,33 @@
using OperationControl.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace OperationControl.Views
{
/// <summary>
/// Interaction logic for PreparacaoMapaBottomView.xaml
/// </summary>
public partial class PreparacaoMapaBottomView : System.Windows.Controls.UserControl
{
public PreparacaoMapaBottomViewModel _vm;
public PreparacaoMapaBottomView()
{
InitializeComponent();
_vm = new PreparacaoMapaBottomViewModel();
DataContext = _vm;
}
}
}

View File

@ -0,0 +1,73 @@
<UserControl x:Class="OperationControl.Views.PreparacaoMapaTopView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:OperationControl.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#222222" Margin="0">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<StackPanel Orientation="Horizontal" Margin="8,6">
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Latitude" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding LatitudeTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Longitude" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding LongitudeTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Altitude" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding AltitudeTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Heading" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding HeadingTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Fix" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding FixTexto}" Foreground="{Binding FixCor}" FontSize="16" FontWeight="Bold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Precisão" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding PrecisaoTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Satélites" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding SatelitesTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
<Border Background="#2E2E2E" CornerRadius="8" Padding="12" Margin="4,0">
<StackPanel>
<TextBlock Text="Idade Correção" Foreground="#BFBFBF" FontSize="11"/>
<TextBlock Text="{Binding IdadeCorrecaoTexto}" Foreground="White" FontSize="16" FontWeight="SemiBold"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>

View File

@ -0,0 +1,33 @@
using OperationControl.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace OperationControl.Views
{
/// <summary>
/// Interaction logic for PreparacaoMapaTopView.xaml
/// </summary>
public partial class PreparacaoMapaTopView : System.Windows.Controls.UserControl
{
public PreparacaoMapaTopViewModel _vm;
public PreparacaoMapaTopView()
{
InitializeComponent();
_vm = new PreparacaoMapaTopViewModel();
DataContext = _vm;
}
}
}

View File

@ -0,0 +1,14 @@
<UserControl x:Class="OperationControl.Views.PreparacaoMapaView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:OperationControl.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#111111">
<Border Margin="8" CornerRadius="8" BorderThickness="1" BorderBrush="#3A3A3A" Background="#1A1A1A">
<controls:MapViewControl x:Name="MapaPreparacao" MbTilesPath="Data/basemap.mbtiles" Latitude="-22.1726572492617" Longitude="-47.3952163870556" Scale="5000" />
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,31 @@
using OperationControl.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace OperationControl.Views
{
/// <summary>
/// Interaction logic for PreparacaoMapaView.xaml
/// </summary>
public partial class PreparacaoMapaView : System.Windows.Controls.UserControl
{
public MapViewControl MapControl => MapaPreparacao;
public PreparacaoMapaView()
{
InitializeComponent();
}
}
}

View File

@ -4,11 +4,16 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OperationControl.Windows"
xmlns:localHelpers="clr-namespace:OperationControl.Helpers"
mc:Ignorable="d"
Title="DockWindow" Height="1025" Width="1920" WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Window.Resources>
<!--<localHelpers:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>-->
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="100"/>
<!-- Topo -->
<RowDefinition Height="*"/>
<!-- Centro -->
@ -17,11 +22,11 @@
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="{Binding LeftPanelWidth}"/>
<!-- Lateral esquerda (voltar) -->
<ColumnDefinition Width="*"/>
<!-- Centro -->
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="{Binding RightPanelWidth}"/>
<!-- Lateral direita (avançar) -->
</Grid.ColumnDefinitions>
@ -47,26 +52,44 @@
<ContentControl Content="{Binding BottomContent}"/>
</Border>
<!-- Lateral esquerda: botão voltar -->
<Grid Grid.Row="1" Grid.Column="0">
<Button Content="⮜"
FontSize="24"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="40" Height="40"
Command="{Binding BackCommand}"
IsEnabled="{Binding CanGoBack}"/>
<!-- Lateral esquerda -->
<Grid Grid.Row="1" Grid.Column="0" Margin="8">
<ContentControl Content="{Binding LeftContent}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{x:Null}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding LeftContent}" Value="{x:Null}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Grid>
<Button Content="⮜"
FontSize="24"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="40"
Height="40"
Command="{Binding BackCommand}"
IsEnabled="{Binding CanGoBack}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
<!-- Lateral direita: botão avançar -->
<Grid Grid.Row="1" Grid.Column="2">
<Button Content="⮞"
FontSize="24"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="40" Height="40"
Command="{Binding NextCommand}"
IsEnabled="{Binding CanGoNext}"/>
</Grid>
<!-- Lateral direita -->
<Border Grid.Row="1" Grid.Column="2"
Margin="8"
BorderThickness="1"
BorderBrush="#404040"
Background="#1C1C1C">
<ContentControl Content="{Binding RightContent}"/>
</Border>
</Grid>
</Window>

View File

@ -20,10 +20,12 @@ namespace OperationControl.Windows
/// </summary>
public partial class DockWindow : Window
{
public DockWindowViewModel _vm;
public DockWindow()
{
InitializeComponent();
DataContext = new DockWindowViewModel();
_vm = new DockWindowViewModel();
DataContext = _vm;
}
}
}