From a6d9a7271a3f138dddbcadf59e493a47ec18192f Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Wed, 25 Mar 2026 07:52:19 -0300 Subject: [PATCH] =?UTF-8?q?metodo=20para=20fixa=C3=A7=C3=A3o=20da=20base?= =?UTF-8?q?=20e=20aplica=C3=A7=C3=A3o=20de=20offset=20global?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperationControl/Services/GpsService.cs | 184 +++++++--------- .../PreparacaoMapaBottomViewModel.cs | 55 +++-- .../ViewModels/Windows/DockWindowViewModel.cs | 41 ++-- .../PreparacaoMapaBottomView.xaml | 202 +++++------------- .../Windows/MainWindow.xaml.cs | 3 +- 5 files changed, 187 insertions(+), 298 deletions(-) diff --git a/AgroBase/OperationControl/Services/GpsService.cs b/AgroBase/OperationControl/Services/GpsService.cs index 9f4fac824..a6fc064fe 100644 --- a/AgroBase/OperationControl/Services/GpsService.cs +++ b/AgroBase/OperationControl/Services/GpsService.cs @@ -9,8 +9,7 @@ using AgroBase.Models; using static AgroBase.Models.Enums; using OperationControl.Models; using Application = System.Windows.Application; -using Mapsui.Animations; -using OperationControl.ViewModels; +using static OperationControl.Services.BaseFixService; namespace OperationControl.Services { @@ -233,7 +232,7 @@ namespace OperationControl.Services private int rtk_timeout = 60; public GPSModel UltimaLeitura = new GPSModel(); public BaseFixService BaseFix; - private GeoLeverArm LeverArm; + public GeoLeverArm LeverArm = new GeoLeverArm(); private readonly StringBuilder _nmeaBuffer = new(); private bool _nmeaInSentence = false; @@ -242,13 +241,14 @@ namespace OperationControl.Services private List _rtcmMsg = new(); private int _rtcmTotalBytes = -1; - public async Task ConfigurarModulo(bool fixar, MetodoFixacaoBase metodo = MetodoFixacaoBase.Ntrip, double? lat = null, double? lon = null, double? alt = null) + public async Task ConfigurarModulo(bool fixar, MetodoFixacaoBase metodo = MetodoFixacaoBase.Ntrip, double? lat = null, double? lon = null, double? alt = null, bool? offset = false) { Models.Variaveis.MostrarLog("Iniciando configuração do módulo GPS..."); - LeverArm = new GeoLeverArm(VariaveisControleOperacao.LeverArmFrontal, VariaveisControleOperacao.LeverArmLateral); - - BaseFix = new BaseFixService(PortaGps, this); + if (!(offset ?? false)) + { + BaseFix = new BaseFixService(PortaGps, this); + } BaseFix.FixLiberado = fixar; string portaUsb = "com3"; @@ -268,101 +268,73 @@ namespace OperationControl.Services bool sucesso = false; string mensagem = ""; + + BaseFix.fimProcesso = null; + BaseFix.inicioProcesso = DateTime.UtcNow; + BaseFix.inicioFix = DateTime.UtcNow; + + Models.Variaveis.MostrarLog($"Aplicando posição fixa da base com o método {metodo}..."); + switch (metodo) { case MetodoFixacaoBase.Ntrip: - { - Models.Variaveis.MostrarLog("Tentando fixação da base via NTRIP..."); - - sucesso = await BaseFix.FixarBaseViaNtripAsync( - portaUsb: portaUsb, - portaSaida: portaSaida, - portaEntrada: portaEntrada, - startNtrip: () => - { - Models.Variaveis.MostrarLog("Iniciando correção NTRIP..."); - CorrecaoRTK_Ntrip = true; - _ = Task.Run(async () => await AplicarCorrecaoRTK_Ntrip()); - return Task.CompletedTask; - }, - stopNtrip: () => - { - Models.Variaveis.MostrarLog("Parando correção NTRIP..."); - CorrecaoRTK_Ntrip = false; - return Task.CompletedTask; - }, - segsFixEstavel: TempoSurveryIn - ); - - if (sucesso) + sucesso = await BaseFix.FixarBaseViaNtripAsync( + portaUsb: portaUsb, + portaSaida: portaSaida, + portaEntrada: portaEntrada, + startNtrip: () => { - mensagem = "Posição fixada utilizando NTRIP com sucesso!"; - } - else + Models.Variaveis.MostrarLog("Iniciando correção NTRIP..."); + CorrecaoRTK_Ntrip = true; + _ = Task.Run(async () => await AplicarCorrecaoRTK_Ntrip()); + return Task.CompletedTask; + }, + stopNtrip: () => { - Models.Variaveis.MostrarLog("Falha na fixação via NTRIP. Iniciando Survey-In..."); - - BaseFix.fimProcesso = null; - BaseFix.inicioProcesso = DateTime.UtcNow; - BaseFix.inicioFix = DateTime.UtcNow; - - await ConfigurarModuloBase( - tempo_fixacao: TempoSurveryIn, - porta_usb: portaUsb, - porta_saida: portaSaida); - - BaseFix.fimProcesso = DateTime.UtcNow; - - sucesso = true; - mensagem = "Posição survery in da base aplicada com sucesso."; - } - - break; - } + Models.Variaveis.MostrarLog("Parando correção NTRIP..."); + CorrecaoRTK_Ntrip = false; + return Task.CompletedTask; + }, + segsFixEstavel: TempoSurveryIn + ); + break; case MetodoFixacaoBase.SurveyIn: - { - Models.Variaveis.MostrarLog("Configurando módulo como base em Survey-In..."); - - BaseFix.fimProcesso = null; - BaseFix.inicioProcesso = DateTime.UtcNow; - BaseFix.inicioFix = DateTime.UtcNow; - - await ConfigurarModuloBase( - tempo_fixacao: TempoSurveryIn, - porta_usb: portaUsb, - porta_saida: portaSaida); - - BaseFix.fimProcesso = DateTime.UtcNow; - - sucesso = true; - mensagem = "Posição survery in da base aplicada com sucesso."; - break; - } + await ConfigurarModuloBase(tempo_fixacao: TempoSurveryIn, porta_usb: portaUsb, porta_saida: portaSaida); + break; case MetodoFixacaoBase.Manual: - { - if (!lat.HasValue || !lon.HasValue || !alt.HasValue) - throw new ArgumentException("Latitude, longitude e altitude elipsoidal são obrigatórias no modo manual."); + if (lat.HasValue && lon.HasValue && alt.HasValue) + await BaseFix.AplicarBaseFixAsync(portaUsb, portaSaida, baseId, lat.Value, lon.Value, alt.Value); + break; + } - Models.Variaveis.MostrarLog("Aplicando posição manual fixa da base..."); + // Aguarda 10 segundos para ver se o UM982 pegou o BaseFix + while (sucesso && UltimaLeitura.QualidadeFix != TiposCorrecaoGPS.BaseFix && BaseFix.inicioFix != null && (DateTime.UtcNow - BaseFix.inicioFix).Value.TotalSeconds < 10) + { + await Task.Delay(1000); + } - UltimaLeitura.Latitude = lat.Value; - UltimaLeitura.Longitude = lon.Value; - AtualizarCoordenadasGPS(); + BaseFix.fimProcesso = DateTime.UtcNow; + BaseFix.FixLiberado = false; - await BaseFix.AplicarBaseFixAsync( - portaUsb, - portaSaida, - baseId, - lat.Value, - lon.Value, - alt.Value); + sucesso = UltimaLeitura.QualidadeFix == TiposCorrecaoGPS.BaseFix; + mensagem = sucesso ? $"Posição da base aplicada com sucesso com o método {metodo}." : $"Falha ao aplicar posição da base com o método {metodo}."; - sucesso = true; - mensagem = "Posição manual da base aplicada com sucesso."; - break; - } + if (sucesso && !(offset ?? false)) + { + BaseFix.LatitudeFix = UltimaLeitura.Latitude; + BaseFix.LongitudeFix = UltimaLeitura.Longitude; + BaseFix.AltitudeElpsoidalFix = UltimaLeitura.AltitudeElipsoidal; + BaseFix.OrientacaoFix = UltimaLeitura.OrientacaoReal; + + if (AppShell.Mock) + { + UltimaLeitura.Latitude = lat.Value; + UltimaLeitura.Longitude = lon.Value; + UltimaLeitura.AltitudeElipsoidal = alt.Value; + AtualizarCoordenadasGPS(); + } } Models.Variaveis.MostrarLog(mensagem); @@ -1304,6 +1276,10 @@ namespace OperationControl.Services public bool CorrecaoAbsoluta = false; public bool CorrecaoEmAndamento = false; public bool FixLiberado = false; + public double LatitudeFix { get; set; } + public double LongitudeFix { get; set; } + public double AltitudeElpsoidalFix { get; set; } + public double OrientacaoFix { get; set; } public void ReiniciarFix() { @@ -1321,7 +1297,6 @@ namespace OperationControl.Services return false; CorrecaoEmAndamento = true; - fimProcesso = null; segundosFixEstavel = segsFixEstavel; maxJanelaSegundos = maxJanelaSegs; @@ -1349,26 +1324,22 @@ namespace OperationControl.Services { Models.Variaveis.MostrarLog("Poucas amostras de RTK FIX coletadas. Tente aumentar o tempo ou verificar sinais."); CorrecaoAbsoluta = false; - FixLiberado = false; return CorrecaoAbsoluta; } // 3) Filtro robusto (MAD) + média final - var (lat, lon, h, nAmostras) = FiltrarEAgrupar(amostras, madK); + var (lat, lon, h, heading, nAmostras) = FiltrarEAgrupar(amostras, madK); // 4) Alternar para base FIX + perfil RTCM await AplicarBaseFixAsync(portaUsb, portaSaida, baseId, lat, lon, h); - Models.Variaveis.MostrarLog($"[BASE/FIX] Coordenadas aplicadas (n={nAmostras}):"); - Models.Variaveis.MostrarLog($" lat = {lat:0.000000000}, lon = {lon:0.000000000}, h = {h:0.000}"); + Models.Variaveis.MostrarLog($"[BASE/FIX] Coordenadas aplicadas (n={nAmostras}): lat = {lat:0.000000000}, lon = {lon:0.000000000}, h = {h:0.000}"); CorrecaoAbsoluta = true; - FixLiberado = false; return CorrecaoAbsoluta; } finally { if (stopNtrip != null) await stopNtrip(); - fimProcesso = DateTime.UtcNow; CorrecaoEmAndamento = false; } } @@ -1499,6 +1470,7 @@ namespace OperationControl.Services double lat = ultimaLeitura.Latitude; double lon = ultimaLeitura.Longitude; double altElips = ultimaLeitura.AltitudeElipsoidal; // garanta que já é elipsoidal no parser + double heading = ultimaLeitura.OrientacaoReal; var fixQual = ultimaLeitura.QualidadeFix; // enum? ok. int hbAfter = ultimaLeitura.Heartbeat; @@ -1515,6 +1487,7 @@ namespace OperationControl.Services latDeg: lat, lonDeg: lon, altElipsoidalM: altElips, + headingDeg: heading, fixQuality: fixQual ); } @@ -1525,40 +1498,46 @@ namespace OperationControl.Services } // ===== 3) Filtro robusto (MAD) + média ===== - private (double lat, double lon, double h, int n) FiltrarEAgrupar(List amostras, double madK = 3.5) + private (double lat, double lon, double h, double hdg, int n) FiltrarEAgrupar(List amostras, double madK = 3.5) { Models.Variaveis.MostrarLog("Filtrando dados aferidos..."); // Medianas var lats = amostras.Select(a => a.LatDeg).OrderBy(x => x).ToArray(); var lons = amostras.Select(a => a.LonDeg).OrderBy(x => x).ToArray(); var hs = amostras.Select(a => a.AltElipsoidalM).OrderBy(x => x).ToArray(); + var hdgs = amostras.Select(a => a.HeadingDeg).OrderBy(x => x).ToArray(); double medLat = Mediana(lats); double medLon = Mediana(lons); double medH = Mediana(hs); + double medHdg = Mediana(hdgs); // Desvios absolutos da mediana (MAD) var dLat = amostras.Select(a => Math.Abs(a.LatDeg - medLat)).OrderBy(x => x).ToArray(); var dLon = amostras.Select(a => Math.Abs(a.LonDeg - medLon)).OrderBy(x => x).ToArray(); var dH = amostras.Select(a => Math.Abs(a.AltElipsoidalM - medH)).OrderBy(x => x).ToArray(); + var dHdg = amostras.Select(a => Math.Abs(a.HeadingDeg - medH)).OrderBy(x => x).ToArray(); double madLat = Mediana(dLat) + 1e-12; double madLon = Mediana(dLon) + 1e-12; double madHgt = Mediana(dH) + 1e-12; + double madHdg = Mediana(dHdg) + 1e-12; // Filtra outliers (|x - med| / MAD <= madK) var filtradas = amostras.Where(a => (Math.Abs(a.LatDeg - medLat) / madLat) <= madK && (Math.Abs(a.LonDeg - medLon) / madLon) <= madK && - (Math.Abs(a.AltElipsoidalM - medH) / madHgt) <= madK + (Math.Abs(a.AltElipsoidalM - medH) / madHgt) <= madK && + (Math.Abs(a.HeadingDeg - medHdg) / madHdg) <= madK ).ToList(); // Média final double lat = filtradas.Average(a => a.LatDeg); double lon = filtradas.Average(a => a.LonDeg); double h = filtradas.Average(a => a.AltElipsoidalM); + double hdg = filtradas.Average(a => a.HeadingDeg); - return (lat, lon, h, filtradas.Count); + return (lat, lon, h, hdg, filtradas.Count); double Mediana(double[] arr) { @@ -1618,10 +1597,9 @@ namespace OperationControl.Services public enum MetodoFixacaoBase { - Automatico = 0, - Ntrip = 1, - SurveyIn = 2, - Manual = 3 + Ntrip = 0, + SurveyIn = 1, + Manual = 2 } } diff --git a/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs index 66ab68662..2e8a8219f 100644 --- a/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/PreparacaoMapa/PreparacaoMapaBottomViewModel.cs @@ -1,20 +1,12 @@ -using Microsoft.Win32; -using OperationControl.Helpers; -using System; -using System.Collections.Generic; +using OperationControl.Helpers; using System.ComponentModel; using System.Globalization; using System.Windows; using System.Windows.Input; +using static OperationControl.Services.BaseFixService; namespace OperationControl.ViewModels { - public enum MetodoFixacaoBase - { - Ntrip = 0, - SurveyIn = 1, - Manual = 2 - } public class MetodoFixacaoItem { @@ -29,7 +21,9 @@ namespace OperationControl.ViewModels public event Action? CarregarMapaSolicitado; // Método, latitude, longitude, altitude, offset frontal cm, offset lateral cm - public event Action? FixarBaseSolicitado; + public event Action? FixarBaseSolicitado; + // Offset frontal cm, offset lateral cm + public event Action? AplicarOffsetSolicitado; public List MetodosFixacaoDisponiveis { get; } = new() { @@ -40,11 +34,13 @@ namespace OperationControl.ViewModels public ICommand CarregarMapaCommand { get; } public ICommand FixarBaseCommand { get; } + public ICommand AplicarOffsetCommand { get; } public PreparacaoMapaBottomViewModel() { CarregarMapaCommand = new RelayCommand(_ => ExecutarCarregarMapa(), _ => PodeCarregarMapa); FixarBaseCommand = new RelayCommand(_ => ExecutarFixarBase(), _ => PodeFixarBase); + AplicarOffsetCommand = new RelayCommand(_ => ExecutarAplicarOffset(), _ => PodeEditarOffsetBase); MetodoFixacaoSelecionado = MetodoFixacaoBase.Ntrip; OffsetFrontalBaseTexto = "0"; @@ -121,6 +117,17 @@ namespace OperationControl.ViewModels public bool ExibirProgresso => MetodoFixacaoSelecionado != MetodoFixacaoBase.Manual; + private bool _exibirOffsets; + public bool ExibirOffsets + { + get { return _exibirOffsets; } + set + { + _exibirOffsets = value; + OnPropertyChanged(nameof(ExibirOffsets)); + } + } + public bool PodeFixarBase { get @@ -356,13 +363,7 @@ namespace OperationControl.ViewModels { if (FixacaoEmAndamento) { - FixarBaseSolicitado?.Invoke( - MetodoFixacaoSelecionado, - LatitudeManual, - LongitudeManual, - AltitudeManual, - OffsetFrontalBaseCm, - OffsetLateralBaseCm); + FixarBaseSolicitado?.Invoke(MetodoFixacaoSelecionado, LatitudeManual, LongitudeManual, AltitudeManual); return; } @@ -381,13 +382,12 @@ namespace OperationControl.ViewModels StatusTexto = "Pronto para aplicar a posição manual da base."; } - FixarBaseSolicitado?.Invoke( - MetodoFixacaoSelecionado, - LatitudeManual, - LongitudeManual, - AltitudeManual, - OffsetFrontalBaseCm, - OffsetLateralBaseCm); + FixarBaseSolicitado?.Invoke(MetodoFixacaoSelecionado, LatitudeManual, LongitudeManual, AltitudeManual); + } + + private void ExecutarAplicarOffset() + { + AplicarOffsetSolicitado?.Invoke(OffsetFrontalBaseCm, OffsetLateralBaseCm); } public void IniciarFixacao() @@ -412,10 +412,9 @@ namespace OperationControl.ViewModels public void FinalizarFixacao(bool sucesso, string? mensagem = null) { + ExibirOffsets = sucesso; FixacaoEmAndamento = false; - - if (MetodoFixacaoSelecionado != MetodoFixacaoBase.Manual && sucesso) - ProgressoFixacao = 100; + if (sucesso) ProgressoFixacao = 100; StatusTexto = !string.IsNullOrWhiteSpace(mensagem) ? mensagem diff --git a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs index 8ad274ba4..a8c82f1ad 100644 --- a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs @@ -1,15 +1,19 @@ -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 AgroBase.Models.Operacoes; +using AgroBase.Services; +using Microsoft.VisualBasic.Logging; using Newtonsoft.Json.Linq; -using OperationControl.ViewModels.Views.Operacao; +using OpenTK.Windowing.Common.Input; using OperationControl.Controls; +using OperationControl.Helpers; +using OperationControl.Models; +using OperationControl.ViewModels.Views.Operacao; +using OperationControl.Views; +using OperationControl.Views.Operacao; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; using static AgroBase.Models.Enums; +using static OperationControl.Services.BaseFixService; namespace OperationControl.ViewModels { @@ -44,6 +48,7 @@ namespace OperationControl.ViewModels _viewPreparacaoMapaBottom = new PreparacaoMapaBottomView(); _viewPreparacaoMapaBottom._vm.CarregarMapaSolicitado += OnCarregarMapaSolicitado; _viewPreparacaoMapaBottom._vm.FixarBaseSolicitado += OnFixarBaseSolicitado; + _viewPreparacaoMapaBottom._vm.AplicarOffsetSolicitado += OnAplicarOffsetSolicitado; _viewOperacaoTop = new OperacaoTopView(); @@ -683,8 +688,6 @@ namespace OperationControl.ViewModels if (string.IsNullOrWhiteSpace(linha2)) linha2 = "Sem detalhes adicionais."; - Models.Variaveis.MostrarLog($"LINHA 1: {linha1}"); - // ============================ // 8) Atualiza UI // ============================ @@ -776,7 +779,7 @@ namespace OperationControl.ViewModels } } - private async void OnFixarBaseSolicitado(MetodoFixacaoBase metodoFix, double? lat, double? lon, double? alt, double? offsetFrontalCm, double? offsetLateralCm) + private async void OnFixarBaseSolicitado(MetodoFixacaoBase metodoFix, double? lat, double? lon, double? alt) { try { @@ -803,14 +806,13 @@ namespace OperationControl.ViewModels if (fixar) { _viewPreparacaoMapaBottom?._vm.IniciarFixacao(); - await Task.Run(async () => await Variaveis.GpsService.ConfigurarModulo(true, metodoFix, lat, lon, alt)); + await Variaveis.GpsService.ConfigurarModulo(true, metodoFix, lat, lon, alt); } } else { Variaveis.GpsService.BaseFix.ReiniciarFix(); BaseFixada = false; - _viewPreparacaoMapaBottom?._vm.CancelarFixacao("Fixação da base interrompida pelo operador."); } } catch (Exception ex) @@ -845,6 +847,17 @@ namespace OperationControl.ViewModels })); } + private async void OnAplicarOffsetSolicitado(double? offsetFrontalCm, double? offsetLateralCm) + { + if (offsetFrontalCm == null || offsetLateralCm == null) return; + + _viewPreparacaoMapaBottom?._vm.IniciarFixacao(); + var gps = Variaveis.GpsService; + gps.LeverArm = new GeoLeverArm(offsetCampoFrontalCm: offsetFrontalCm.Value, offsetCampoLateralCm: offsetLateralCm.Value); + (double lat, double lon) = gps.LeverArm.FixLeverArmLatLon_Fast(gps.BaseFix.LatitudeFix, gps.BaseFix.LongitudeFix, gps.BaseFix.OrientacaoFix); + await Variaveis.GpsService.ConfigurarModulo(true, MetodoFixacaoBase.Manual, lat, lon, gps.BaseFix.AltitudeElpsoidalFix, offset: true); + } + #endregion #region TELA 3 diff --git a/AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaBottomView.xaml b/AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaBottomView.xaml index 0cfd9ee47..4c628cee7 100644 --- a/AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaBottomView.xaml +++ b/AgroBase/OperationControl/Views/PreparacaoMapa/PreparacaoMapaBottomView.xaml @@ -36,90 +36,39 @@ - -