agrobot_base/AgroBase/AgroBase/Services/GPSService.cs

567 lines
22 KiB
C#

using AgroBase.Models;
using Emgu.CV.Features2D;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static AgroBase.Models.Enuns;
namespace AgroBase.Services
{
public class GPSService
{
public static double RaioDaTerra = 6378137; // Raio da Terra em Km
public static bool Iniciado = false;
public static SerialPort PortaGPS = null;
public static GPSModel UltimaLeitura = new GPSModel();
public static GPSModel PenultimaLeitura = new GPSModel();
public static void AtualizarPortaGPS(SerialPort _porta)
{
PortaGPS = new SerialPort()
{
PortName = _porta.PortName,
BaudRate = _porta.BaudRate,
};
PortaGPS.DataReceived -= PortaGPS_DataReceived;
PortaGPS.DataReceived += PortaGPS_DataReceived;
}
public static void IniciarRecepcaoDados()
{
try
{
if (PortaGPS != null && !PortaGPS.IsOpen)
{
PortaGPS.Open();
if (PortaGPS.IsOpen)
{
Iniciado = true;
}
}
}
catch
{
PortaGPS = null;
}
}
public static void PararRecepcaoDados()
{
if (PortaGPS != null && PortaGPS.IsOpen)
{
PortaGPS.Close();
if (!PortaGPS.IsOpen)
{
Iniciado = false;
}
}
}
private static void PortaGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (PortaGPS.IsOpen)
{
string Recebido = PortaGPS.ReadLine();
if (Recebido.Length > 2)
{
ProcessarStringNMEA(Recebido);
}
}
}
public static void ProcessarStringNMEA(string nmeaString)
{
if (string.IsNullOrWhiteSpace(nmeaString))
return;
string[] parts = nmeaString.Split(',');
switch (parts[0])
{
// Global Positioning System Fix Data
case "$GPGGA":
PenultimaLeitura.Latitude = UltimaLeitura.Latitude;
PenultimaLeitura.Longitude = UltimaLeitura.Longitude;
PenultimaLeitura.Altitude = UltimaLeitura.Altitude;
PenultimaLeitura.PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal;
PenultimaLeitura.DataHora = UltimaLeitura.DataHora;
PenultimaLeitura.NumeroSatelites = UltimaLeitura.NumeroSatelites;
PenultimaLeitura.Velocidade = UltimaLeitura.Velocidade;
PenultimaLeitura.Orientacao = UltimaLeitura.Orientacao;
if (parts[2] != "" && parts[3] != "")
{
UltimaLeitura.Latitude = ConvertToDecimalDegrees(parts[2], parts[3], 2); // ConvertToDouble(parts[2]);
}
if (parts[4] != "" && parts[5] != "")
{
UltimaLeitura.Longitude = ConvertToDecimalDegrees(parts[4], parts[5], 3); // ConvertToDouble(parts[4]);
}
UltimaLeitura.NumeroSatelites = int.TryParse(parts[7], out int numSat) ? numSat : 0;
UltimaLeitura.PrecisaoHorizontal = double.TryParse(parts[8], NumberStyles.Float, CultureInfo.InvariantCulture, out double hdop) ? hdop : 0;
UltimaLeitura.Altitude = ConvertToDouble(parts[9]);
UltimaLeitura.Orientacao = CalcularOrientacao(PenultimaLeitura, UltimaLeitura);
//APIService.AtualizarCoordenadasGPS(UltimaLeitura, APIService.GPS_IDcoordenadasRobo);
AtualizarCoordenadasGPS();
break;
// Recommended minimum specific GPS/Transit data
case "$GPRMC":
UltimaLeitura.DataHora = ParseDateTime(parts[9], parts[1], -3);
UltimaLeitura.Velocidade = ConvertKnotsToKmh(ConvertToDouble(parts[7]));
break;
// Você pode adicionar mais cases aqui para outros tipos de sentenças NMEA que você deseja processar
}
}
private static double ConvertToDecimalDegrees(string nmeaCoordinate, string direction, int digits)
{
// Divide a string em graus e minutos
int degrees = int.Parse(nmeaCoordinate.Substring(0, digits));
double minutes = double.Parse(nmeaCoordinate.Substring(digits), CultureInfo.InvariantCulture);
// Converte para graus decimais
double decimalDegrees = degrees + (minutes / 60);
// Se a direção for Sul ou Oeste, o valor deve ser negativo
if (direction == "S" || direction == "W")
{
decimalDegrees *= -1;
}
return decimalDegrees;
}
private static double ConvertToDouble(string value)
{
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out double result);
return result;
}
private static DateTime ParseDateTime(string date, string time, int timeZoneOffset)
{
// DDMMAA, HHMMSS.ss
string dateTimeFormat = "ddMMyyHHmmss.ff";
// Verifica se o formato de data e hora está completo
if (date.Length == 6 && (time.Length == 6 || time.Length == 9))
{
string dateTimeStr = date + time;
if (DateTime.TryParseExact(dateTimeStr, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime result))
{
// Ajusta o DateTime para o fuso horário especificado
result = result.AddHours(timeZoneOffset);
return result;
}
}
// Se a conversão falhar, retorne DateTime.MinValue ou lance uma exceção, conforme sua necessidade
return DateTime.MinValue;
}
private static double ConvertKnotsToKmh(double knots)
{
return knots * 1.852;
}
public static double CalcularOrientacao(GPSModel P1, GPSModel P2)
{
// Converter latitudes e longitudes de graus para radianos
double latA = P1.Latitude * (Math.PI / 180.0);
double lonA = P1.Longitude * (Math.PI / 180.0);
double latB = P2.Latitude * (Math.PI / 180.0);
double lonB = P2.Longitude * (Math.PI / 180.0);
// Calcular a diferença de longitude
double deltaLon = lonB - lonA;
// Calcular a direção
double y = Math.Sin(deltaLon) * Math.Cos(latB);
double x = Math.Cos(latA) * Math.Sin(latB) - Math.Sin(latA) * Math.Cos(latB) * Math.Cos(deltaLon);
double direcaoRadianos = Math.Atan2(y, x);
// Converter a direção de radianos para graus
double direcaoGraus = direcaoRadianos * (180.0 / Math.PI);
// Normalizar a direção para que esteja no intervalo de 0 a 360 graus
direcaoGraus = (direcaoGraus + 360) % 360;
return direcaoGraus;
}
public static double NormalizarAngulo(double angulo)
{
if (angulo < 0)
{
angulo += 360;
}
angulo %= 360;
return angulo;
}
public static double CalcularDiferencaAngulo(double anguloVariavel, double anguloRef)
{
// Calcula a diferença entre o ângulo do carro e o ângulo da rua
double diferenca = anguloVariavel - anguloRef;
// Normaliza a diferença para estar entre -180 e 180 graus
diferenca = ((diferenca + 180) % 360) - 180;
return diferenca;
}
public static GPSModel PontoMaisProximoRuaProjetada(List<GPSModel> RuaProjetada)
{
// Verifica se a lista está vazia
if (RuaProjetada == null || RuaProjetada.Count == 0)
{
return null;
}
// Inicializa variáveis
GPSModel pontoMaisProximo = RuaProjetada[0];
double menorDistancia = double.MaxValue;
GPSModel posicaoAtual = UltimaLeitura;
// Itera sobre os pontos da rua projetada
foreach (var ponto in RuaProjetada)
{
double distanciaAtual = DistanciaEntrePontos(posicaoAtual, ponto);
if (distanciaAtual <= menorDistancia)
{
ponto.Passado = true;
menorDistancia = distanciaAtual;
pontoMaisProximo = ponto;
}
else
{
break;
}
}
return pontoMaisProximo;
}
public static GPSModel PontoMaisProximoRuaProjetada2(List<GPSModel> RuaProjetada)
{
// Verifica se a lista está vazia
if (RuaProjetada == null || RuaProjetada.Count == 0)
{
return null;
}
// Inicializa variáveis
GPSModel pontoMaisProximo = RuaProjetada[0];
double menorDistancia = double.MaxValue;
bool estaAproximando = false;
bool inverteu = false;
//double distanciaPercorrida = DistanciaDoTrecho(Variaveis.OperacaoEmAndamento.GPSTrajetoriaRua[Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual]);
// Calcula a direção (aproximando ou afastando) com base nas primeiras distâncias
double distanciaAtual = 0;
double distanciaAnterior = 0;
GPSModel posicaoAtual = UltimaLeitura;
GPSModel posicaoAnterior = PenultimaLeitura;
// Itera sobre os pontos da rua projetada
foreach (var ponto in RuaProjetada)
{
int idx = RuaProjetada.IndexOf(ponto);
// Calcula a distância entre a última leitura e o ponto atual
distanciaAtual = DistanciaEntrePontos(posicaoAtual, ponto);
distanciaAnterior = DistanciaEntrePontos(posicaoAnterior, ponto);
//double distanciaTrecho = DistanciaDoTrecho(RuaProjetada.Take(idx + 1).ToList());
double distanciaComparar = distanciaAtual; // + distanciaTrecho;
// Atualiza o ponto mais próximo e a menor distância se apropriado
if (distanciaComparar <= menorDistancia)
{
menorDistancia = distanciaComparar;
pontoMaisProximo = ponto;
}
else
{
inverteu = true;
}
estaAproximando = distanciaAtual < distanciaAnterior;
Console.WriteLine("Ponto: " + idx + " - Distancia Atual: " + distanciaAtual.ToString(".00") + " - Distancia Anterior: " + distanciaAnterior.ToString("0.00") + " - Aproximando: " + (estaAproximando ? "Sim" : "Nao"));
// Verifica se houve inversão na direção
if (estaAproximando && pontoMaisProximo != ponto)
{
// O robô começou a se afastar
break;
}
else if (inverteu)
{
break;
}
}
return pontoMaisProximo;
}
public static double DistanciaEntrePontos(GPSModel P1, GPSModel P2)
{
double lat1 = P1.Latitude;
double lat2 = P2.Latitude;
double lon1 = P1.Longitude;
double lon2 = P2.Longitude;
double dLat = ToRadians(lat2 - lat1);
double dLon = ToRadians(lon2 - lon1);
lat1 = ToRadians(lat1);
lat2 = ToRadians(lat2);
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(lat1) * Math.Cos(lat2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
double distance = RaioDaTerra * c;
//distance = distance * 0.5;
return distance;
}
public static double DistanciaDoTrecho(List<GPSModel> Trecho)
{
double d = 0;
for (int i = 0; i < Trecho.Count - 1; i++)
{
d += DistanciaEntrePontos(Trecho[i], Trecho[i + 1]);
}
return d;
}
public static bool CompararDirecaoTrajeto(List<GPSModel> Trecho1, List<GPSModel> Trecho2)
{
double MargemErroAngulo = 45;
double anguloTrecho1 = CalcularOrientacao(Trecho1[0], Trecho1[Trecho1.Count - 1]);
double anguloTrecho2 = CalcularOrientacao(Trecho2[0], Trecho2[Trecho2.Count - 1]);
if ((anguloTrecho1 + MargemErroAngulo) >= anguloTrecho2 && (anguloTrecho1 - MargemErroAngulo) <= anguloTrecho2)
{
return true;
}
else
{
return false;
}
}
public static GPSModel ProjetarPontoDeslocado(GPSModel pontoOriginal, double distancia, double angulo)
{
// Convertendo ângulo em radianos
double anguloRad = (Math.PI / 180) * angulo;
// Calculando deslocamento
double deltaLat = (distancia * Math.Cos(anguloRad)) / RaioDaTerra;
double deltaLon = (distancia * Math.Sin(anguloRad)) / (RaioDaTerra * Math.Cos(pontoOriginal.Latitude * Math.PI / 180));
// Convertendo deslocamento de radianos para graus
deltaLat = deltaLat * (180 / Math.PI);
deltaLon = deltaLon * (180 / Math.PI);
// Criando novo ponto com o deslocamento
GPSModel pontoDeslocado = new GPSModel
{
Latitude = pontoOriginal.Latitude + deltaLat,
Longitude = pontoOriginal.Longitude + deltaLon,
Altitude = pontoOriginal.Altitude,
DataHora = pontoOriginal.DataHora,
NumeroSatelites = pontoOriginal.NumeroSatelites,
PrecisaoHorizontal = pontoOriginal.PrecisaoHorizontal,
Velocidade = pontoOriginal.Velocidade
};
return pontoDeslocado;
}
private static double ToRadians(double angle)
{
return Math.PI / 180 * angle;
}
public static void AtualizarCoordenadasGPS()
{
var Coordenadas = new
{
latitude = UltimaLeitura.Latitude,
longitude = UltimaLeitura.Longitude
};
Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoCoordenadasGPS),
JsonConvert.SerializeObject(Coordenadas)
).ConfigureAwait(false);
Variaveis.OperacaoEmAndamento.GPSTrajetoria.Add(new GPSModel()
{
NumeroSatelites = UltimaLeitura.NumeroSatelites,
Altitude = UltimaLeitura.Altitude,
DataHora = UltimaLeitura.DataHora,
Latitude = UltimaLeitura.Latitude,
Longitude = UltimaLeitura.Longitude,
PrecisaoHorizontal = UltimaLeitura.PrecisaoHorizontal,
Velocidade = UltimaLeitura.Velocidade
});
if (Variaveis.OperacaoEmAndamento.GPSTrajetoriaRua.Count() - 1 < Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual)
{
Variaveis.OperacaoEmAndamento.GPSTrajetoriaRua.Add(new List<GPSModel>());
}
Variaveis.OperacaoEmAndamento.GPSTrajetoriaRua[Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual].Add(Variaveis.OperacaoEmAndamento.GPSTrajetoria[Variaveis.OperacaoEmAndamento.GPSTrajetoria.Count - 1]);
if (Variaveis.OperacaoEmAndamento.GPSTrajetoria.Count > 1)
{
if (Variaveis.OperacaoEmAndamento.Sensoriamento.DistanciaPercorrida.Count - 1 < Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual)
{
Variaveis.OperacaoEmAndamento.Sensoriamento.DistanciaPercorrida.Add(0);
}
Variaveis.OperacaoEmAndamento.Sensoriamento.DistanciaPercorrida[Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual] += GPSService.DistanciaEntrePontos(Variaveis.OperacaoEmAndamento.GPSTrajetoria[Variaveis.OperacaoEmAndamento.GPSTrajetoria.Count - 1], Variaveis.OperacaoEmAndamento.GPSTrajetoria[Variaveis.OperacaoEmAndamento.GPSTrajetoria.Count - 2]);
}
}
public static void AtualizarTrajetoriaDinamica()
{
var Trajetoria = Variaveis.OperacaoEmAndamento.Mapa.TrajetoriaDinamica.Select(x => new
{
latitude = x.Latitude,
longitude = x.Longitude
}).ToArray();
Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoTrajetoriaDinamica),
JsonConvert.SerializeObject(Trajetoria)
).ConfigureAwait(false);
}
public static List<GPSModel> InterpolarPontos(GPSModel inicio, GPSModel destino, double distanciaPasso)
{
List<GPSModel> pontos = new List<GPSModel>();
var distanciaTotal = DistanciaEntrePontos(inicio, destino);
var passos = distanciaTotal / distanciaPasso;
for (int i = 0; i <= passos; i++)
{
var frac = i / passos;
var lat = inicio.Latitude + (destino.Latitude - inicio.Latitude) * frac;
var lon = inicio.Longitude + (destino.Longitude - inicio.Longitude) * frac;
pontos.Add(new GPSModel { Latitude = lat, Longitude = lon });
}
return pontos;
}
public static GPSModel PontoMedio(GPSModel P1, GPSModel P2)
{
return new GPSModel()
{
Latitude = (P1.Latitude + P2.Latitude) / 2,
Longitude = (P1.Longitude + P2.Longitude) / 2,
};
}
private static MapaFeatureCollectionModel TrajetoFake = new MapaFeatureCollectionModel();
private static int idxPonto = 0;
private static int idxRuaA = 0;
public static void SimularGPS()
{
var dados = File.ReadAllText("trajetoFakeGps.json");
TrajetoFake = JsonConvert.DeserializeObject<MapaFeatureCollectionModel>(dados);
int i = 0;
TrajetoFake.features.ForEach(feature =>
{
feature.geometry.id = i.ToString();
i++;
});
Timer tmr = new Timer() { Interval = 1000 };
tmr.Tick += Tmr_Tick;
tmr.Start();
Iniciado = true;
}
private static void Tmr_Tick(object sender, EventArgs e)
{
if (Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual >= TrajetoFake.features.Count) // Verifica se chegou ao fim do trajeto total
{
((Timer)sender).Stop();
Iniciado = false;
return; // Encerra a execução para evitar processamento adicional
}
AtualizaLeituraSimulacao();
}
public static void AtualizaLeituraSimulacao()
{
if (idxRuaA != Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual)
{
idxPonto = 0;
idxRuaA = Variaveis.OperacaoEmAndamento.Mapa.IdxRuaAtual;
//double distPP = DistanciaEntrePontos(UltimaLeitura, new GPSModel() { Longitude = TrajetoFake.features[idxRuaA].geometry.coordinates[0][0], Latitude = TrajetoFake.features[idxRuaA].geometry.coordinates[0][1] });
//double distUP = DistanciaEntrePontos(UltimaLeitura, new GPSModel() { Longitude = TrajetoFake.features[idxRuaA].geometry.coordinates[TrajetoFake.features[idxRuaA].geometry.coordinates.Count - 1][0], Latitude = TrajetoFake.features[idxRuaA].geometry.coordinates[TrajetoFake.features[idxRuaA].geometry.coordinates.Count - 1][1] });
if (idxRuaA % 2 != 0)
{
TrajetoFake.features[idxRuaA].geometry.coordinates.Reverse();
}
}
PenultimaLeitura = new GPSModel()
{
DataHora = UltimaLeitura.DataHora,
Longitude = UltimaLeitura.Longitude,
Latitude = UltimaLeitura.Latitude,
};
UltimaLeitura = new GPSModel()
{
DataHora = DateTime.Now,
Longitude = TrajetoFake.features[idxRuaA].geometry.coordinates[idxPonto][0],
Latitude = TrajetoFake.features[idxRuaA].geometry.coordinates[idxPonto][1],
};
//APIService.AtualizarCoordenadasGPS(UltimaLeitura, APIService.GPS_IDcoordenadasRobo);
AtualizarCoordenadasGPS();
var StatusCarro = Variaveis.OperacaoEmAndamento.OpMapaGPS.ReferencialRuaGPS.StatusAtual;
if (StatusCarro == Enuns.StatusCarroMapa.Parado || StatusCarro == Enuns.StatusCarroMapa.Direcionando)
{
return;
}
if (idxPonto < TrajetoFake.features[idxRuaA].geometry.coordinates.Count - 1)
{
idxPonto++;
}
}
}
}