otimizado o simulador
This commit is contained in:
parent
024eceef60
commit
1e7b725422
File diff suppressed because it is too large
Load Diff
|
|
@ -1959,7 +1959,7 @@ namespace AgroBase.Models
|
||||||
var dispMvd = DispMvd;
|
var dispMvd = DispMvd;
|
||||||
|
|
||||||
if (dispMvd?.Dados?.Modulos != null) // && value != Controle.TipoMovimento
|
if (dispMvd?.Dados?.Modulos != null) // && value != Controle.TipoMovimento
|
||||||
{
|
{
|
||||||
Controle.DefinirTipoMovimento(value, dispMvd.Dados.Modulos);
|
Controle.DefinirTipoMovimento(value, dispMvd.Dados.Modulos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
using static AgroBase.Models.Enums;
|
using static AgroBase.Models.Enums;
|
||||||
|
|
||||||
namespace AgroBase.Services
|
namespace AgroBase.Services
|
||||||
|
|
@ -171,6 +172,94 @@ namespace AgroBase.Services
|
||||||
private static string _lastError;
|
private static string _lastError;
|
||||||
private static readonly object _metricsTextLock = new object();
|
private static readonly object _metricsTextLock = new object();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private const int IntervaloTrajetoriaMapaMs = 1_000;
|
||||||
|
private const int MaxPontosTrajetoriaMapa = 800;
|
||||||
|
|
||||||
|
private static int _publicacaoMapaAutomaticaHabilitada = 1;
|
||||||
|
private static long _ultimaTrajetoriaMapaMono;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Permite que uma tela seja dona da publicação visual sem interromper o
|
||||||
|
/// processamento GNSS, Redis, trajetória ou histórico da operação.
|
||||||
|
/// O valor padrão preserva o comportamento legado.
|
||||||
|
/// </summary>
|
||||||
|
public static bool PublicacaoMapaAutomaticaHabilitada
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Volatile.Read(
|
||||||
|
ref _publicacaoMapaAutomaticaHabilitada
|
||||||
|
) == 1;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Volatile.Write(
|
||||||
|
ref _publicacaoMapaAutomaticaHabilitada,
|
||||||
|
value ? 1 : 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AtualizarLeituraSimulada(GPSModel leitura, int atrasoPosicaoPassos = 0)
|
||||||
|
{
|
||||||
|
if (leitura == null)
|
||||||
|
throw new ArgumentNullException(nameof(leitura));
|
||||||
|
|
||||||
|
lock (_stateLock)
|
||||||
|
{
|
||||||
|
double orientacaoReal =
|
||||||
|
GPSUtils.NormalizarAngulo(leitura.OrientacaoReal);
|
||||||
|
|
||||||
|
UltimaLeitura.Momento = leitura.Momento;
|
||||||
|
UltimaLeitura.DataHora = leitura.DataHora;
|
||||||
|
UltimaLeitura.UltimoComandoRespondido =
|
||||||
|
leitura.UltimoComandoRespondido;
|
||||||
|
|
||||||
|
UltimaLeitura.Latitude = leitura.Latitude;
|
||||||
|
UltimaLeitura.Longitude = leitura.Longitude;
|
||||||
|
UltimaLeitura.LatitudeAnt = leitura.LatitudeAnt;
|
||||||
|
UltimaLeitura.LongitudeAnt = leitura.LongitudeAnt;
|
||||||
|
|
||||||
|
UltimaLeitura.OrientacaoReal = orientacaoReal;
|
||||||
|
UltimaLeitura.AnguloCarroDefinido = orientacaoReal;
|
||||||
|
UltimaLeitura.TipoOrientacao = "A";
|
||||||
|
|
||||||
|
UltimaLeitura.OrientacaoMovimento =
|
||||||
|
double.IsNaN(leitura.OrientacaoMovimento) ||
|
||||||
|
double.IsInfinity(leitura.OrientacaoMovimento)
|
||||||
|
? orientacaoReal
|
||||||
|
: GPSUtils.NormalizarAngulo(
|
||||||
|
leitura.OrientacaoMovimento
|
||||||
|
);
|
||||||
|
|
||||||
|
UltimaLeitura.Distancia = leitura.Distancia;
|
||||||
|
UltimaLeitura.Velocidade = leitura.Velocidade;
|
||||||
|
|
||||||
|
if (leitura.TimestampOri != null)
|
||||||
|
UltimaLeitura.TimestampOri =
|
||||||
|
leitura.TimestampOri.Clone();
|
||||||
|
|
||||||
|
if (leitura.TimestampPos != null)
|
||||||
|
UltimaLeitura.TimestampPos =
|
||||||
|
leitura.TimestampPos.Clone();
|
||||||
|
|
||||||
|
UltimaLeitura.Heartbeat = leitura.Heartbeat;
|
||||||
|
UltimaLeitura.Inicializado = true;
|
||||||
|
|
||||||
|
// Monitor/lock do C# é reentrante; os dois métodos abaixo usam o
|
||||||
|
// mesmo lock e permanecem em uma única transação de leitura GNSS.
|
||||||
|
AtualizarAtrasoPosicoes(
|
||||||
|
Math.Max(0, atrasoPosicaoPassos)
|
||||||
|
);
|
||||||
|
AtualizarCoordenadasGPS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// INICIALIZAÇÃO DA PORTA
|
// INICIALIZAÇÃO DA PORTA
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -2198,14 +2287,30 @@ namespace AgroBase.Services
|
||||||
op.Trajetoria.LoopAtualizaDados();
|
op.Trajetoria.LoopAtualizaDados();
|
||||||
}
|
}
|
||||||
|
|
||||||
AtualizarTrajetoriaDinamica();
|
if (PublicacaoMapaAutomaticaHabilitada)
|
||||||
|
|
||||||
EnviarCoordenadasParaMapa(UltimaLeitura.Latitude, UltimaLeitura.Longitude, UltimaLeitura.AnguloCarroDefinido, op.Sensoriamento.Operacao.OperacaoIniciada, rover: true);
|
|
||||||
|
|
||||||
GPSModel posicaoBase = VariaveisOperacao.ObterPosicaoBaseSnapshot();
|
|
||||||
if ((posicaoBase?.UltimoComandoRespondido ?? DateTime.MinValue) > DateTime.UtcNow.AddSeconds(-60))
|
|
||||||
{
|
{
|
||||||
EnviarCoordenadasParaMapa(posicaoBase.Latitude, posicaoBase.Longitude, posicaoBase.OrientacaoReal, false, rover: false);
|
AtualizarTrajetoriaDinamica();
|
||||||
|
|
||||||
|
EnviarCoordenadasParaMapa(
|
||||||
|
UltimaLeitura.Latitude,
|
||||||
|
UltimaLeitura.Longitude,
|
||||||
|
UltimaLeitura.AnguloCarroDefinido,
|
||||||
|
op.Sensoriamento.Operacao.OperacaoIniciada,
|
||||||
|
rover: true
|
||||||
|
);
|
||||||
|
|
||||||
|
GPSModel posicaoBase = VariaveisOperacao.ObterPosicaoBaseSnapshot();
|
||||||
|
|
||||||
|
if ((posicaoBase?.UltimoComandoRespondido ?? DateTime.MinValue) > DateTime.UtcNow.AddSeconds(-60))
|
||||||
|
{
|
||||||
|
EnviarCoordenadasParaMapa(
|
||||||
|
posicaoBase.Latitude,
|
||||||
|
posicaoBase.Longitude,
|
||||||
|
posicaoBase.OrientacaoReal,
|
||||||
|
false,
|
||||||
|
rover: false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PenultimaLeitura.Inicializado = UltimaLeitura.Inicializado;
|
PenultimaLeitura.Inicializado = UltimaLeitura.Inicializado;
|
||||||
|
|
@ -2221,29 +2326,172 @@ namespace AgroBase.Services
|
||||||
// PUBLICAÇÃO LOCAL LATEST-ONLY
|
// PUBLICAÇÃO LOCAL LATEST-ONLY
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
public static void EnviarCoordenadasParaMapa(double Latitude, double Longitude, double Orientacao, bool EmFoco, bool rover)
|
public static void EnviarCoordenadasParaMapa(double latitude, double longitude, double orientacao, bool emFoco, bool rover)
|
||||||
{
|
{
|
||||||
int ID = rover ? MapasVariaveisModel.IDMarcadorRover : MapasVariaveisModel.IDMarcadorBase;
|
MapasModel mapa = Variaveis.OperacaoEmAndamento?.Mapa;
|
||||||
Variaveis.OperacaoEmAndamento?.Mapa?.AtualizarPosicaoMapaAsync(ID, Latitude, Longitude, Orientacao, EmFoco);
|
Panel painel = mapa?.pnlMapa;
|
||||||
|
|
||||||
|
// Produto headless: retorna antes de tocar no WebView.
|
||||||
|
if (painel == null || painel.IsDisposed || !painel.IsHandleCreated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = rover
|
||||||
|
? MapasVariaveisModel.IDMarcadorRover
|
||||||
|
: MapasVariaveisModel.IDMarcadorBase;
|
||||||
|
|
||||||
|
ExecutarMapaNaUi(
|
||||||
|
mapa,
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
Task tarefa = mapa.AtualizarPosicaoMapaAsync(
|
||||||
|
id,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
orientacao,
|
||||||
|
emFoco
|
||||||
|
);
|
||||||
|
|
||||||
|
ObservarFalhaMapa(
|
||||||
|
tarefa,
|
||||||
|
"GPSService.EnviarCoordenadasParaMapa"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AtualizarTrajetoriaDinamica()
|
public static void AtualizarTrajetoriaDinamica()
|
||||||
{
|
{
|
||||||
var op = Variaveis.OperacaoEmAndamento;
|
var op = Variaveis.OperacaoEmAndamento;
|
||||||
|
var trajetoria = op?.Trajetoria;
|
||||||
|
MapasModel mapa = op?.Mapa;
|
||||||
|
Panel painel = mapa?.pnlMapa;
|
||||||
|
|
||||||
var dinamica = op.Trajetoria?._TrajetoriaDinamica;
|
// No produto sem UI, não cria cópia nem serializa a trajetória.
|
||||||
|
if (trajetoria == null || painel == null || painel.IsDisposed || !painel.IsHandleCreated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(dinamica?.Any() ?? false))
|
long anterior = Interlocked.Read(ref _ultimaTrajetoriaMapaMono);
|
||||||
|
|
||||||
|
if (GetAgeMs(anterior) < IntervaloTrajetoriaMapaMs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var trajetoria = op.Trajetoria.TrajetoriaDinamica.Select(x => new
|
long agora = Stopwatch.GetTimestamp();
|
||||||
{
|
|
||||||
latitude = x.Latitude,
|
|
||||||
longitude = x.Longitude
|
|
||||||
})
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
op?.Mapa?.DefinirTrajetoriaPlanejadaAsync(op.Trajetoria.TrajetoriaDinamica);
|
// Caso duas leituras cheguem juntas, somente uma publica.
|
||||||
|
if (Interlocked.CompareExchange(ref _ultimaTrajetoriaMapaMono, agora, anterior) != anterior)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GPSModel> pontos = CriarTrajetoriaMapaVisual(trajetoria._TrajetoriaDinamica, MaxPontosTrajetoriaMapa);
|
||||||
|
|
||||||
|
if (pontos.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ExecutarMapaNaUi(
|
||||||
|
mapa,
|
||||||
|
delegate
|
||||||
|
{
|
||||||
|
Task tarefa = mapa.DefinirTrajetoriaPlanejadaAsync(pontos);
|
||||||
|
|
||||||
|
ObservarFalhaMapa(
|
||||||
|
tarefa,
|
||||||
|
"GPSService.AtualizarTrajetoriaDinamica"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<GPSModel> CriarTrajetoriaMapaVisual(IList<PontoTrajetoriaModel> origem, int limite)
|
||||||
|
{
|
||||||
|
var resultado = new List<GPSModel>();
|
||||||
|
|
||||||
|
if (origem == null || origem.Count == 0)
|
||||||
|
return resultado;
|
||||||
|
|
||||||
|
int count = origem.Count;
|
||||||
|
int passo = Math.Max(1, (int)Math.Ceiling(count / (double)Math.Max(2, limite)));
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
PontoTrajetoriaModel ponto = origem[i];
|
||||||
|
|
||||||
|
if (ponto?.Posicao == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool estrutural =
|
||||||
|
ponto.PontoBorda ||
|
||||||
|
ponto.PontoLigacao ||
|
||||||
|
ponto.Tipo == TipoPontoRua.BordaEntrada ||
|
||||||
|
ponto.Tipo == TipoPontoRua.BordaSaida ||
|
||||||
|
ponto.Tipo == TipoPontoRua.LigacaoEntrada ||
|
||||||
|
ponto.Tipo == TipoPontoRua.LigacaoSaida;
|
||||||
|
|
||||||
|
if (i != 0 && i != count - 1 && !estrutural && i % passo != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultado.Add(new GPSModel()
|
||||||
|
{
|
||||||
|
Latitude = ponto.Posicao.Latitude,
|
||||||
|
Longitude = ponto.Posicao.Longitude
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultado;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ExecutarMapaNaUi(MapasModel mapa, Action acao)
|
||||||
|
{
|
||||||
|
Panel painel = mapa?.pnlMapa;
|
||||||
|
|
||||||
|
if (acao == null || painel == null || painel.IsDisposed || !painel.IsHandleCreated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (painel.InvokeRequired)
|
||||||
|
{
|
||||||
|
painel.BeginInvoke(acao);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
acao();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// A janela pode estar fechando entre as validações.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ObservarFalhaMapa(Task tarefa, string origem)
|
||||||
|
{
|
||||||
|
if (tarefa == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_ = tarefa.ContinueWith(
|
||||||
|
t =>
|
||||||
|
{
|
||||||
|
Exception erro = t.Exception?.GetBaseException();
|
||||||
|
RecordError(
|
||||||
|
"[" + origem + "] " +
|
||||||
|
(erro?.Message ?? "Falha desconhecida no mapa.")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
CancellationToken.None,
|
||||||
|
TaskContinuationOptions.OnlyOnFaulted,
|
||||||
|
TaskScheduler.Default
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LocalPublisherLoopAsync(CancellationToken cancellationToken)
|
private static async Task LocalPublisherLoopAsync(CancellationToken cancellationToken)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue