ajustado saude do gps
This commit is contained in:
parent
b83b74cf77
commit
a5c377195f
|
|
@ -617,46 +617,136 @@ namespace AgroBase.Services
|
||||||
|
|
||||||
private static void ProcessarGNTHS(string sentenca)
|
private static void ProcessarGNTHS(string sentenca)
|
||||||
{
|
{
|
||||||
//Console.WriteLine(sentenca);
|
if (string.IsNullOrWhiteSpace(sentenca))
|
||||||
// Remove o caractere de início '$' e divide os campos
|
|
||||||
var campos = sentenca.TrimStart('$').Split(',');
|
|
||||||
|
|
||||||
if (campos.Length < 2)
|
|
||||||
{
|
|
||||||
Variaveis.MostrarLog("[GPSService.ProcessarGNTHS] Sentença incompleta.");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (campos.Length > 2 && string.IsNullOrEmpty(campos[1]))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Parsing dos campos
|
/*
|
||||||
double headingTrue = double.Parse(campos[1].Replace(".", ",")); // Campo <Heading>
|
* Exemplos:
|
||||||
string status = campos[2].Split('*')[0]; // Campo <Status>
|
*
|
||||||
string checksum = campos[2].Split('*')[1]; // Campo <Checksum>
|
* Válido:
|
||||||
|
* $GPTHS,123.4567,A*XX
|
||||||
|
*
|
||||||
|
* Inválido:
|
||||||
|
* $GPTHS,,V*0E
|
||||||
|
*/
|
||||||
|
|
||||||
PenultimaLeitura.TimestampOri = UltimaLeitura.TimestampOri.Clone();
|
string conteudo = sentenca.Trim();
|
||||||
PenultimaLeitura.Momento = UltimaLeitura.Momento;
|
|
||||||
PenultimaLeitura.OrientacaoReal = UltimaLeitura.OrientacaoReal;
|
if (conteudo.StartsWith("$"))
|
||||||
PenultimaLeitura.TipoOrientacao = UltimaLeitura.TipoOrientacao;
|
conteudo = conteudo.Substring(1);
|
||||||
|
|
||||||
|
string[] partesChecksum = conteudo.Split(
|
||||||
|
new[] { '*' },
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
string corpo = partesChecksum[0];
|
||||||
|
|
||||||
|
string[] campos = corpo.Split(',');
|
||||||
|
|
||||||
|
// [0] = GPTHS/GNTHS
|
||||||
|
// [1] = heading
|
||||||
|
// [2] = status A/V
|
||||||
|
if (campos.Length < 3)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog(
|
||||||
|
$"[GPSService.ProcessarGNTHS] Sentença incompleta: {sentenca}"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string headingTexto = (campos[1] ?? string.Empty).Trim();
|
||||||
|
|
||||||
|
string statusRecebido = (campos[2] ?? string.Empty)
|
||||||
|
.Trim()
|
||||||
|
.ToUpperInvariant();
|
||||||
|
|
||||||
|
bool headingNumerico = double.TryParse(
|
||||||
|
headingTexto,
|
||||||
|
NumberStyles.Float,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
out double headingTrue
|
||||||
|
);
|
||||||
|
|
||||||
|
bool headingValido =
|
||||||
|
statusRecebido == "A" &&
|
||||||
|
headingNumerico &&
|
||||||
|
!double.IsNaN(headingTrue) &&
|
||||||
|
!double.IsInfinity(headingTrue);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Toda sentença recebida gera uma nova leitura,
|
||||||
|
* mesmo quando o heading está inválido.
|
||||||
|
*
|
||||||
|
* Dessa forma, a frequência continua sendo calculada
|
||||||
|
* pela chegada das sentenças THS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PenultimaLeitura.TimestampOri.valor =
|
||||||
|
UltimaLeitura.TimestampOri.valor;
|
||||||
|
|
||||||
|
PenultimaLeitura.Momento =
|
||||||
|
UltimaLeitura.Momento;
|
||||||
|
|
||||||
|
PenultimaLeitura.OrientacaoReal =
|
||||||
|
UltimaLeitura.OrientacaoReal;
|
||||||
|
|
||||||
|
PenultimaLeitura.TipoOrientacao =
|
||||||
|
UltimaLeitura.TipoOrientacao;
|
||||||
|
|
||||||
|
UltimaLeitura.TimestampOri.valor =
|
||||||
|
Stopwatch.GetTimestamp() /
|
||||||
|
(double)Stopwatch.Frequency;
|
||||||
|
|
||||||
UltimaLeitura.TimestampOri.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
|
||||||
UltimaLeitura.Momento = DateTime.Now;
|
UltimaLeitura.Momento = DateTime.Now;
|
||||||
UltimaLeitura.OrientacaoReal = InverterHeading ? GPSUtils.NormalizarAngulo(headingTrue - 180.0) : headingTrue;
|
|
||||||
UltimaLeitura.TipoOrientacao = status;
|
if (!headingValido)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Comunicação está funcionando, mas o UM982
|
||||||
|
* não possui solução válida de heading.
|
||||||
|
*/
|
||||||
|
UltimaLeitura.OrientacaoReal = 9999;
|
||||||
|
|
||||||
|
UltimaLeitura.TipoOrientacao = "V";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Não aplicar posição corrigida nem atualizar
|
||||||
|
* o ângulo usado pelo controle do rover.
|
||||||
|
*
|
||||||
|
* O valor 9999 existe apenas para telemetria
|
||||||
|
* e diagnóstico de saúde.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
headingTrue = GPSUtils.NormalizarAngulo(
|
||||||
|
headingTrue
|
||||||
|
);
|
||||||
|
|
||||||
|
UltimaLeitura.OrientacaoReal =
|
||||||
|
InverterHeading
|
||||||
|
? GPSUtils.NormalizarAngulo(
|
||||||
|
headingTrue - 180.0
|
||||||
|
)
|
||||||
|
: headingTrue;
|
||||||
|
|
||||||
|
UltimaLeitura.TipoOrientacao = "A";
|
||||||
|
|
||||||
AplicarPosicaoCorrigida();
|
AplicarPosicaoCorrigida();
|
||||||
|
|
||||||
// ✅ Atualiza o ângulo unificado no GPSService
|
// Atualiza o ângulo usado pelo restante do sistema
|
||||||
|
// somente quando a solução é válida.
|
||||||
DefinirAnguloCarro();
|
DefinirAnguloCarro();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Variaveis.MostrarLog($"[GPSService.ProcessarGNTHS] Erro ao processar a sentença: {ex.Message}");
|
Variaveis.MostrarLog(
|
||||||
|
$"[GPSService.ProcessarGNTHS] " +
|
||||||
|
$"Erro ao processar a sentença '{sentenca}': {ex.Message}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1320,7 +1410,10 @@ namespace AgroBase.Services
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GPSModel posicaoAtual = Variaveis.OperacaoEmAndamento.Simulando ? historicoPosicao.Peek() : UltimaLeitura;
|
GPSModel posicaoAtual =
|
||||||
|
Variaveis.OperacaoEmAndamento.Simulando
|
||||||
|
? historicoPosicao.Peek()
|
||||||
|
: UltimaLeitura;
|
||||||
|
|
||||||
bool rtkValido =
|
bool rtkValido =
|
||||||
posicaoAtual.IdadeCorrecao >= 0 &&
|
posicaoAtual.IdadeCorrecao >= 0 &&
|
||||||
|
|
@ -1331,31 +1424,91 @@ namespace AgroBase.Services
|
||||||
posicaoAtual.QualidadeFix == TiposCorrecaoGPS.DGPS
|
posicaoAtual.QualidadeFix == TiposCorrecaoGPS.DGPS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// ESTADO DO HEADING DUAL-ANTENNA
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
string statusOrientacao =
|
||||||
|
(posicaoAtual.TipoOrientacao ?? string.Empty)
|
||||||
|
.Trim()
|
||||||
|
.ToUpperInvariant();
|
||||||
|
|
||||||
|
double valorOrientacao =
|
||||||
|
posicaoAtual.OrientacaoReal;
|
||||||
|
|
||||||
|
bool valorOrientacaoNumerico =
|
||||||
|
!double.IsNaN(valorOrientacao) &&
|
||||||
|
!double.IsInfinity(valorOrientacao);
|
||||||
|
|
||||||
|
bool valorOrientacaoDentroDaFaixa =
|
||||||
|
valorOrientacaoNumerico &&
|
||||||
|
valorOrientacao >= 0.0 &&
|
||||||
|
valorOrientacao < 360.0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A solução só é utilizável quando:
|
||||||
|
*
|
||||||
|
* 1. O UM982 informou status "A";
|
||||||
|
* 2. O valor é numérico;
|
||||||
|
* 3. O valor está na faixa válida de heading.
|
||||||
|
*
|
||||||
|
* Assim, a sentinela 9999 nunca será considerada válida.
|
||||||
|
*/
|
||||||
|
bool orientacaoValida =
|
||||||
|
statusOrientacao == "A" &&
|
||||||
|
valorOrientacaoDentroDaFaixa;
|
||||||
|
|
||||||
RedisService.AtualizarCampos(
|
RedisService.AtualizarCampos(
|
||||||
RedisService.ModKey(T_Code.Gps),
|
RedisService.ModKey(T_Code.Gps),
|
||||||
|
|
||||||
("conectado", Iniciado),
|
("conectado", Iniciado),
|
||||||
("freq_base", TaxaAmostragemHz),
|
("freq_base", TaxaAmostragemHz),
|
||||||
|
|
||||||
|
// Posição e orientação utilizada pelo carro
|
||||||
("lat", posicaoAtual.Latitude),
|
("lat", posicaoAtual.Latitude),
|
||||||
("lon", posicaoAtual.Longitude),
|
("lon", posicaoAtual.Longitude),
|
||||||
("theta", posicaoAtual.AnguloCarroDefinido),
|
("theta", posicaoAtual.AnguloCarroDefinido),
|
||||||
|
|
||||||
|
// Estado da solução GNSS
|
||||||
("fix", (int)posicaoAtual.QualidadeFix),
|
("fix", (int)posicaoAtual.QualidadeFix),
|
||||||
("rtk", rtkValido),
|
("rtk", rtkValido),
|
||||||
("hAcc", posicaoAtual.PrecisaoCm),
|
("hAcc", posicaoAtual.PrecisaoCm),
|
||||||
("nSatelites", posicaoAtual.NumeroSatelites),
|
("nSatelites", posicaoAtual.NumeroSatelites),
|
||||||
|
("age", posicaoAtual.IdadeCorrecao),
|
||||||
|
|
||||||
|
// Frequências
|
||||||
("freq.posicao", posicaoAtual.TimestampPos.frequencia),
|
("freq.posicao", posicaoAtual.TimestampPos.frequencia),
|
||||||
("freq.orientacao", posicaoAtual.TimestampOri.frequencia),
|
("freq.orientacao", posicaoAtual.TimestampOri.frequencia),
|
||||||
|
|
||||||
|
// Latências
|
||||||
("latency.posicao", posicaoAtual.TimestampPos.dt),
|
("latency.posicao", posicaoAtual.TimestampPos.dt),
|
||||||
("latency.orientacao", posicaoAtual.TimestampOri.dt),
|
("latency.orientacao", posicaoAtual.TimestampOri.dt),
|
||||||
|
|
||||||
|
// Timestamps monotônicos
|
||||||
("timestamp.posicao", posicaoAtual.TimestampPos.valor),
|
("timestamp.posicao", posicaoAtual.TimestampPos.valor),
|
||||||
("timestamp.orientacao", posicaoAtual.TimestampOri.valor),
|
("timestamp.orientacao", posicaoAtual.TimestampOri.valor),
|
||||||
("age", posicaoAtual.IdadeCorrecao),
|
|
||||||
|
// Estado específico do heading dual-antenna
|
||||||
|
("orientacao.status", statusOrientacao),
|
||||||
|
("orientacao.valida", orientacaoValida),
|
||||||
|
("orientacao.valor", valorOrientacao),
|
||||||
|
|
||||||
("heartbeat", posicaoAtual.Heartbeat)
|
("heartbeat", posicaoAtual.Heartbeat)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Variaveis.MostrarLog($"[GPSService.AtualizarDadosRedis] Erro ao salvar dados do GPS no Redis: {e.Message}");
|
Variaveis.MostrarLog(
|
||||||
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(T_Code.Gps, StatusModulo.Falha, 0, $"Erro ao salvar dados do GPS no Redis: {e.Message}");
|
$"[GPSService.AtualizarDadosRedis] " +
|
||||||
|
$"Erro ao salvar dados do GPS no Redis: {e.Message}"
|
||||||
|
);
|
||||||
|
|
||||||
|
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(
|
||||||
|
T_Code.Gps,
|
||||||
|
StatusModulo.Falha,
|
||||||
|
0,
|
||||||
|
$"Erro ao salvar dados do GPS no Redis: {e.Message}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
||||||
def atualizar_saude(self):
|
def atualizar_saude(self):
|
||||||
try:
|
try:
|
||||||
now = time.perf_counter()
|
now = time.perf_counter()
|
||||||
modulo = ContextoGlobalRedis.get_modulo(self.t_code)
|
modulo = ContextoGlobalRedis.get_modulo(self.t_code) or {}
|
||||||
FREQ_BASE = modulo.get("freq_base", 1)
|
FREQ_BASE = modulo.get("freq_base", 1)
|
||||||
FREQ_MIN = FREQ_BASE * 0.2
|
FREQ_MIN = FREQ_BASE * 0.2
|
||||||
SAUDE_MIN_ALERTA = 80
|
SAUDE_MIN_ALERTA = 80
|
||||||
|
|
@ -31,18 +31,68 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
||||||
age = modulo.get("age", -1)
|
age = modulo.get("age", -1)
|
||||||
ntrip = modulo.get("correcao_ntrip", False)
|
ntrip = modulo.get("correcao_ntrip", False)
|
||||||
|
|
||||||
|
orientacao = modulo.get("orientacao") or {}
|
||||||
|
|
||||||
|
status_ori = str(
|
||||||
|
orientacao.get("status", "")
|
||||||
|
).strip().upper()
|
||||||
|
|
||||||
|
valor_ori_bruto = orientacao.get("valor", 9999.0)
|
||||||
|
valida_redis = orientacao.get("valida", False)
|
||||||
|
|
||||||
|
# Proteção caso o Redis devolva booleano como texto.
|
||||||
|
if isinstance(valida_redis, str):
|
||||||
|
valida_redis = valida_redis.strip().lower() in (
|
||||||
|
"true",
|
||||||
|
"1",
|
||||||
|
"yes",
|
||||||
|
"sim",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
valida_redis = bool(valida_redis)
|
||||||
|
|
||||||
|
try:
|
||||||
|
valor_ori = float(valor_ori_bruto)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
valor_ori = 9999.0
|
||||||
|
|
||||||
|
valor_ori_valido = (
|
||||||
|
math.isfinite(valor_ori)
|
||||||
|
and 0.0 <= valor_ori < 360.0
|
||||||
|
)
|
||||||
|
|
||||||
|
orientacao_recebendo = (
|
||||||
|
freq_ori > FREQ_MIN
|
||||||
|
and not fail_ori
|
||||||
|
)
|
||||||
|
|
||||||
|
posicao_recebendo = (
|
||||||
|
freq_pos > FREQ_MIN
|
||||||
|
and not fail_pos
|
||||||
|
)
|
||||||
|
|
||||||
|
orientacao_valida = (
|
||||||
|
orientacao_recebendo
|
||||||
|
and valida_redis
|
||||||
|
and status_ori == "A"
|
||||||
|
and valor_ori_valido
|
||||||
|
)
|
||||||
|
|
||||||
saude = 100
|
saude = 100
|
||||||
motivos = []
|
motivos = []
|
||||||
|
|
||||||
if not conectado:
|
if not conectado:
|
||||||
motivos.append("desconectado")
|
motivos.append("GNSS desconectado")
|
||||||
saude = 0
|
saude = 0
|
||||||
elif freq_pos <= FREQ_MIN or fail_pos:
|
|
||||||
motivos.append("Sem resposta de coordenadas")
|
elif not posicao_recebendo:
|
||||||
|
motivos.append("Sem resposta de coordenadas GGA")
|
||||||
saude = 0
|
saude = 0
|
||||||
elif freq_ori <= FREQ_MIN or fail_ori:
|
|
||||||
motivos.append("Sem resposta de orientacao")
|
elif not orientacao_recebendo:
|
||||||
|
motivos.append("Sem resposta da sentença de orientação THS")
|
||||||
saude = 0
|
saude = 0
|
||||||
|
|
||||||
else:
|
else:
|
||||||
f_pos = min(freq_pos, FREQ_BASE)
|
f_pos = min(freq_pos, FREQ_BASE)
|
||||||
erro_pos = (FREQ_BASE - f_pos) / FREQ_BASE
|
erro_pos = (FREQ_BASE - f_pos) / FREQ_BASE
|
||||||
|
|
@ -57,6 +107,30 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
||||||
saude -= abs(p_freq_ori)
|
saude -= abs(p_freq_ori)
|
||||||
if abs(p_freq_ori) > 0:
|
if abs(p_freq_ori) > 0:
|
||||||
motivos.append(f"Frequência de orientacao baixa: {f_ori:.2f} Hz (penalidade {p_freq_ori}%)")
|
motivos.append(f"Frequência de orientacao baixa: {f_ori:.2f} Hz (penalidade {p_freq_ori}%)")
|
||||||
|
|
||||||
|
# A sentença está chegando, mas a solução pode estar inválida.
|
||||||
|
if not orientacao_valida:
|
||||||
|
P_ORIENTACAO_INVALIDA = 45
|
||||||
|
saude -= P_ORIENTACAO_INVALIDA
|
||||||
|
|
||||||
|
if status_ori == "V":
|
||||||
|
motivos.append(
|
||||||
|
"Sentença THS recebida, mas solução de heading inválida "
|
||||||
|
f"(status V, penalidade {P_ORIENTACAO_INVALIDA}%)"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif not valor_ori_valido:
|
||||||
|
motivos.append(
|
||||||
|
"Sentença THS recebida com valor de heading inválido "
|
||||||
|
f"({valor_ori}, penalidade {P_ORIENTACAO_INVALIDA}%)"
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
motivos.append(
|
||||||
|
"Sentença THS recebida, mas orientação não foi marcada "
|
||||||
|
f"como válida (penalidade {P_ORIENTACAO_INVALIDA}%)"
|
||||||
|
)
|
||||||
|
|
||||||
# 🔹 Fix
|
# 🔹 Fix
|
||||||
|
|
||||||
p_fix = self._penalidade_fix(fix)
|
p_fix = self._penalidade_fix(fix)
|
||||||
|
|
@ -80,15 +154,22 @@ class ModuloGPS(ModuloDiagnosticoBase):
|
||||||
saude -= p_hacc
|
saude -= p_hacc
|
||||||
motivos.append(f"hAcc {'nao definido' if hAcc == 0 else 'alto'}: {hAcc:.2f} cm (penalidade {p_hacc}%)")
|
motivos.append(f"hAcc {'nao definido' if hAcc == 0 else 'alto'}: {hAcc:.2f} cm (penalidade {p_hacc}%)")
|
||||||
|
|
||||||
saude = max(saude, 0)
|
saude = max(round(saude, 1), 0)
|
||||||
|
|
||||||
status = StatusModulo.OPERANTE
|
|
||||||
if not conectado:
|
if not conectado:
|
||||||
status = StatusModulo.DESCONECTADO
|
status = StatusModulo.DESCONECTADO
|
||||||
|
|
||||||
|
elif not posicao_recebendo or not orientacao_recebendo:
|
||||||
|
status = StatusModulo.FALHA
|
||||||
|
|
||||||
elif saude <= 0:
|
elif saude <= 0:
|
||||||
status = StatusModulo.FALHA
|
status = StatusModulo.FALHA
|
||||||
|
|
||||||
elif saude < SAUDE_MIN_ALERTA:
|
elif saude < SAUDE_MIN_ALERTA:
|
||||||
status = StatusModulo.ALERTA
|
status = StatusModulo.ALERTA
|
||||||
|
|
||||||
|
else:
|
||||||
|
status = StatusModulo.OPERANTE
|
||||||
|
|
||||||
saude_geral = {
|
saude_geral = {
|
||||||
"conectado": conectado,
|
"conectado": conectado,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue