From 4f387a834aa128446d587dc8791f7f080ad613c1 Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Wed, 4 Mar 2026 15:55:52 -0300 Subject: [PATCH] Ajustes pontuais no OPControl e enxugada nos dados de telemetria --- .../Forms/Operacoes/frmOperacaoManual.cs | 6 - .../Forms/Operacoes/frmResultadosOperacao.cs | 6 +- AgroBase/AgroBase/Models/OAKCameraModel.cs | 12 + .../Models/Operacoes/OperacaoModel.cs | 16 +- .../Operacoes/OperacaoParametrosModel.cs | 149 ++++++++++- .../Controls/ManualControlPad.xaml.cs | 1 + .../OperationControl/Helpers/RelayCommand.cs | 28 +++ AgroBase/OperationControl/Models/AppShell.cs | 4 +- .../OperationControl.csproj.user | 18 ++ .../OperationControl/Services/GpsService.cs | 80 ++++-- .../ViewModels/ConfigConexaoViewModel.cs | 232 ++++++++++++++++++ .../ViewModels/DockWindowViewModel.cs | 118 +++++++++ .../ViewModels/NetworkConfigViewModel.cs | 203 +++++++++++++++ .../Views/ConfigConexaoView.xaml | 107 ++++++++ .../Views/ConfigConexaoView.xaml.cs | 38 +++ .../OperationControl/Windows/DockWindow.xaml | 72 ++++++ .../Windows/DockWindow.xaml.cs | 29 +++ .../Windows/MainWindow.xaml.cs | 41 ++-- .../Windows/NetworkConfigWindow.xaml | 86 +++++++ .../Windows/NetworkConfigWindow.xaml.cs | 25 ++ 20 files changed, 1206 insertions(+), 65 deletions(-) create mode 100644 AgroBase/OperationControl/Helpers/RelayCommand.cs create mode 100644 AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs create mode 100644 AgroBase/OperationControl/ViewModels/NetworkConfigViewModel.cs create mode 100644 AgroBase/OperationControl/Views/ConfigConexaoView.xaml create mode 100644 AgroBase/OperationControl/Views/ConfigConexaoView.xaml.cs create mode 100644 AgroBase/OperationControl/Windows/DockWindow.xaml create mode 100644 AgroBase/OperationControl/Windows/DockWindow.xaml.cs create mode 100644 AgroBase/OperationControl/Windows/NetworkConfigWindow.xaml create mode 100644 AgroBase/OperationControl/Windows/NetworkConfigWindow.xaml.cs diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs b/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs index f0ff6af2d..7b8bea2b2 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmOperacaoManual.cs @@ -499,9 +499,6 @@ namespace AgroBase.Forms.Operacoes gridDados.Rows.Add("Roll", "", ""); gridDados.Rows.Add("Pitch", "", ""); gridDados.Rows.Add("Yaw", "", ""); - gridDados.Rows.Add("Temperatura", "", ""); - gridDados.Rows.Add("Pressao", "", ""); - gridDados.Rows.Add("Altitude", "", ""); gridDados.Rows.Add("Temperatura ET", "", ""); gridDados.Rows.Add("Temperatura EF", "", ""); gridDados.Rows.Add("Temperatura DT", "", ""); @@ -558,9 +555,6 @@ namespace AgroBase.Forms.Operacoes gridDados.Rows[37].Cells[1].Value = (_Sensoriamento.IMU.InclinacaoLateral).ToString("0.00"); gridDados.Rows[38].Cells[1].Value = (_Sensoriamento.IMU.InclinacaoFrontal).ToString("0.00"); gridDados.Rows[39].Cells[1].Value = (_Sensoriamento.IMU.Rotacao).ToString("0.00"); - gridDados.Rows[40].Cells[1].Value = (_Sensoriamento.IMU.Temperatura).ToString("0.00"); - gridDados.Rows[41].Cells[1].Value = (_Sensoriamento.IMU.Pressao).ToString("0.00"); - gridDados.Rows[42].Cells[1].Value = (_Sensoriamento.IMU.Altitude).ToString("0.00"); } #endregion diff --git a/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs b/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs index 1168fbd5e..0e4d85edb 100644 --- a/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs +++ b/AgroBase/AgroBase/Forms/Operacoes/frmResultadosOperacao.cs @@ -1444,9 +1444,9 @@ namespace AgroBase.Forms.Operacoes pnlOrientacaoMagnetometro.Invalidate(); pnlInclinacao.Invalidate(); visualizador3D.AtualizarAngulos(LogImu.InclinacaoFrontal, LogImu.InclinacaoLateral, -LogsOperacao[idxMomentoAtual].Gps.OrientacaoReal); - txtTemperatura.Text = LogImu.Temperatura.ToString("0.00"); - txtAltitude.Text = LogImu.Altitude.ToString("0.00"); - txtPressao.Text = LogImu.Pressao + " Pa (" + (LogImu.Pressao / 101300.0).ToString("0.00") + " atm)"; + //txtTemperatura.Text = LogImu.Temperatura.ToString("0.00"); + //txtAltitude.Text = LogImu.Altitude.ToString("0.00"); + //txtPressao.Text = LogImu.Pressao + " Pa (" + (LogImu.Pressao / 101300.0).ToString("0.00") + " atm)"; } var LogSen = LogsSensoriamento.FirstOrDefault(x => x.Momento == MomentoAtual); diff --git a/AgroBase/AgroBase/Models/OAKCameraModel.cs b/AgroBase/AgroBase/Models/OAKCameraModel.cs index ddef35f80..af7f0edcc 100644 --- a/AgroBase/AgroBase/Models/OAKCameraModel.cs +++ b/AgroBase/AgroBase/Models/OAKCameraModel.cs @@ -166,4 +166,16 @@ namespace AgroBase.Models } } + public class OAKImuModel + { + public double timestamp { get; set; } + public double roll { get; set; } + public double pitch { get; set; } + public double yaw { get; set; } + public double yaw_fixed { get; set; } + public double vel_mps { get; set; } + public double latencia { get; set; } + public double frequencia { get; set; } + } + } diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 60eb2de39..14a327b49 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -1006,6 +1006,7 @@ namespace AgroBase.Models public async Task IniciarOperacao() { + Variaveis.OperacaoEmAndamento.Sensoriamento.UltimoRegistroLog = DateTime.Now; tmrLogs?.Start(); Iniciado = true; @@ -1695,6 +1696,7 @@ namespace AgroBase.Models if (topico != null && !string.IsNullOrEmpty(msg)) { await Variaveis.MqttServiceBase.PublishAsync(topico, msg); + Variaveis.OperacaoEmAndamento.Parametros.DadosLeitura.UltimoEnvioLog = DateTime.Now; } } @@ -1733,6 +1735,7 @@ namespace AgroBase.Models OperacaoSensoriamentoLogModel logGeral = Variaveis.OperacaoEmAndamento.Sensoriamento.Clone(); logGeral.Momento = Agora; SalvarLog("operacao", logGeral, Fim); + Variaveis.OperacaoEmAndamento.Sensoriamento.UltimoRegistroLog = DateTime.Now; Variaveis.OperacaoEmAndamento.Controle.TiposControle.ForEach(x => x.Comandos.Clear()); Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.ForEach(Modulo => @@ -2289,6 +2292,7 @@ namespace AgroBase.Models public Dictionary> ModsCalibragem { get; set; } public bool CanSerialAtivado { get; set; } public List Logs { get; set; } + public DateTime UltimoRegistroLog { get; set; } = DateTime.MinValue; public CoolerControlDataModel CoolerControl { get; set; } public void InserirLog(T_Code dispositivo, StatusModulo status, double saude, string mensagem, string id = null, string condicoes = null) @@ -2338,7 +2342,8 @@ namespace AgroBase.Models _Reservatorio?.AtualizarConsumo(_DispMvd?.Dados?.DistanciaPercorridaTotal ?? 0, Variaveis.OperacaoEmAndamento.TempoDecorridoSegs); _Trajetoria?.AtualizarDadosAutonomiaCorredor(); - var sIMU = LivoxManagerProcess.DadosLeitura.imu; + //var sIMU = LivoxManagerProcess.DadosLeitura.imu; + var sIMU = JsonConvert.DeserializeObject(RedisService.Get(RedisService.ModKey(T_Code.Imu))); var dadosIMU = sIMU != null ? new OperacaoSensoriamentoLogImuModel() { Iniciado = sIMU.timestamp > 0 && HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == T_Code.Imu)?.status != StatusModulo.Desconectado, @@ -2565,7 +2570,7 @@ namespace AgroBase.Models ModulosSaude = HealthWorkerService.ModulosSaude.ToList(), DispositivosMapeados = new List(SerialService.DispositivosMapeados), ModsCalibragem = new Dictionary>(Variaveis.OperacaoEmAndamento.ModsCalibragem), - Logs = new List(Logs ?? new List()), + Logs = Logs != null ? Logs.Where(x => x.Momento >= UltimoRegistroLog).ToList() : new List(), CoolerControl = dadosCooler }; @@ -2616,6 +2621,7 @@ namespace AgroBase.Models DispositivosMapeados = new List(DispositivosMapeados), ModsCalibragem = new Dictionary>(ModsCalibragem), Logs = new List(Logs ?? new List()), + UltimoRegistroLog = UltimoRegistroLog, CoolerControl = CoolerControl?.Clone(), }; } @@ -2629,9 +2635,6 @@ namespace AgroBase.Models public double InclinacaoFrontal { get; set; } public double Rotacao { get; set; } public double RotacaoCorrigida { get; set; } - public double Temperatura { get; set; } - public double Pressao { get; set; } - public double Altitude { get; set; } public OperacaoSensoriamentoLogImuModel Clone() { @@ -2642,9 +2645,6 @@ namespace AgroBase.Models InclinacaoFrontal = InclinacaoFrontal, Rotacao = Rotacao, RotacaoCorrigida = RotacaoCorrigida, - Temperatura = Temperatura, - Pressao = Pressao, - Altitude = Altitude }; } } diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs index aa711b65e..0c7d02ed2 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoParametrosModel.cs @@ -77,8 +77,8 @@ namespace AgroBase.Models.Operacoes public StatusModulo StatusRover { get; set; } public bool CanSerial { get; set; } public OperacaoParametrosDadosOperacaoModel Operacao { get; set; } - public OperacaoSensoriamentoLogTrajetoriaModel Trajetoria { get; set; } - public OperacaoControleModel Controle { get; set; } + public OperacaoParametrosDadosTrajetoriaModel Trajetoria { get; set; } + public OperacaoParametrosDadosControleModel Controle { get; set; } public List ModulosSaude { get; set; } public List DispositivosMapeados { get; set; } public OperacaoSensoriamentoLogImuModel Imu { get; set; } @@ -91,6 +91,7 @@ namespace AgroBase.Models.Operacoes public OperacaoParametrosDadosGNSSModel Gnss { get; set; } public PerformanceModel PerformanceNcp { get; set; } public LivoxModel LivoxLidar { get; set; } + public DateTime UltimoEnvioLog { get; set; } = DateTime.MinValue; public List Logs { get; set; } public void Atualizar() @@ -122,14 +123,57 @@ namespace AgroBase.Models.Operacoes ModulosMandatorios = new List(op.Parametros.ModulosMandatorios ?? new List()), ParametrosMandatorios = new List(op.Parametros.ParametrosMandatorios ?? new List()) }; - Trajetoria = sen?.Trajetoria?.Clone(); - Controle = op.Controle?.Clone(); + var t = sen?.Trajetoria; + Trajetoria = t != null ? new OperacaoParametrosDadosTrajetoriaModel() + { + AutonomiaCorredor = new OperacaoParametrosDadosTrajetoriaAutonomiaCorredorModel() + { + BateriaSuficiente = t.AutonomiaCooredor.BateriaSuficiente, + DistanciaCorredor_m = t.AutonomiaCooredor.DistanciaSeguraBateria_m, + DistanciaSeguraBateria_m = t.AutonomiaCooredor.DistanciaSeguraBateria_m, + DistanciaSeguraHerbicida_m = t.AutonomiaCooredor.DistanciaSeguraBateria_m, + HerbicidaSuficiente = t.AutonomiaCooredor.HerbicidaSuficiente, + Liberado = t.AutonomiaCooredor.Liberado, + Motivo = t.AutonomiaCooredor.Motivo, + Status = t.AutonomiaCooredor.Status + }, + AnguloCaminho = t.AnguloCaminho, + CorredorAtualDentro = t.CorredorAtual.Dentro, + CorredorAtualDistanciaPercorrida = t.CorredorAtual.DistanciaPercorridaCorredor, + CorredorAtualDistanciaTotal = t.CorredorAtual.DistanciaTotal, + CorredorAtualIdx = t.CorredorAtual.Idx, + DistanciaDireita = t.DistanciaDireita, + DistanciaEsquerda = t.DistanciaEsquerda, + DistanciaPercorrida = t.DistanciaPercorrida, + DistanciaTotal = t.DistanciaTotal, + NaMargemDoCorredor = t.NaMargemDoCorredor, + PercentualOperacao = t.PercentualOperacao, + PercentualRuaAtual = t.PercentualRuaAtual, + PontoAtualDistanciaAtual = t.PontoAtual.DistanciaAtual, + PontoAtualIdx = t.PontoAtual.idxPonto, + ProximoPontoAproximando = t.ProximoPonto.Aproximando, + ProximoPontoDistanciaAtual = t.ProximoPonto.DistanciaAtual, + ProximoPontoIdx = t.ProximoPonto.idxPonto, + QtdPontos = t.QtdPontos, + StatusCarro = t.StatusCarro, + TempoEstimadoOperacao = t.TempoEstimadoOperacao + } : new OperacaoParametrosDadosTrajetoriaModel(); + var c = op.Controle?.Clone(); + Controle = c != null ? new OperacaoParametrosDadosControleModel() + { + Angulo = c.Angulo, + AlturaBarra = c.AlturaBarra, + PercentualVelocidadeSPKmh = c.PercentualVelocidadeSPKmh, + TipoMovimento = c.TipoMovimento, + SimulacaoMPC = c.SimulacaoMPC, + } : new OperacaoParametrosDadosControleModel(); ModulosSaude = new List(sen?.ModulosSaude ?? new List()); DispositivosMapeados = new List(sen?.DispositivosMapeados ?? new List()); Imu = sen?.IMU?.Clone(); Atuador = new OperacaoParametrosDadosAtuadorModel() { Iniciado = dAtu?.Conectado ?? false, + Latencia = dAtu?.DadosLeitura?.LatenciaLoop ?? 0, AnguloBarraAltura = sen?.Atuador?.AnguloBarraAltura ?? 0, AnguloBarraDireita = sen?.Atuador?.AnguloBarraDireita ?? 0, @@ -208,6 +252,8 @@ namespace AgroBase.Models.Operacoes Sensoriamento = new OperacaoParametrosDadosSensoriamentoModel() { Iniciado = dSen?.Conectado ?? false, + Latencia = dSen?.DadosLeitura?.LatenciaLoop ?? 0, + Sensores = dSen?.Sensores?.Select(x => new OperacaoParametrosDadosSensorModel() { Componente = x.Componente, @@ -322,7 +368,8 @@ namespace AgroBase.Models.Operacoes }; PerformanceNcp = sen?.DadosPerformance?.Clone(); LivoxLidar = sen?.LivoxLidar?.Clone(); - Logs = new List(sen?.Logs ?? new List()); + var l = sen?.Logs; + Logs = l != null ? l.Where(x => x.Momento >= UltimoEnvioLog).ToList() : new List(); } public OperacaoParametrosDadosModel Clone() @@ -347,7 +394,8 @@ namespace AgroBase.Models.Operacoes Gnss = Gnss, PerformanceNcp = PerformanceNcp?.Clone(), LivoxLidar = LivoxLidar?.Clone(), - Logs = new List(Logs ?? new List()) + Logs = new List(Logs ?? new List()), + UltimoEnvioLog = UltimoEnvioLog }; } @@ -370,9 +418,97 @@ namespace AgroBase.Models.Operacoes public List ParametrosMandatorios { get; set; } } + public class OperacaoParametrosDadosTrajetoriaModel + { + public StatusCarroMapa StatusCarro { get; set; } + public int QtdPontos { get; set; } + public double AnguloCaminho { get; set; } + public bool NaMargemDoCorredor { get; set; } + public OperacaoParametrosDadosTrajetoriaAutonomiaCorredorModel AutonomiaCorredor { get; set; } + public int ProximoPontoIdx { get; set; } + public bool ProximoPontoAproximando { get; set; } + public double ProximoPontoDistanciaAtual { get; set; } + public int PontoAtualIdx { get; set; } + public double PontoAtualDistanciaAtual { get; set; } + public double DistanciaEsquerda { get; set; } + public double DistanciaDireita { get; set; } + public double PercentualRuaAtual { get; set; } + public double PercentualOperacao { get; set; } + public double DistanciaPercorrida { get; set; } + public double DistanciaTotal { get; set; } + public string TempoEstimadoOperacao { get; set; } + public bool CorredorAtualDentro { get; set; } + public double CorredorAtualIdx { get; set; } + public double CorredorAtualDistanciaTotal { get; set; } + public double CorredorAtualDistanciaPercorrida { get; set; } + + public OperacaoParametrosDadosTrajetoriaModel Clone() + { + return new OperacaoParametrosDadosTrajetoriaModel() + { + TempoEstimadoOperacao = TempoEstimadoOperacao, + StatusCarro = StatusCarro, + QtdPontos = QtdPontos, + ProximoPontoIdx = ProximoPontoIdx, + ProximoPontoDistanciaAtual = ProximoPontoDistanciaAtual, + AnguloCaminho = AnguloCaminho, + AutonomiaCorredor = AutonomiaCorredor, + CorredorAtualDentro = CorredorAtualDentro, + CorredorAtualDistanciaPercorrida = CorredorAtualDistanciaPercorrida, + CorredorAtualDistanciaTotal = CorredorAtualDistanciaTotal, + CorredorAtualIdx = CorredorAtualIdx, + DistanciaDireita = DistanciaDireita, + DistanciaEsquerda = DistanciaEsquerda, + DistanciaPercorrida = DistanciaPercorrida, + DistanciaTotal = DistanciaTotal, + NaMargemDoCorredor = NaMargemDoCorredor, + PercentualOperacao = PercentualOperacao, + PercentualRuaAtual = PercentualRuaAtual, + PontoAtualDistanciaAtual = PontoAtualDistanciaAtual, + PontoAtualIdx = PontoAtualIdx, + ProximoPontoAproximando = ProximoPontoAproximando, + }; + } + } + + public class OperacaoParametrosDadosTrajetoriaAutonomiaCorredorModel + { + public AutonomiaCorredorStatus Status { get; set; } + public double DistanciaCorredor_m { get; set; } + public double DistanciaSeguraBateria_m { get; set; } + public double DistanciaSeguraHerbicida_m { get; set; } + public bool BateriaSuficiente { get; set; } + public bool HerbicidaSuficiente { get; set; } + public bool Liberado { get; set; } + public string Motivo { get; set; } + + } + + public class OperacaoParametrosDadosControleModel + { + public double AlturaBarra { get; set; } + public double Angulo { get; set; } + public TipoMovimentoDirecional TipoMovimento { get; set; } + public double PercentualVelocidadeSPKmh { get; set; } + public List SimulacaoMPC { get; set; } = new List(); + + public OperacaoParametrosDadosControleModel Clone() + { + return new OperacaoParametrosDadosControleModel() + { + AlturaBarra = AlturaBarra, + Angulo = Angulo, + PercentualVelocidadeSPKmh = PercentualVelocidadeSPKmh, + TipoMovimento = TipoMovimento, + SimulacaoMPC = SimulacaoMPC + }; + } + } + public class OperacaoParametrosDadosAtuadorModel { public bool Iniciado { get; set; } + public double Latencia { get; set; } public double CapacidadeReservatorio { get; set; } @@ -510,6 +646,7 @@ namespace AgroBase.Models.Operacoes public class OperacaoParametrosDadosSensoriamentoModel { public bool Iniciado { get; set; } + public double Latencia { get; set; } public List Sinaleiros { get; set; } public List Sensores { get; set; } diff --git a/AgroBase/OperationControl/Controls/ManualControlPad.xaml.cs b/AgroBase/OperationControl/Controls/ManualControlPad.xaml.cs index 2bf525504..5b303eb20 100644 --- a/AgroBase/OperationControl/Controls/ManualControlPad.xaml.cs +++ b/AgroBase/OperationControl/Controls/ManualControlPad.xaml.cs @@ -113,6 +113,7 @@ namespace OperationControl.Controls if (VariaveisControleOperacao.RoverEmFoco != null) { VariaveisControleOperacao.RoverEmFoco!.Controle.MovimentoAutomatico = !manual_on; + VariaveisControleOperacao.RoverEmFoco!.Controle.DirecionalAutomatico = !manual_on; VariaveisControleOperacao.EnviarParametrosOperacao(new AgroBase.Models.Operacoes.OperacaoParametrosModel() { Controle = VariaveisControleOperacao.RoverEmFoco?.Controle diff --git a/AgroBase/OperationControl/Helpers/RelayCommand.cs b/AgroBase/OperationControl/Helpers/RelayCommand.cs new file mode 100644 index 000000000..3c92b1e0a --- /dev/null +++ b/AgroBase/OperationControl/Helpers/RelayCommand.cs @@ -0,0 +1,28 @@ +// Helpers/RelayCommand.cs +using System; +using System.Windows.Input; + +namespace OperationControl.Helpers +{ + public class RelayCommand : ICommand + { + private readonly Action _execute; + private readonly Predicate _canExecute; + + public RelayCommand(Action execute, Predicate canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + + public bool CanExecute(object parameter) => _canExecute?.Invoke(parameter) ?? true; + + public void Execute(object parameter) => _execute(parameter); + + public event EventHandler CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + } +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Models/AppShell.cs b/AgroBase/OperationControl/Models/AppShell.cs index ee2ba7bb1..7ed704196 100644 --- a/AgroBase/OperationControl/Models/AppShell.cs +++ b/AgroBase/OperationControl/Models/AppShell.cs @@ -13,6 +13,7 @@ namespace OperationControl.Models private bool tmrComunicacaoTicking = false; public MainWindow Main { get; private set; } + public DockWindow Dock { get; private set; } public void Inicializar() { @@ -29,7 +30,8 @@ namespace OperationControl.Models { // Aqui você decide qual janela é a inicial de fato Main = new MainWindow(); - Main.Show(); + Dock = new DockWindow(); + Dock.Show(); } private void tmrComunicacao_Elapsed() diff --git a/AgroBase/OperationControl/OperationControl.csproj.user b/AgroBase/OperationControl/OperationControl.csproj.user index 623d7bc0a..0ef63b7ba 100644 --- a/AgroBase/OperationControl/OperationControl.csproj.user +++ b/AgroBase/OperationControl/OperationControl.csproj.user @@ -43,6 +43,15 @@ Code + + Code + + + Code + + + Code + @@ -81,8 +90,17 @@ Designer + + Designer + + + Designer + Designer + + Designer + \ No newline at end of file diff --git a/AgroBase/OperationControl/Services/GpsService.cs b/AgroBase/OperationControl/Services/GpsService.cs index b4f9694fc..d61ad11b2 100644 --- a/AgroBase/OperationControl/Services/GpsService.cs +++ b/AgroBase/OperationControl/Services/GpsService.cs @@ -75,7 +75,7 @@ namespace OperationControl.Services bool achou = await TryConnectOnPortAsync(portName, timeoutPorPortaMs, ct); if (achou) { - await ConfigurarModulo(); + await ConfigurarModulo(false); return true; } } @@ -240,12 +240,14 @@ namespace OperationControl.Services private List _rtcmMsg = new(); private int _rtcmTotalBytes = -1; - public async Task ConfigurarModulo() + public async Task ConfigurarModulo(bool fixar) { Models.Variaveis.MostrarLog("Iniciando configuração do módulo GPS..."); LeverArm = new GeoLeverArm(VariaveisControleOperacao.LeverArmFrontal, VariaveisControleOperacao.LeverArmLateral); BaseFix = new BaseFixService(PortaGps, this); - bool sucesso = await BaseFix.FixarBaseViaNtripAsync( + if (fixar) + { + bool sucesso = await BaseFix.FixarBaseViaNtripAsync( portaUsb: "com3", portaSaida: "com3", portaEntrada: "com3", @@ -262,14 +264,20 @@ namespace OperationControl.Services }, segsFixEstavel: TempoSurveryIn ); - if (!sucesso) - { - Models.Variaveis.MostrarLog("Configurando módulo como base survery in..."); - BaseFix.fimProcesso = null; - BaseFix.inicioProcesso = DateTime.UtcNow; - await ConfigurarModuloBase(tempo_fixacao: TempoSurveryIn, porta_usb: "com3", porta_saida: "com3"); - BaseFix.inicioFix = DateTime.UtcNow; + if (!sucesso) + { + Models.Variaveis.MostrarLog("Configurando módulo como base survery in..."); + BaseFix.fimProcesso = null; + BaseFix.inicioProcesso = DateTime.UtcNow; + await ConfigurarModuloBase(tempo_fixacao: TempoSurveryIn, porta_usb: "com3", porta_saida: "com3"); + BaseFix.inicioFix = DateTime.UtcNow; + } } + else + { + await BaseFix.ConfigurarComoRoverParadoAsync(portaUsb: "com3", portaEntrada: "com3"); + } + } private async Task ConfigurarModuloBase(string porta_usb = "com3", string porta_saida = "com2", int tempo_fixacao = 60) @@ -310,8 +318,16 @@ namespace OperationControl.Services foreach (string cmd in comandos) { - byte[] bytes = Encoding.ASCII.GetBytes(cmd); - PortaGps.Write(bytes, 0, bytes.Length); + try + { + byte[] bytes = Encoding.ASCII.GetBytes(cmd); + PortaGps.Write(bytes, 0, bytes.Length); + } + catch (Exception ex) + { + Models.Variaveis.MostrarLog($"Erro ao enviar comando para o GNSS: {ex.Message}"); + } + await Task.Delay(500); } } @@ -1067,10 +1083,17 @@ namespace OperationControl.Services int bytesRead; while (CorrecaoRTK_Ntrip && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) { - if (PortaGps?.IsOpen ?? false) + try { - PortaGps.Write(buffer, 0, bytesRead); // Envia os dados RTCM para o GPS - //Console.WriteLine($"Enviando {bytesRead} bytes de correção RTCM para o GPS"); + if (PortaGps?.IsOpen ?? false) + { + PortaGps.Write(buffer, 0, bytesRead); // Envia os dados RTCM para o GPS + //Console.WriteLine($"Enviando {bytesRead} bytes de correção RTCM para o GPS"); + } + } + catch (Exception ex) + { + Models.Variaveis.MostrarLog($"Erro ao enviar correção RTCM para o módulo GNSS: {ex.Message}"); } } } @@ -1211,13 +1234,13 @@ namespace OperationControl.Services try { - if (!gpsService.Ntrip_Conectado) - { - Models.Variaveis.MostrarLog("Ntrip não conectado."); - CorrecaoAbsoluta = false; - FixLiberado = false; - return CorrecaoAbsoluta; - } + //if (!gpsService.Ntrip_Conectado) + //{ + // Models.Variaveis.MostrarLog("Ntrip não conectado."); + // CorrecaoAbsoluta = false; + // FixLiberado = false; + // return CorrecaoAbsoluta; + //} // 2) Esperar FIX sustentado e coletar GNGGA var amostras = await EsperarFixEAmostrarAsync(); @@ -1251,7 +1274,7 @@ namespace OperationControl.Services } // ===== 1.1 Rover parado + NMEA + limpar logs ===== - private async Task ConfigurarComoRoverParadoAsync(string portaUsb, string portaEntrada) + public async Task ConfigurarComoRoverParadoAsync(string portaUsb, string portaEntrada) { Models.Variaveis.MostrarLog("Configurando base como modo rover parado..."); string freq = "1.0"; @@ -1278,8 +1301,15 @@ namespace OperationControl.Services await Task.Delay(1000); foreach (var c in cmds) { - var b = Encoding.ASCII.GetBytes(c); - _Porta.Write(b, 0, b.Length); + try + { + var b = Encoding.ASCII.GetBytes(c); + _Porta.Write(b, 0, b.Length); + } + catch (Exception ex) + { + Models.Variaveis.MostrarLog($"Erro ao enviar comando de configuração GNSS: {ex.Message}"); + } await Task.Delay(250); } } diff --git a/AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs b/AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs new file mode 100644 index 000000000..8188526d7 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/ConfigConexaoViewModel.cs @@ -0,0 +1,232 @@ +// ViewModels/ConfigConexaoViewModel.cs +using System; +using System.ComponentModel; +using System.Windows.Input; +using System.Windows.Threading; +using OperationControl.Helpers; +using OperationControl.Models; + +namespace OperationControl.ViewModels +{ + public class ConfigConexaoViewModel : INotifyPropertyChanged + { + public event Action SolicitarConfigRede; + private readonly DispatcherTimer _timerGnss; + + public ConfigConexaoViewModel() + { + AbrirConfigRedeCommand = new RelayCommand(_ => AbrirConfigRede()); + AbrirDetalhesGnssCommand = new RelayCommand(_ => AbrirDetalhesGnss()); + + // aqui você já pode chamar um serviço real depois + CarregarStatusInicial(); + + _timerGnss = new DispatcherTimer(); + _timerGnss.Interval = TimeSpan.FromMilliseconds(800); // 0.8s fica responsivo + _timerGnss.Tick += (_, __) => AtualizarStatusGnss(); + _timerGnss.Start(); + } + + // ====== PROPRIEDADES DE REDE ====== + + private bool _redeOk; + public bool RedeOk + { + get => _redeOk; + set + { + if (_redeOk != value) + { + _redeOk = value; + OnPropertyChanged(nameof(RedeOk)); + OnPropertyChanged(nameof(StatusRedeTexto)); + OnPropertyChanged(nameof(CorStatusRede)); + OnPropertyChanged(nameof(CorBordaRede)); + OnPropertyChanged(nameof(TudoOk)); + OnTudoOkChanged(); + } + } + } + + private string _detalhesRede; + public string DetalhesRede + { + get => _detalhesRede; + set { _detalhesRede = value; OnPropertyChanged(nameof(DetalhesRede)); } + } + + public string StatusRedeTexto => RedeOk ? "Rede configurada" : "Rede com problema"; + + public Brush CorStatusRede => RedeOk ? Brushes.LightGreen : Brushes.OrangeRed; + + public Brush CorBordaRede => RedeOk ? Brushes.LightGreen : Brushes.OrangeRed; + + // ====== PROPRIEDADES DE GNSS ====== + + private bool _gnssOk; + public bool GnssOk + { + get => _gnssOk; + set + { + if (_gnssOk != value) + { + _gnssOk = value; + OnPropertyChanged(nameof(GnssOk)); + OnPropertyChanged(nameof(StatusGnssTexto)); + OnPropertyChanged(nameof(CorStatusGnss)); + OnPropertyChanged(nameof(CorBordaGnss)); + OnPropertyChanged(nameof(TudoOk)); + OnTudoOkChanged(); + } + } + } + + private string _detalhesGnss; + public string DetalhesGnss + { + get => _detalhesGnss; + set { _detalhesGnss = value; OnPropertyChanged(nameof(DetalhesGnss)); } + } + + public string StatusGnssTexto => GnssOk ? "GNSS conectado" : "GNSS não detectado"; + + public Brush CorStatusGnss => GnssOk ? Brushes.LightGreen : Brushes.OrangeRed; + + public Brush CorBordaGnss => GnssOk ? Brushes.LightGreen : Brushes.OrangeRed; + + // ====== MENSAGEM GERAL ====== + + private string _mensagemGeral; + public string MensagemGeral + { + get => _mensagemGeral; + set { _mensagemGeral = value; OnPropertyChanged(nameof(MensagemGeral)); } + } + + // ====== COMANDOS ====== + + public ICommand AbrirConfigRedeCommand { get; } + public ICommand AbrirDetalhesGnssCommand { get; } + + private void AbrirConfigRede() + { + // depois você pode abrir uma janelinha de config + //MessageBox.Show("Aqui vai abrir a tela de configuração de rede."); + SolicitarConfigRede?.Invoke(); + } + + private void AbrirDetalhesGnss() + { + MessageBox.Show("Aqui vai abrir uma tela com detalhes do GNSS/UM982."); + } + + // ====== STATUS GERAL PARA AVANÇAR ====== + + public bool TudoOk => RedeOk && GnssOk; + + // Evento para o DockWindow saber quando ficou tudo ok + public event Action TudoOkChanged; + + private void OnTudoOkChanged() + { + TudoOkChanged?.Invoke(TudoOk); + } + + // ====== STATUS INICIAL (fake por enquanto) ====== + + public void CarregarStatusInicial() + { + // aqui você pluga de verdade nos seus serviços: + // var statusRede = NetworkService.Get(); + // var statusGnss = GnssService.Get(); + + AtualizarStatusRede(); + AtualizarStatusGnss(); + } + + + + private void AtualizarStatusGnss() + { + try + { + // Exemplo de checks (troca pelo teu serviço): + // bool conectado = GnssService.Um982Conectado(); + // string porta = GnssService.PortaAtual(); + // string fix = GnssService.FixMode(); // "No Fix", "3D", "RTK Float", "RTK Fixed" + // double? hdop = GnssService.Hdop(); + + bool conectado = Variaveis.GpsService?.IsConnected ?? false; // <-- seu método real aqui + //conectado = true; + + 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; + } + + // Se conectou: + GnssOk = true; + + // 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."; + } + } + + public void AtualizarStatusRede() + { + try + { + // 1) Escolhe a interface “ativa” ou a que você usa pra ponte IP + // Se você já tem isso fixo, melhor ainda: use sempre a mesma. + var interfaces = EthernetService.ObterNomesInterfaces().Where(x => x.Contains("Ativa")).Select(x => x.Split('(')[0].Trim()).ToList(); + var iface = interfaces[0]; // <-- se tiver + // ou se você já sabe o nome: var iface = "Ethernet 2"; + + var ip = EthernetService.ObterIpAtual(iface); + var sub = EthernetService.ObterSubnetMaskAtual(iface); + var gw = EthernetService.ObterIpGateway(iface); + + DetalhesRede = $"{iface}\nIP: {ip}\nMáscara: {sub}\nGateway: {gw}"; + + // 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); + } + catch (Exception ex) + { + RedeOk = false; + DetalhesRede = "Não foi possível ler a configuração.\n" + ex.Message; + } + } + + + // ====== INotifyPropertyChanged ====== + + public event PropertyChangedEventHandler PropertyChanged; + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + + public void Dispose() + { + _timerGnss?.Stop(); + } + } +} \ No newline at end of file diff --git a/AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs new file mode 100644 index 000000000..d3c9316f4 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/DockWindowViewModel.cs @@ -0,0 +1,118 @@ +using System.ComponentModel; +using System.Windows.Input; +using System.Windows; +using OperationControl.Views; +using OperationControl.Helpers; +using OperationControl.Models; + +namespace OperationControl.ViewModels +{ + public class DockWindowViewModel : INotifyPropertyChanged + { + private readonly ConfigConexaoView _viewConfig; + + public DockWindowViewModel() + { + // 1) Cria a view do passo 1 + _viewConfig = new ConfigConexaoView(); + + // 2) Escuta quando RedeOk && GnssOk mudar + _viewConfig._vm.TudoOkChanged += OnTudoOkChanged; + + // 3) Conteúdo inicial + CenterContent = _viewConfig; + TopContent = null; + BottomContent = null; + + // 4) Commands já usando CanGoBack / CanGoNext + BackCommand = new RelayCommand(_ => GoBack(), _ => CanGoBack); + NextCommand = new RelayCommand(_ => GoNext(), _ => CanGoNext); + + // 5) Estado inicial do Next (caso já venha tudo ok no boot) + ConfigConexaoOk = _viewConfig._vm.TudoOk; + } + + private void OnTudoOkChanged(bool ok) + { + ConfigConexaoOk = ok; + + // avisa bindings + OnPropertyChanged(nameof(CanGoNext)); + + // força o WPF a reavaliar CanExecute dos botões + CommandManager.InvalidateRequerySuggested(); + } + + // ====== ESTADO ====== + private bool _configConexaoOk; + public bool ConfigConexaoOk + { + get => _configConexaoOk; + set + { + if (_configConexaoOk != value) + { + _configConexaoOk = value; + OnPropertyChanged(nameof(ConfigConexaoOk)); + OnPropertyChanged(nameof(CanGoNext)); + } + } + } + + public bool CanGoBack => false; // passo 1 não volta + public bool CanGoNext => ConfigConexaoOk; // libera quando tudo ok + + // ====== CONTEÚDOS ====== + private object _centerContent; + public object CenterContent + { + get => _centerContent; + set { _centerContent = value; OnPropertyChanged(nameof(CenterContent)); } + } + + private object _topContent; + public object TopContent + { + get => _topContent; + set { _topContent = value; OnPropertyChanged(nameof(TopContent)); } + } + + private object _bottomContent; + public object BottomContent + { + get => _bottomContent; + set { _bottomContent = value; OnPropertyChanged(nameof(BottomContent)); } + } + + // ====== COMMANDS ====== + public ICommand BackCommand { get; } + public ICommand NextCommand { get; } + + private void GoBack() + { + // depois você implementa quando tiver passo 2/3 + } + + private void GoNext() + { + if (!CanGoNext) return; + + // Aqui depois você troca para a próxima tela: + // CenterContent = new FixacaoBaseView(); + // e atualiza CanGoBack/CanGoNext conforme o step + + //System.Windows.MessageBox.Show("Avançou! Próximo passo: Fixação da Base (ainda vamos implementar)."); + + ((App)System.Windows.Application.Current).Shell.Dock?.Hide(); + ((App)System.Windows.Application.Current).Shell.Main?.Show(); + } + + // ====== INotifyPropertyChanged ====== + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + } +} \ No newline at end of file diff --git a/AgroBase/OperationControl/ViewModels/NetworkConfigViewModel.cs b/AgroBase/OperationControl/ViewModels/NetworkConfigViewModel.cs new file mode 100644 index 000000000..55426d4a0 --- /dev/null +++ b/AgroBase/OperationControl/ViewModels/NetworkConfigViewModel.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Net; +using System.Windows; +using System.Windows.Input; +using OperationControl.Helpers; + +namespace OperationControl.ViewModels +{ + public class NetworkConfigViewModel : INotifyPropertyChanged + { + public NetworkConfigViewModel() + { + Interfaces = new ObservableCollection(); + + AtualizarCommand = new RelayCommand(_ => Atualizar(), _ => PodeAtualizar); + SalvarCommand = new RelayCommand(async _ => await SalvarAsync(), _ => PodeSalvar); + FecharCommand = new RelayCommand(_ => Fechar()); + + CarregarInterfaces(); + } + + // Lista de interfaces + public ObservableCollection Interfaces { get; } + + private string _interfaceSelecionada; + public string InterfaceSelecionada + { + get => _interfaceSelecionada; + set + { + if (_interfaceSelecionada != value) + { + _interfaceSelecionada = value; + OnPropertyChanged(nameof(InterfaceSelecionada)); + OnPropertyChanged(nameof(PodeAtualizar)); + OnPropertyChanged(nameof(PodeSalvar)); + MensagemStatus = "Clique em Atualizar para ler os dados da interface selecionada."; + } + } + } + + private string _ip; + public string Ip + { + get => _ip; + set { _ip = value; OnPropertyChanged(nameof(Ip)); OnPropertyChanged(nameof(PodeSalvar)); } + } + + private string _subnet; + public string Subnet + { + get => _subnet; + set { _subnet = value; OnPropertyChanged(nameof(Subnet)); OnPropertyChanged(nameof(PodeSalvar)); } + } + + private string _gateway; + public string Gateway + { + get => _gateway; + set { _gateway = value; OnPropertyChanged(nameof(Gateway)); OnPropertyChanged(nameof(PodeSalvar)); } + } + + private string _mensagemStatus; + public string MensagemStatus + { + get => _mensagemStatus; + set { _mensagemStatus = value; OnPropertyChanged(nameof(MensagemStatus)); } + } + + // Comandos + public ICommand AtualizarCommand { get; } + public ICommand SalvarCommand { get; } + public ICommand FecharCommand { get; } + + private bool _isBusy; + public bool IsBusy + { + get => _isBusy; + set + { + _isBusy = value; + OnPropertyChanged(nameof(IsBusy)); + OnPropertyChanged(nameof(PodeSalvar)); + OnPropertyChanged(nameof(PodeAtualizar)); + } + } + + public bool PodeAtualizar => !IsBusy && !string.IsNullOrEmpty(InterfaceSelecionada); + + public bool PodeSalvar => !IsBusy && + !string.IsNullOrEmpty(InterfaceSelecionada) && + !string.IsNullOrWhiteSpace(Ip) && + !string.IsNullOrWhiteSpace(Subnet) && + !string.IsNullOrWhiteSpace(Gateway); + + private bool IpValido(string s) => IPAddress.TryParse(s, out _); + + // Evento para a janela fechar de fora + public event Action RequisitarFechamento; + + private void CarregarInterfaces() + { + Interfaces.Clear(); + + // TODO: trocar pelo seu método real pra listar as interfaces + // Exemplo: + // foreach (var iface in NetworkHelper.ListarInterfaces()) + // Interfaces.Add(iface.NomeExibicao); + + foreach (var i in EthernetService.ObterNomesInterfaces()) + { + if (i.Contains("Ativa")) + Interfaces.Add(i.Split('(')[0].Trim()); + } + + if (Interfaces.Count > 0) + InterfaceSelecionada = Interfaces[0]; + + MensagemStatus = "Selecione a interface e clique em Atualizar para carregar os dados atuais."; + } + + private void Atualizar() + { + if (string.IsNullOrEmpty(InterfaceSelecionada)) + { + MensagemStatus = "Selecione uma interface primeiro."; + return; + } + + try + { + // TODO: aqui você chama SEUS métodos reais + // var config = NetworkHelper.GetConfig(InterfaceSelecionada); + // Ip = config.Ip; + // Subnet = config.Subnet; + // Gateway = config.Gateway; + + // MOCK por enquanto: + Ip = EthernetService.ObterIpAtual(InterfaceSelecionada); + Subnet = EthernetService.ObterSubnetMaskAtual(InterfaceSelecionada); + Gateway = EthernetService.ObterIpGateway(InterfaceSelecionada); + + MensagemStatus = "Configuração atual carregada da interface selecionada."; + } + catch (Exception ex) + { + MensagemStatus = "Erro ao ler configuração da interface: " + ex.Message; + } + } + + private async Task SalvarAsync() + { + if (!PodeSalvar) { MensagemStatus = "Preencha IP, Máscara e Gateway antes de salvar."; return; } + + if (!IpValido(Ip) || !IpValido(Subnet) || !IpValido(Gateway)) + { + MensagemStatus = "IP, máscara ou gateway inválidos. Ex: 192.168.0.50 / 255.255.255.0 / 192.168.0.1"; + return; + } + + try + { + IsBusy = true; + MensagemStatus = "Salvando configuração..."; + + bool sucesso = await EthernetService.DefinirIpFixo(InterfaceSelecionada, Ip, Subnet, Gateway, false); + + if (!sucesso) + { + MensagemStatus = "Falha ao aplicar configuração de rede."; + return; + } + + MensagemStatus = "Configuração aplicada com sucesso."; + System.Windows.MessageBox.Show($"Configuração salva para {InterfaceSelecionada}...", "Rede", MessageBoxButton.OK, MessageBoxImage.Information); + RequisitarFechamento?.Invoke(true); + } + catch (Exception ex) + { + MensagemStatus = "Erro ao salvar configuração de rede: " + ex.Message; + } + finally + { + IsBusy = false; + } + } + + private void Fechar() + { + // fecha sem "sucesso" + RequisitarFechamento?.Invoke(false); + } + + // INotifyPropertyChanged + public event PropertyChangedEventHandler PropertyChanged; + private void OnPropertyChanged(string nome) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome)); + } + } +} \ No newline at end of file diff --git a/AgroBase/OperationControl/Views/ConfigConexaoView.xaml b/AgroBase/OperationControl/Views/ConfigConexaoView.xaml new file mode 100644 index 000000000..0df3098d4 --- /dev/null +++ b/AgroBase/OperationControl/Views/ConfigConexaoView.xaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +