ajuste no simulador; mapa dinamico; envio de controle do redis para o controle c#; troca do modo direcional; blindado trajetoria; tempo aguardando
This commit is contained in:
parent
4344cbf14a
commit
2936df66fa
|
|
@ -4,11 +4,13 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using static AgroBase.Models.Enums;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AgroBase.Forms
|
||||
{
|
||||
|
|
@ -31,6 +33,15 @@ namespace AgroBase.Forms
|
|||
|
||||
List<MPCSimulacaoModel> simulacao = new List<MPCSimulacaoModel>();
|
||||
|
||||
|
||||
private int _atualizacaoTelaPendente;
|
||||
private int _tickEmExecucao;
|
||||
|
||||
private readonly Stopwatch _cronometroTela = Stopwatch.StartNew();
|
||||
|
||||
private const int IntervaloTelaMs = 100; // 10 FPS para a UI
|
||||
|
||||
|
||||
public frmSimulacaoMapaGPS()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
|
@ -70,58 +81,96 @@ namespace AgroBase.Forms
|
|||
private void frmSimulacaoMapaGPS_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
tmrLeitura?.Dispose();
|
||||
MapaDinamico?.Dispose();
|
||||
Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.OperacaoIniciada = false;
|
||||
}
|
||||
|
||||
DateTime t0 = DateTime.Now;
|
||||
private async Task tmrLeitura_Tick()
|
||||
{
|
||||
double latencia_tick = (DateTime.Now - t0).TotalSeconds;
|
||||
t0 = DateTime.Now;
|
||||
int.TryParse(txtTempoGPS.Text, out int tempo);
|
||||
if (tempo > 0)
|
||||
if (Interlocked.Exchange(ref _tickEmExecucao, 1) == 1)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
tempoGps = tempo;
|
||||
int tempo = 0;
|
||||
bool automatico = false;
|
||||
|
||||
// Esses componentes pertencem à UI.
|
||||
if (!IsDisposed && IsHandleCreated)
|
||||
{
|
||||
Invoke(new Action(() =>
|
||||
{
|
||||
int.TryParse(txtTempoGPS.Text, out tempo);
|
||||
automatico = chbAutomatico.Checked;
|
||||
}));
|
||||
}
|
||||
|
||||
if (tempo > 0)
|
||||
tempoGps = tempo;
|
||||
|
||||
if (automatico)
|
||||
{
|
||||
// Temporariamente mantém o passo na UI porque o método ainda
|
||||
// acessa vários TextBox diretamente.
|
||||
Invoke(new Action(() =>
|
||||
{
|
||||
btnAcrescentarGPS_Click(
|
||||
btnAcrescentarGPS,
|
||||
EventArgs.Empty
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
if (_cronometroTela.ElapsedMilliseconds >= IntervaloTelaMs)
|
||||
{
|
||||
SolicitarAtualizacaoTela();
|
||||
_cronometroTela.Restart();
|
||||
}
|
||||
|
||||
// Não reduza para 1 ms quando estiver atrasado.
|
||||
tmrLeitura.SetInterval(Math.Max((int)tempoGps, 10));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref _tickEmExecucao, 0);
|
||||
}
|
||||
|
||||
if (!AtualizandoTela)
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
private void SolicitarAtualizacaoTela()
|
||||
{
|
||||
if (IsDisposed || !IsHandleCreated)
|
||||
return;
|
||||
|
||||
if (Interlocked.Exchange(ref _atualizacaoTelaPendente, 1) == 1)
|
||||
return;
|
||||
|
||||
BeginInvoke(new Action(() =>
|
||||
{
|
||||
Func<bool> funcAtt = new Func<bool>(() =>
|
||||
try
|
||||
{
|
||||
AtualizarDadosTela();
|
||||
return true;
|
||||
});
|
||||
Task.Run(() => FuncoesGlobais.ExecutarMetodoComVerificacaoCrossThread(this, funcAtt));
|
||||
}
|
||||
|
||||
if (chbAutomatico.Checked)
|
||||
{
|
||||
btnAcrescentarGPS_Click(btnAcrescentarGPS, new EventArgs());
|
||||
}
|
||||
|
||||
DateTime t1 = DateTime.Now;
|
||||
double latencia_exec = (t1 - t0).TotalSeconds;
|
||||
double frequencia_exec = 1.0 / Math.Max(latencia_exec, 1e-6);
|
||||
double frequencia_tick = 1.0 / Math.Max(latencia_tick, 1e-6);
|
||||
//Console.WriteLine($"Tempo por tick GPS: {Math.Round(latencia_exec, 4)} s, Frequencia: {Math.Round(frequencia_exec, 2)} Hz | Tempo entre tick GPS: {Math.Round(latencia_tick, 4)}, Frequencia: {Math.Round(frequencia_tick, 2)} Hz");
|
||||
double novoDelay = Math.Max(tempoGps - (latencia_exec * 1000.0), 1);
|
||||
tmrLeitura.SetInterval((int)novoDelay);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Variaveis.MostrarLog(
|
||||
"[Simulacao] Erro ao atualizar tela: " + ex
|
||||
);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref _atualizacaoTelaPendente, 0);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
bool AtualizandoTela = false;
|
||||
private void AtualizarDadosTela()
|
||||
{
|
||||
if (AtualizandoTela)
|
||||
return;
|
||||
AtualizandoTela = true;
|
||||
|
||||
var _Controle = Variaveis.OperacaoEmAndamento.Controle;
|
||||
var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento;
|
||||
var _Trajetoria = Variaveis.OperacaoEmAndamento.Trajetoria;
|
||||
|
||||
if (_Trajetoria == null)
|
||||
{
|
||||
AtualizandoTela = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -132,13 +181,11 @@ namespace AgroBase.Forms
|
|||
if (_Trajetoria == null)
|
||||
{
|
||||
Variaveis.OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas();
|
||||
AtualizandoTela = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Sensoriamento.Trajetoria == null)
|
||||
{
|
||||
AtualizandoTela = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -202,8 +249,6 @@ namespace AgroBase.Forms
|
|||
|
||||
Variaveis.OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas();
|
||||
}
|
||||
|
||||
AtualizandoTela = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -239,20 +284,36 @@ namespace AgroBase.Forms
|
|||
|
||||
private void AtualizarPosicaoGPS()
|
||||
{
|
||||
GPSService.PenultimaLeitura = new GPSModel()
|
||||
{
|
||||
DataHora = GPSService.UltimaLeitura.DataHora,
|
||||
Longitude = GPSService.UltimaLeitura.Longitude,
|
||||
LongitudeAnt = GPSService.UltimaLeitura.LongitudeAnt,
|
||||
Latitude = GPSService.UltimaLeitura.Latitude,
|
||||
LatitudeAnt = GPSService.UltimaLeitura.LatitudeAnt,
|
||||
Heartbeat = GPSService.UltimaLeitura.Heartbeat,
|
||||
OrientacaoReal = GPSService.UltimaLeitura.OrientacaoReal
|
||||
};
|
||||
|
||||
if (!GPSService.Iniciado || (txtLongitude.Text != "" && txtLatitude.Text != ""))
|
||||
{
|
||||
GPSService.UltimaLeitura = posicaoAtual;
|
||||
double orientacaoReal = GPSUtils.NormalizarAngulo(posicaoAtual.OrientacaoReal);
|
||||
|
||||
GPSService.UltimaLeitura.Momento = posicaoAtual.Momento;
|
||||
GPSService.UltimaLeitura.DataHora = posicaoAtual.DataHora;
|
||||
GPSService.UltimaLeitura.UltimoComandoRespondido = posicaoAtual.UltimoComandoRespondido;
|
||||
|
||||
GPSService.UltimaLeitura.Latitude = posicaoAtual.Latitude;
|
||||
GPSService.UltimaLeitura.Longitude = posicaoAtual.Longitude;
|
||||
GPSService.UltimaLeitura.LatitudeAnt = posicaoAtual.LatitudeAnt;
|
||||
GPSService.UltimaLeitura.LongitudeAnt = posicaoAtual.LongitudeAnt;
|
||||
|
||||
GPSService.UltimaLeitura.OrientacaoReal = orientacaoReal;
|
||||
GPSService.UltimaLeitura.AnguloCarroDefinido = orientacaoReal;
|
||||
GPSService.UltimaLeitura.TipoOrientacao = "A";
|
||||
|
||||
GPSService.UltimaLeitura.OrientacaoMovimento =
|
||||
double.IsNaN(posicaoAtual.OrientacaoMovimento) || double.IsInfinity(posicaoAtual.OrientacaoMovimento)
|
||||
? orientacaoReal
|
||||
: GPSUtils.NormalizarAngulo(posicaoAtual.OrientacaoMovimento);
|
||||
|
||||
GPSService.UltimaLeitura.Distancia = posicaoAtual.Distancia;
|
||||
GPSService.UltimaLeitura.Velocidade = posicaoAtual.Velocidade;
|
||||
|
||||
GPSService.UltimaLeitura.TimestampOri = posicaoAtual.TimestampOri.Clone();
|
||||
GPSService.UltimaLeitura.TimestampPos = posicaoAtual.TimestampPos.Clone();
|
||||
|
||||
GPSService.UltimaLeitura.Heartbeat = posicaoAtual.Heartbeat;
|
||||
GPSService.UltimaLeitura.Inicializado = true;
|
||||
}
|
||||
|
||||
GPSService.AtualizarAtrasoPosicoes(Convert.ToInt32(txtDelayPosicao.Text));
|
||||
|
|
@ -277,7 +338,7 @@ namespace AgroBase.Forms
|
|||
// Obtém os parâmetros
|
||||
double anguloAtual = double.Parse(txtAnguloGPS.Text);
|
||||
|
||||
var _Gps = Variaveis.OperacaoEmAndamento.Sensoriamento.Gps;
|
||||
var _Gps = GPSService.UltimaLeitura;
|
||||
|
||||
double anguloControle = 0.0;
|
||||
TipoMovimentoDirecional tipoMovimento = Variaveis.OperacaoEmAndamento.Controle.TipoMovimento;
|
||||
|
|
@ -328,8 +389,8 @@ namespace AgroBase.Forms
|
|||
txtAnguloGPS.Text = posicaoAtual.OrientacaoReal.ToString("0.00");
|
||||
txtVelocidade.Text = veloccidade.ToString("0.00");
|
||||
txtDistanciaGPS.Text = posicaoAtual.Distancia.ToString("0.0000");
|
||||
txtLatitude.Text = posicaoAtual.Latitude.ToString();
|
||||
txtLongitude.Text = posicaoAtual.Longitude.ToString();
|
||||
txtLatitude.Text = posicaoAtual.LatitudeAnt.ToString();
|
||||
txtLongitude.Text = posicaoAtual.LongitudeAnt.ToString();
|
||||
|
||||
UltimaAtualizacaoGps = DateTime.Now;
|
||||
|
||||
|
|
@ -570,7 +631,7 @@ namespace AgroBase.Forms
|
|||
Heartbeat = GPSService.UltimaLeitura.Heartbeat,
|
||||
OrientacaoReal = double.Parse(txtAnguloGPS.Text.Replace(".", ",")),
|
||||
};
|
||||
GPSService.EnviarCoordenadasParaMapa(VariaveisOperacao.PosicaoBase.Latitude, VariaveisOperacao.PosicaoBase.Longitude, VariaveisOperacao.PosicaoBase.AnguloCarroDefinido, false, 0x01);
|
||||
GPSService.EnviarCoordenadasParaMapa(VariaveisOperacao.PosicaoBase.Latitude, VariaveisOperacao.PosicaoBase.Longitude, VariaveisOperacao.PosicaoBase.AnguloCarroDefinido, false, 0x02);
|
||||
}
|
||||
|
||||
private void btnLiberarCorredor_Click(object sender, EventArgs e)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -554,7 +554,6 @@ namespace AgroBase.Models
|
|||
};
|
||||
op.Controle = new OperacaoControleModel()
|
||||
{
|
||||
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras,
|
||||
Angulo = 0,
|
||||
EmFreio = false,
|
||||
PercentualVelocidadeSP = 0,
|
||||
|
|
@ -615,7 +614,7 @@ namespace AgroBase.Models
|
|||
DirAuxilioSonar = true,
|
||||
MovAuxilioSonar = true,
|
||||
MpcHorizonte = 4.0,
|
||||
AnteciparManobraCorredorM = 10.0,
|
||||
AnteciparManobraCorredorM = 0.0,
|
||||
|
||||
MovVelocidadeSErvasPercent = 50,
|
||||
MovVelocidadeCErvasPercent = 20,
|
||||
|
|
@ -695,7 +694,6 @@ namespace AgroBase.Models
|
|||
};
|
||||
op.Controle = new OperacaoControleModel()
|
||||
{
|
||||
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras,
|
||||
Angulo = 0,
|
||||
EmFreio = false,
|
||||
PercentualVelocidadeSP = 0,
|
||||
|
|
@ -836,7 +834,6 @@ namespace AgroBase.Models
|
|||
};
|
||||
op.Controle = new OperacaoControleModel()
|
||||
{
|
||||
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras,
|
||||
Angulo = 0,
|
||||
EmFreio = false,
|
||||
PercentualVelocidadeSP = 0,
|
||||
|
|
@ -894,7 +891,7 @@ namespace AgroBase.Models
|
|||
DirAuxilioSonar = true,
|
||||
MovAuxilioSonar = true,
|
||||
MpcHorizonte = 4.0,
|
||||
AnteciparManobraCorredorM = 10.0,
|
||||
AnteciparManobraCorredorM = 0.0,
|
||||
|
||||
MovVelocidadeSErvasPercent = 40,
|
||||
MovVelocidadeCErvasPercent = 20,
|
||||
|
|
@ -944,7 +941,6 @@ namespace AgroBase.Models
|
|||
};
|
||||
op.Controle = new OperacaoControleModel()
|
||||
{
|
||||
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras,
|
||||
Angulo = 0,
|
||||
EmFreio = false,
|
||||
PercentualVelocidadeSP = 0,
|
||||
|
|
@ -1059,7 +1055,6 @@ namespace AgroBase.Models
|
|||
op.Parametros.ParametrosMandatorios = new List<OperacaoParametrosMandatoriosModel>();
|
||||
op.Controle = new OperacaoControleModel()
|
||||
{
|
||||
TipoMovimento = TipoMovimentoDirecional.RodasDianteiras,
|
||||
Angulo = 0,
|
||||
EmFreio = false,
|
||||
PercentualVelocidadeSP = 0,
|
||||
|
|
@ -1093,7 +1088,7 @@ namespace AgroBase.Models
|
|||
break;
|
||||
|
||||
}
|
||||
|
||||
op.Controle.DefinirTipoMovimento(TipoMovimentoDirecional.RodasDianteiras, op.DispMvd?.Dados?.Modulos);
|
||||
op.Parametros.ModulosMandatorios.ForEach(m => m.ComponentesEmUso = op.PreencherComponentesEmUso(m.Dispositivo));
|
||||
if (CarregarModulos)
|
||||
{
|
||||
|
|
@ -1799,40 +1794,10 @@ namespace AgroBase.Models
|
|||
|
||||
var dispMvd = DispMvd;
|
||||
|
||||
if (dispMvd?.Dados?.Modulos != null && value != Controle.TipoMovimento)
|
||||
if (dispMvd?.Dados?.Modulos != null) // && value != Controle.TipoMovimento
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case TipoMovimentoDirecional.RodasDianteiras:
|
||||
dispMvd.Dados.Modulos.ForEach(x =>
|
||||
{
|
||||
x.MovMotor.Comandar = true;
|
||||
x.DirMotor.Comandar = x.Modulo_ID.Contains("F");
|
||||
x.DirMotor.Angulo_SP = x.DirMotor.Comandar ? Controle.Angulo : 0;
|
||||
});
|
||||
break;
|
||||
|
||||
case TipoMovimentoDirecional.RodasTraseiras:
|
||||
dispMvd.Dados.Modulos.ForEach(x =>
|
||||
{
|
||||
x.MovMotor.Comandar = true;
|
||||
x.DirMotor.Comandar = x.Modulo_ID.Contains("T");
|
||||
x.DirMotor.Angulo_SP = x.DirMotor.Comandar ? Controle.Angulo : 0;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
dispMvd.Dados.Modulos.ForEach(x =>
|
||||
{
|
||||
x.MovMotor.Comandar = true;
|
||||
x.DirMotor.Comandar = true;
|
||||
x.DirMotor.Angulo_SP = Controle.Angulo;
|
||||
});
|
||||
break;
|
||||
}
|
||||
Controle.DefinirTipoMovimento(value, dispMvd.Dados.Modulos);
|
||||
}
|
||||
|
||||
Controle.TipoMovimento = value;
|
||||
}
|
||||
|
||||
public void AtualizaInformacoesControleOperacao()
|
||||
|
|
@ -1958,7 +1923,7 @@ namespace AgroBase.Models
|
|||
//Variaveis.MostrarLog($"Angulo SP alterado de {_ControleAnterior.Angulo:F2} para {_Controle.Angulo:F2}");
|
||||
|
||||
_ControleAnterior.Angulo = _Controle.Angulo;
|
||||
_ControleAnterior.TipoMovimento = _Controle.TipoMovimento;
|
||||
_ControleAnterior.DefinirTipoMovimento(_Controle.TipoMovimento);
|
||||
|
||||
RedisService.AtualizarCampos(
|
||||
CtxKey.DadosControle,
|
||||
|
|
@ -3640,7 +3605,45 @@ namespace AgroBase.Models
|
|||
}
|
||||
public bool EmFreio { get; set; } = false;
|
||||
public double AlturaBarra { get; set; }
|
||||
public TipoMovimentoDirecional TipoMovimento { get; set; } = TipoMovimentoDirecional.Diagnostico;
|
||||
[JsonProperty]
|
||||
public TipoMovimentoDirecional TipoMovimento { get; private set; } = TipoMovimentoDirecional.Diagnostico;
|
||||
public void DefinirTipoMovimento(TipoMovimentoDirecional value, List<ModuloMvdModel> modulos = null)
|
||||
{
|
||||
if (modulos != null)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case TipoMovimentoDirecional.RodasDianteiras:
|
||||
modulos.ForEach(x =>
|
||||
{
|
||||
x.MovMotor.Comandar = true;
|
||||
x.DirMotor.Comandar = x.Modulo_ID.Contains("F");
|
||||
x.DirMotor.Angulo_SP = x.DirMotor.Comandar ? Angulo : 0;
|
||||
});
|
||||
break;
|
||||
|
||||
case TipoMovimentoDirecional.RodasTraseiras:
|
||||
modulos.ForEach(x =>
|
||||
{
|
||||
x.MovMotor.Comandar = true;
|
||||
x.DirMotor.Comandar = x.Modulo_ID.Contains("T");
|
||||
x.DirMotor.Angulo_SP = x.DirMotor.Comandar ? Angulo : 0;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
modulos.ForEach(x =>
|
||||
{
|
||||
x.MovMotor.Comandar = true;
|
||||
x.DirMotor.Comandar = true;
|
||||
x.DirMotor.Angulo_SP = Angulo;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TipoMovimento = value;
|
||||
}
|
||||
public List<OperacaoControleTipoModel> TiposControle { get; set; } = new List<OperacaoControleTipoModel>();
|
||||
public List<MPCSimulacaoModel> SimulacaoMPC { get; set; } = new List<MPCSimulacaoModel>();
|
||||
public double Latencia { get; set; }
|
||||
|
|
@ -3648,6 +3651,10 @@ namespace AgroBase.Models
|
|||
public Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel> DebugCustoMpc { get; set; } = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>();
|
||||
public List<string> Motivos { get; set; } = new List<string>();
|
||||
|
||||
public string UltimaSessaoComando { get; set; }
|
||||
public long? UltimaSeqMovimentoAplicada { get; set; }
|
||||
public long? UltimaSeqDirecionalAplicada { get; set; }
|
||||
|
||||
public void Reiniciar()
|
||||
{
|
||||
PercentualVelocidadeSP = 0;
|
||||
|
|
@ -3668,6 +3675,8 @@ namespace AgroBase.Models
|
|||
|
||||
Angulo = Angulo,
|
||||
PercentualVelocidadeSP = PercentualVelocidadeSP,
|
||||
_pctVelSp = _pctVelSp,
|
||||
TipoMovimento = TipoMovimento,
|
||||
AlturaBarra = AlturaBarra,
|
||||
heartbeat = heartbeat,
|
||||
ticks_sem_resposta = ticks_sem_resposta,
|
||||
|
|
@ -3687,7 +3696,11 @@ namespace AgroBase.Models
|
|||
}).ToList() ?? new List<OperacaoControleTipoModel>(),
|
||||
SimulacaoMPC = new List<MPCSimulacaoModel>(SimulacaoMPC ?? new List<MPCSimulacaoModel>()),
|
||||
DebugCustoMpc = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>(DebugCustoMpc ?? new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>()),
|
||||
Motivos = new List<string>(Motivos ?? new List<string>())
|
||||
Motivos = new List<string>(Motivos ?? new List<string>()),
|
||||
|
||||
UltimaSessaoComando = UltimaSessaoComando,
|
||||
UltimaSeqMovimentoAplicada = UltimaSeqMovimentoAplicada,
|
||||
UltimaSeqDirecionalAplicada = UltimaSeqDirecionalAplicada
|
||||
};
|
||||
|
||||
return clone;
|
||||
|
|
@ -3787,19 +3800,33 @@ namespace AgroBase.Models
|
|||
tempoDecorridoSeg = Math.Max(0, (fim - Operacao.DataInicio).TotalSeconds);
|
||||
}
|
||||
|
||||
double tempoAguardandoRaw = RedisService.GetField<double>(CtxKey.DadosOperacao, "tempo_aguardando", 0.0);
|
||||
double tempoAguardandoInicioTs = RedisService.GetField<double>(CtxKey.DadosOperacao, "tempo_aguardando", 0.0);
|
||||
double tempoAguardarInicio = RedisService.GetField<double>(CtxKey.DadosOperacao, "tempo_aguardar_inicio_operacao", op.TempoIniciarOperacao);
|
||||
double agoraUnix = DateTimeOffset.Now.ToUnixTimeMilliseconds() / 1000.0;
|
||||
double tempoAguardandoSeg;
|
||||
if (tempoAguardandoRaw > 1_000_000_000)
|
||||
double agoraUnix = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
|
||||
double tempoRestanteInicioSeg = 0.0;
|
||||
if (tempoAguardandoInicioTs > 1_000_000_000)
|
||||
{
|
||||
// Redis está mandando timestamp Unix de início da espera.
|
||||
tempoAguardandoSeg = Math.Max(0.0, agoraUnix - tempoAguardandoRaw);
|
||||
/*
|
||||
* tempo_aguardando é o timestamp Unix
|
||||
* de quando a espera começou.
|
||||
*/
|
||||
double tempoDecorridoEsperaSeg = Math.Max(0.0, agoraUnix - tempoAguardandoInicioTs);
|
||||
tempoRestanteInicioSeg = Math.Max(0.0, tempoAguardarInicio - tempoDecorridoEsperaSeg);
|
||||
}
|
||||
else if (tempoAguardandoInicioTs > 0.0)
|
||||
{
|
||||
/*
|
||||
* Compatibilidade com formato antigo:
|
||||
* considera o valor como tempo já decorrido.
|
||||
*/
|
||||
tempoRestanteInicioSeg = Math.Max(0.0, tempoAguardarInicio - tempoAguardandoInicioTs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compatibilidade: caso algum fluxo antigo mande duração em segundos.
|
||||
tempoAguardandoSeg = Math.Max(0.0, tempoAguardandoRaw);
|
||||
/*
|
||||
* Ainda não existe timestamp de espera.
|
||||
*/
|
||||
tempoRestanteInicioSeg = Math.Max(0.0, tempoAguardarInicio);
|
||||
}
|
||||
|
||||
op.TempoIniciarOperacao = Math.Max(0, Convert.ToInt32(Math.Round(tempoAguardarInicio)));
|
||||
|
|
@ -3819,7 +3846,7 @@ namespace AgroBase.Models
|
|||
DataInicio = Operacao.DataInicio,
|
||||
DataFim = Operacao.DataFim,
|
||||
TempoDecorridoSeg = tempoDecorridoSeg,
|
||||
TempoAguardandoSeg = tempoAguardandoSeg,
|
||||
TempoAguardandoSeg = tempoRestanteInicioSeg,
|
||||
TipoCanAtivo = CanManager.TipoServico,
|
||||
|
||||
OperacaoLiberada = RedisService.GetField(CtxKey.DadosOperacao, "liberado", false),
|
||||
|
|
@ -3835,10 +3862,7 @@ namespace AgroBase.Models
|
|||
ModsCalibragemView = Operacao.ModsCalibragem?.ToDictionary(x => x.Key.ToString(), x => new Dictionary<string, bool>(x.Value ?? new Dictionary<string, bool>())) ?? new Dictionary<string, Dictionary<string, bool>>(),
|
||||
};
|
||||
|
||||
if (operacaoAtualizada.StatusOperacaoAtual != Operacao.StatusOperacaoAnterior)
|
||||
{
|
||||
AudioAlertaService.AlertaSonoroPorStatus(operacaoAtualizada.StatusOperacaoAtual);
|
||||
}
|
||||
bool statusMudou = operacaoAtualizada.StatusOperacaoAtual != Operacao.StatusOperacaoAnterior;
|
||||
operacaoAtualizada.StatusOperacaoAnterior = operacaoAtualizada.StatusOperacaoAtual;
|
||||
|
||||
var saude = OperadorSaude.ModulosSaude;
|
||||
|
|
@ -3877,6 +3901,11 @@ namespace AgroBase.Models
|
|||
StatusModulo.Operante;
|
||||
|
||||
Operacao = operacaoAtualizada;
|
||||
|
||||
if (statusMudou)
|
||||
{
|
||||
AudioAlertaService.AlertaSonoroPorStatus(operacaoAtualizada.StatusOperacaoAtual);
|
||||
}
|
||||
}
|
||||
|
||||
// Atu
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ namespace AgroBase.Models.Operadores
|
|||
public int versao_comando { get; set; } = 1;
|
||||
public double ts_comando { get; set; } = 0;
|
||||
|
||||
public string sessao_comando { get; set; }
|
||||
|
||||
public long? seq_movimento { get; set; }
|
||||
public long? seq_direcional { get; set; }
|
||||
|
||||
public bool atualizar_movimento { get; set; } = true;
|
||||
public bool atualizar_direcional { get; set; } = true;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -27,7 +27,7 @@ namespace AgroBase.Models
|
|||
{
|
||||
public class Variaveis
|
||||
{
|
||||
public static readonly bool IniciarWorkers = true;
|
||||
public static readonly bool IniciarWorkers = false;
|
||||
public static readonly bool Producao = false;
|
||||
public static bool DebugMode { get; set; } = true;
|
||||
public static bool Fechando { get; set; } = false;
|
||||
|
|
@ -859,8 +859,8 @@ namespace AgroBase.Models
|
|||
public static string TopicoMqttParametros { get; } = $"agrobot/v1/rover/<id>/parameters";
|
||||
public static double LarguraEsquerda { get; } = 44.0; // 62
|
||||
public static double LarguraDireita { get; } = 44.0; // 22
|
||||
public static double ComprimentoFrente { get; } = 7.0; // 7
|
||||
public static double ComprimentoTras { get; } = 107.0; // 107
|
||||
public static double ComprimentoFrente { get; } = 90.0; // 7
|
||||
public static double ComprimentoTras { get; } = 40.0; // 107
|
||||
public static double DistanciaEntreEixos { get; } = 92.0;
|
||||
public static double LeverArmFrontalCm { get; } = 37.0; // cm
|
||||
public static double LeverArmLateralCm { get; } = 0.0; // cm
|
||||
|
|
@ -882,7 +882,7 @@ namespace AgroBase.Models
|
|||
public static int QuantidadeBicosPulverizadores { get; set; } = 7;
|
||||
public static double AlturaBarraPulverizadoraCm { get; set; } = 80;
|
||||
public static double ComprimentoBarraPulverizadoraCm { get; set; } = 103.7;
|
||||
public static double DistanciaEntreBicosCm { get; set; } = 50;
|
||||
public static double DistanciaEntreBicosCm { get; set; } = 21;
|
||||
public static double DensidadeHerbicida { get; set; } = 1.0;
|
||||
public static double CapacidadeReservatorio { get; set; } = 60;
|
||||
public static double PressaoMinimaCavitacao { get; set; } = 10.0;
|
||||
|
|
@ -962,8 +962,8 @@ namespace AgroBase.Models
|
|||
{ TipoMovimentoDirecional.MovimentoLateral, 90 },
|
||||
{ TipoMovimentoDirecional.Diagnostico, 25 },
|
||||
};
|
||||
public static double AnguloInclinacaoRollMax { get; set; } = 30.0; // Frontal
|
||||
public static double AnguloInclinacaoPitchMax { get; set; } = 45.0; // Lateral
|
||||
public static double AnguloInclinacaoRollMax { get; set; } = 45.0; // Frontal
|
||||
public static double AnguloInclinacaoPitchMax { get; set; } = 25.0; // Lateral
|
||||
public static string VozAlerta { get; set; } = "Microsoft Maria Desktop";
|
||||
|
||||
|
||||
|
|
@ -1170,32 +1170,51 @@ namespace AgroBase.Models
|
|||
double dLat = (dy / R_earth) * 180.0 / Math.PI;
|
||||
double dLon = (dx / (R_earth * Math.Cos(pos.Latitude * Math.PI / 180.0))) * 180.0 / Math.PI;
|
||||
|
||||
double latitude = pos.Latitude + dLat;
|
||||
double longitude = pos.Longitude + dLon;
|
||||
double latitude = pos.LatitudeAnt + dLat;
|
||||
double longitude = pos.LongitudeAnt + dLon;
|
||||
|
||||
DateTime agora = DateTime.Now;
|
||||
double agoraMono = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
||||
|
||||
double orientacaoRealDeg = GPSUtils.NormalizarAngulo(theta * 180.0 / Math.PI);
|
||||
double orientacaoMovimentoDeg = GPSUtils.NormalizarAngulo(heading * 180.0 / Math.PI);
|
||||
|
||||
GPSModel novaPosicao = new GPSModel
|
||||
{
|
||||
Momento = DateTime.Now,
|
||||
Momento = agora,
|
||||
DataHora = agora,
|
||||
UltimoComandoRespondido = agora,
|
||||
Lat0 = GPSService.UltimaLeitura.Lat0,
|
||||
Lon0 = GPSService.UltimaLeitura.Lon0,
|
||||
Latitude = latitude,
|
||||
LatitudeAnt = latitude,
|
||||
Longitude = longitude,
|
||||
LongitudeAnt = longitude,
|
||||
OrientacaoReal = GPSUtils.NormalizarAngulo(theta * 180.0 / Math.PI),
|
||||
OrientacaoReal = orientacaoRealDeg,
|
||||
AnguloCarroDefinido = orientacaoRealDeg,
|
||||
OrientacaoMovimento = orientacaoMovimentoDeg,
|
||||
TipoOrientacao = "A",
|
||||
Distancia = velocidadeMs * tempoDelta,
|
||||
Velocidade = velocidadeMs,
|
||||
Heartbeat = ultimaPosicao.Heartbeat,
|
||||
Heartbeat = ultimaPosicao.Heartbeat + 1,
|
||||
TimestampOri = ultimaPosicao.TimestampOri.Clone(),
|
||||
TimestampPos = ultimaPosicao.TimestampPos.Clone(),
|
||||
};
|
||||
|
||||
var corrigida =
|
||||
GPSService.LeverArm.FixLeverArmLatLon_Fast(
|
||||
novaPosicao.LatitudeAnt,
|
||||
novaPosicao.LongitudeAnt,
|
||||
novaPosicao.OrientacaoReal,
|
||||
5
|
||||
);
|
||||
novaPosicao.Latitude = corrigida.lat;
|
||||
novaPosicao.Longitude = corrigida.lon;
|
||||
|
||||
//(double latCor, double lonCorr) = GeoLeverArm.FixLeverArmLatLon_Fast(latitude, longitude, novaPosicao.OrientacaoReal);
|
||||
//novaPosicao.Latitude = latCor;
|
||||
//novaPosicao.Longitude = lonCorr;
|
||||
|
||||
novaPosicao.TimestampOri.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
||||
novaPosicao.TimestampPos.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
||||
novaPosicao.TimestampOri.valor = agoraMono;
|
||||
novaPosicao.TimestampPos.valor = agoraMono;
|
||||
|
||||
return novaPosicao;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ namespace AgroBase.Services
|
|||
// ------- helpers locais -------
|
||||
string SegundosFmt(double s)
|
||||
{
|
||||
int v = (int)Math.Round(s);
|
||||
int v = Math.Max(0, (int)Math.Ceiling(s));
|
||||
return v == 1 ? "1 segundo" : $"{v} segundos";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2189,28 +2189,15 @@ namespace AgroBase.Services
|
|||
|
||||
AtualizarTrajetoriaDinamica();
|
||||
|
||||
int enderecoEquipamento = 1;
|
||||
int enderecoEquipamento = 0x02;
|
||||
|
||||
EnviarCoordenadasParaMapa(
|
||||
UltimaLeitura.Latitude,
|
||||
UltimaLeitura.Longitude,
|
||||
UltimaLeitura.AnguloCarroDefinido,
|
||||
op.Sensoriamento.Operacao.OperacaoIniciada,
|
||||
enderecoEquipamento
|
||||
);
|
||||
EnviarCoordenadasParaMapa(UltimaLeitura.Latitude, UltimaLeitura.Longitude, UltimaLeitura.AnguloCarroDefinido, op.Sensoriamento.Operacao.OperacaoIniciada, enderecoEquipamento);
|
||||
|
||||
GPSModel posicaoBase = VariaveisOperacao.ObterPosicaoBaseSnapshot();
|
||||
if ((posicaoBase?.UltimoComandoRespondido ?? DateTime.MinValue) > DateTime.UtcNow.AddSeconds(-60))
|
||||
{
|
||||
int enderecoBase = 0;
|
||||
|
||||
EnviarCoordenadasParaMapa(
|
||||
posicaoBase.Latitude,
|
||||
posicaoBase.Longitude,
|
||||
posicaoBase.OrientacaoReal,
|
||||
false,
|
||||
enderecoBase
|
||||
);
|
||||
int enderecoBase = 0x01;
|
||||
EnviarCoordenadasParaMapa(posicaoBase.Latitude, posicaoBase.Longitude, posicaoBase.OrientacaoReal, false, enderecoBase);
|
||||
}
|
||||
|
||||
PenultimaLeitura.Inicializado = UltimaLeitura.Inicializado;
|
||||
|
|
@ -2375,23 +2362,10 @@ namespace AgroBase.Services
|
|||
{
|
||||
if (UltimasLeituras.Count < 2)
|
||||
{
|
||||
double ang =
|
||||
GPSUtils.CalcularOrientacao(
|
||||
PenultimaLeitura,
|
||||
UltimaLeitura
|
||||
);
|
||||
|
||||
double d =
|
||||
GPSUtils.DistanciaEntrePontos(
|
||||
PenultimaLeitura,
|
||||
UltimaLeitura
|
||||
);
|
||||
|
||||
PenultimaLeitura.OrientacaoMovimento =
|
||||
UltimaLeitura.OrientacaoMovimento;
|
||||
|
||||
PenultimaLeitura.Distancia =
|
||||
UltimaLeitura.Distancia;
|
||||
double ang = GPSUtils.CalcularOrientacao(PenultimaLeitura, UltimaLeitura);
|
||||
double d = GPSUtils.DistanciaEntrePontos(PenultimaLeitura, UltimaLeitura);
|
||||
PenultimaLeitura.OrientacaoMovimento = UltimaLeitura.OrientacaoMovimento;
|
||||
PenultimaLeitura.Distancia = UltimaLeitura.Distancia;
|
||||
|
||||
UltimaLeitura.OrientacaoMovimento = ang;
|
||||
UltimaLeitura.Distancia = d;
|
||||
|
|
@ -2402,24 +2376,19 @@ namespace AgroBase.Services
|
|||
double sumY = 0;
|
||||
double distAcum = 0;
|
||||
|
||||
for (int i = 0;
|
||||
i < UltimasLeituras.Count - 1;
|
||||
i++)
|
||||
for (int i = 0; i < UltimasLeituras.Count - 1; i++)
|
||||
{
|
||||
GPSModel a = UltimasLeituras[i];
|
||||
GPSModel b = UltimasLeituras[i + 1];
|
||||
|
||||
double angSeg =
|
||||
GPSUtils.CalcularOrientacao(a, b);
|
||||
double angSeg = GPSUtils.CalcularOrientacao(a, b);
|
||||
|
||||
double dSeg =
|
||||
GPSUtils.DistanciaEntrePontos(a, b);
|
||||
double dSeg = GPSUtils.DistanciaEntrePontos(a, b);
|
||||
|
||||
if (dSeg <= 0)
|
||||
continue;
|
||||
|
||||
double rad =
|
||||
angSeg * Math.PI / 180.0;
|
||||
double rad = angSeg * Math.PI / 180.0;
|
||||
|
||||
sumX += Math.Cos(rad) * dSeg;
|
||||
sumY += Math.Sin(rad) * dSeg;
|
||||
|
|
@ -2430,44 +2399,22 @@ namespace AgroBase.Services
|
|||
|
||||
if (distAcum <= 0)
|
||||
{
|
||||
anguloMovimento =
|
||||
UltimaLeitura.OrientacaoReal;
|
||||
anguloMovimento = UltimaLeitura.OrientacaoReal;
|
||||
}
|
||||
else
|
||||
{
|
||||
anguloMovimento =
|
||||
Math.Atan2(sumY, sumX) *
|
||||
180.0 / Math.PI;
|
||||
|
||||
anguloMovimento =
|
||||
GPSUtils.NormalizarAngulo(
|
||||
anguloMovimento
|
||||
);
|
||||
anguloMovimento = Math.Atan2(sumY, sumX) * 180.0 / Math.PI;
|
||||
anguloMovimento = GPSUtils.NormalizarAngulo(anguloMovimento);
|
||||
}
|
||||
|
||||
GPSModel first =
|
||||
UltimasLeituras[0];
|
||||
GPSModel first = UltimasLeituras[0];
|
||||
GPSModel last = UltimasLeituras[UltimasLeituras.Count - 1];
|
||||
|
||||
GPSModel last =
|
||||
UltimasLeituras[
|
||||
UltimasLeituras.Count - 1
|
||||
];
|
||||
|
||||
double distLinear =
|
||||
GPSUtils.DistanciaEntrePontos(
|
||||
first,
|
||||
last
|
||||
);
|
||||
|
||||
PenultimaLeitura.OrientacaoMovimento =
|
||||
UltimaLeitura.OrientacaoMovimento;
|
||||
|
||||
PenultimaLeitura.Distancia =
|
||||
UltimaLeitura.Distancia;
|
||||
|
||||
UltimaLeitura.OrientacaoMovimento =
|
||||
anguloMovimento;
|
||||
double distLinear = GPSUtils.DistanciaEntrePontos(first, last);
|
||||
|
||||
PenultimaLeitura.OrientacaoMovimento = UltimaLeitura.OrientacaoMovimento;
|
||||
PenultimaLeitura.Distancia = UltimaLeitura.Distancia;
|
||||
UltimaLeitura.OrientacaoMovimento = anguloMovimento;
|
||||
UltimaLeitura.Distancia = distLinear;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,10 @@ namespace AgroBase.Services.Operadores
|
|||
versao_comando = GetInt(dictParams, "versao_comando", 1),
|
||||
ts_comando = GetDouble(dictParams, "ts_comando", 0.0),
|
||||
|
||||
sessao_comando = dictParams.Value<string>("sessao_comando"),
|
||||
seq_movimento = dictParams["seq_movimento"]?.Value<long?>(),
|
||||
seq_direcional = dictParams["seq_direcional"]?.Value<long?>(),
|
||||
|
||||
// Default true para compatibilidade com contrato antigo
|
||||
atualizar_movimento = GetBool(dictParams, "atualizar_movimento", true),
|
||||
atualizar_direcional = GetBool(dictParams, "atualizar_direcional", true),
|
||||
|
|
@ -283,33 +287,80 @@ namespace AgroBase.Services.Operadores
|
|||
|
||||
var op = Variaveis.OperacaoEmAndamento;
|
||||
|
||||
var _Controle = op.Controle;
|
||||
var pControle = op.Parametros.Controle;
|
||||
|
||||
if (_Controle == null || pControle == null)
|
||||
if (op == null)
|
||||
return;
|
||||
|
||||
_Controle.Motivos = (novoComando.motivos ?? new string[0]).ToList();
|
||||
var controle = op.Controle;
|
||||
var parametros = op.Parametros?.Controle;
|
||||
|
||||
bool podeAtualizarMovimento =
|
||||
novoComando.atualizar_movimento &&
|
||||
pControle.MovimentoAutomatico;
|
||||
if (controle == null || parametros == null)
|
||||
return;
|
||||
|
||||
bool podeAtualizarDirecional =
|
||||
novoComando.atualizar_direcional &&
|
||||
pControle.DirecionalAutomatico;
|
||||
controle.Motivos = (novoComando.motivos ?? Array.Empty<string>()).ToList();
|
||||
|
||||
bool movimentoMudou =
|
||||
_Controle.PercentualVelocidadeSP != novoComando.percentual_velocidade ||
|
||||
_Controle.EmFreio != novoComando.em_freio;
|
||||
bool sessaoMudou = !string.IsNullOrWhiteSpace(novoComando.sessao_comando) && !string.Equals(controle.UltimaSessaoComando, novoComando.sessao_comando, StringComparison.Ordinal);
|
||||
|
||||
bool direcionalMudou =
|
||||
_Controle.Angulo != novoComando.angulo ||
|
||||
_Controle.TipoMovimento != novoComando.tipo_movimento;
|
||||
bool movimentoMudou = Math.Abs(controle.PercentualVelocidadeSP - novoComando.percentual_velocidade) >= 0.001 || controle.EmFreio != novoComando.em_freio;
|
||||
|
||||
if (podeAtualizarMovimento && (movimentoMudou || forcarAplicacao))
|
||||
bool direcionalMudou = Math.Abs(controle.Angulo - novoComando.angulo) >= 0.001 || controle.TipoMovimento != novoComando.tipo_movimento;
|
||||
|
||||
bool seqMovimentoMudou =
|
||||
novoComando.seq_movimento.HasValue &&
|
||||
(
|
||||
sessaoMudou ||
|
||||
!controle.UltimaSeqMovimentoAplicada.HasValue ||
|
||||
controle.UltimaSeqMovimentoAplicada.Value !=
|
||||
novoComando.seq_movimento.Value
|
||||
);
|
||||
|
||||
bool seqDirecionalMudou =
|
||||
novoComando.seq_direcional.HasValue &&
|
||||
(
|
||||
sessaoMudou ||
|
||||
!controle.UltimaSeqDirecionalAplicada.HasValue ||
|
||||
controle.UltimaSeqDirecionalAplicada.Value !=
|
||||
novoComando.seq_direcional.Value
|
||||
);
|
||||
|
||||
/*
|
||||
* Compatibilidade:
|
||||
*
|
||||
* Mensagem nova:
|
||||
* sequência é a autoridade.
|
||||
*
|
||||
* Mensagem antiga:
|
||||
* usa flag e mudança de valor.
|
||||
*/
|
||||
bool mensagemPossuiSequenciaMov = novoComando.seq_movimento.HasValue;
|
||||
bool mensagemPossuiSequenciaDir = novoComando.seq_direcional.HasValue;
|
||||
|
||||
bool aplicarMovimento =
|
||||
parametros.MovimentoAutomatico &&
|
||||
(
|
||||
forcarAplicacao ||
|
||||
movimentoMudou ||
|
||||
seqMovimentoMudou ||
|
||||
(
|
||||
!mensagemPossuiSequenciaMov &&
|
||||
novoComando.atualizar_movimento
|
||||
)
|
||||
);
|
||||
|
||||
bool aplicarDirecional =
|
||||
parametros.DirecionalAutomatico &&
|
||||
(
|
||||
forcarAplicacao ||
|
||||
direcionalMudou ||
|
||||
seqDirecionalMudou ||
|
||||
(
|
||||
!mensagemPossuiSequenciaDir &&
|
||||
novoComando.atualizar_direcional
|
||||
)
|
||||
);
|
||||
|
||||
if (aplicarMovimento)
|
||||
{
|
||||
var tipoMov = _Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov);
|
||||
var tipoMov = controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov);
|
||||
|
||||
if (tipoMov != null)
|
||||
{
|
||||
|
|
@ -319,15 +370,21 @@ namespace AgroBase.Services.Operadores
|
|||
: Direcao.Parado;
|
||||
}
|
||||
|
||||
_Controle.PercentualVelocidadeSP = novoComando.percentual_velocidade;
|
||||
_Controle.EmFreio = pControle.FrenagemAutomaticaAoParar && novoComando.em_freio;
|
||||
controle.PercentualVelocidadeSP = novoComando.percentual_velocidade;
|
||||
|
||||
op.AtualizarDadosControle(false, new List<T_Code>() { T_Code.Mov });
|
||||
controle.EmFreio = parametros.FrenagemAutomaticaAoParar && novoComando.em_freio;
|
||||
|
||||
op.AtualizarDadosControle(false, new List<T_Code> { T_Code.Mov });
|
||||
|
||||
if (novoComando.seq_movimento.HasValue)
|
||||
{
|
||||
controle.UltimaSeqMovimentoAplicada = novoComando.seq_movimento.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (podeAtualizarDirecional && (direcionalMudou || forcarAplicacao))
|
||||
if (aplicarDirecional)
|
||||
{
|
||||
var tipoDir = _Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Dir);
|
||||
var tipoDir = controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Dir);
|
||||
|
||||
if (tipoDir != null)
|
||||
{
|
||||
|
|
@ -339,26 +396,38 @@ namespace AgroBase.Services.Operadores
|
|||
: Direcao.Parado;
|
||||
}
|
||||
|
||||
_Controle.Angulo = novoComando.angulo;
|
||||
_Controle.TipoMovimento = novoComando.tipo_movimento;
|
||||
controle.Angulo = novoComando.angulo;
|
||||
|
||||
_Controle.SimulacaoMPC = novoComando.simulacao?
|
||||
.Where(x => x != null && x.Length >= 3)
|
||||
.Select(x => new MPCSimulacaoModel()
|
||||
{
|
||||
latitude = x[0],
|
||||
longitude = x[1],
|
||||
orientacao = x[2]
|
||||
})
|
||||
.ToList() ?? new List<MPCSimulacaoModel>();
|
||||
op.DefinirTipoMovimentoControle(novoComando.tipo_movimento);
|
||||
|
||||
_Controle.Latencia = novoComando.latencia;
|
||||
_Controle.ErroLateral = novoComando.erro_lateral;
|
||||
_Controle.DebugCustoMpc =
|
||||
novoComando.debug_custo ??
|
||||
new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>();
|
||||
controle.SimulacaoMPC = novoComando.simulacao?
|
||||
.Where(x => x != null && x.Length >= 3)
|
||||
.Select(
|
||||
x => new MPCSimulacaoModel
|
||||
{
|
||||
latitude = x[0],
|
||||
longitude = x[1],
|
||||
orientacao = x[2]
|
||||
}
|
||||
)
|
||||
.ToList()
|
||||
?? new List<MPCSimulacaoModel>();
|
||||
|
||||
op.AtualizarDadosControle(false, new List<T_Code>() { T_Code.Dir });
|
||||
controle.Latencia = novoComando.latencia;
|
||||
controle.ErroLateral = novoComando.erro_lateral;
|
||||
controle.DebugCustoMpc = novoComando.debug_custo ?? new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>();
|
||||
|
||||
op.AtualizarDadosControle(false, new List<T_Code> { T_Code.Dir });
|
||||
|
||||
if (novoComando.seq_direcional.HasValue)
|
||||
{
|
||||
controle.UltimaSeqDirecionalAplicada = novoComando.seq_direcional.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(novoComando.sessao_comando))
|
||||
{
|
||||
controle.UltimaSessaoComando = novoComando.sessao_comando;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import time
|
||||
import uuid
|
||||
|
||||
from shared.enums import TipoMovimentoDirecional
|
||||
from shared.contexto_global_redis import ContextoGlobalRedis
|
||||
|
|
@ -16,7 +17,11 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
def __init__(self):
|
||||
self.filtro_mov = FiltroVelocidade(alpha=0.7)
|
||||
|
||||
ang_max = ContextoGlobalRedis.get_controle().get("angulo_max", 30)
|
||||
controle = ContextoGlobalRedis.get_controle() or {}
|
||||
ang_max = self._to_float(
|
||||
controle.get("angulo_max", 30.0),
|
||||
30.0
|
||||
)
|
||||
|
||||
self.pid_dir = PIDAdaptativo(
|
||||
kp_base=0.55,
|
||||
|
|
@ -31,7 +36,14 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
|
||||
agora = time.perf_counter()
|
||||
|
||||
# Cadências independentes
|
||||
# Identifica esta execução do Manager.
|
||||
# Se o Python reiniciar, o C# perceberá uma sessão nova.
|
||||
self.sessao_comando = uuid.uuid4().hex
|
||||
|
||||
# Sequências monotônicas por módulo.
|
||||
self.seq_movimento = 0
|
||||
self.seq_direcional = 0
|
||||
|
||||
self.ultimo_envio_envelope = agora
|
||||
self.ultimo_envio_movimento = agora
|
||||
self.ultimo_envio_direcional = agora
|
||||
|
|
@ -40,81 +52,107 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
self.intervalo_movimento_s = 0.25
|
||||
self.intervalo_direcional_s = 0.30
|
||||
|
||||
# Memória local para evitar publicar movimento idêntico o tempo todo
|
||||
self.ultima_velocidade_enviada = None
|
||||
self.ultimo_freio_enviado = None
|
||||
|
||||
self.ultimo_angulo_enviado = None
|
||||
self.ultimo_tipo_enviado = None
|
||||
|
||||
# Evita spam de log de bloqueio
|
||||
self.ultimo_log_bloqueio = 0.0
|
||||
self.ultima_msg_bloqueio = ""
|
||||
|
||||
self.regras = RegrasTaticas()
|
||||
|
||||
def processar(self):
|
||||
try:
|
||||
agora = time.perf_counter()
|
||||
mostrar_log(
|
||||
"🧭 ProcessadorEmAndamento criado | "
|
||||
f"id={id(self)} | "
|
||||
f"sessao={self.sessao_comando} | "
|
||||
f"perf={agora:.4f}"
|
||||
)
|
||||
|
||||
_controle = ContextoGlobalRedis.get_controle()
|
||||
def processar(self):
|
||||
agora = time.perf_counter()
|
||||
|
||||
try:
|
||||
controle = ContextoGlobalRedis.get_controle() or {}
|
||||
|
||||
movimento_automatico = bool(
|
||||
_controle.get("movimento_automatico", False)
|
||||
controle.get("movimento_automatico", False)
|
||||
)
|
||||
|
||||
direcional_automatico = bool(
|
||||
_controle.get("direcional_automatico", False)
|
||||
controle.get("direcional_automatico", False)
|
||||
)
|
||||
|
||||
# Se nenhum controle automático está ativo, este processador não comanda nada.
|
||||
if not movimento_automatico and not direcional_automatico:
|
||||
return None
|
||||
|
||||
valores_atuais = self._ler_valores_atuais(_controle)
|
||||
estado = self._ler_valores_atuais(controle)
|
||||
|
||||
velocidade = estado["velocidade"]
|
||||
frear = estado["frear"]
|
||||
|
||||
angulo = estado["angulo"]
|
||||
tipo_movimento = estado["tipo_movimento"]
|
||||
|
||||
simulacao = estado["simulacao"]
|
||||
latencia = estado["latencia"]
|
||||
erro_lateral = estado["erro_lateral"]
|
||||
debug_custo = estado["debug_custo"]
|
||||
|
||||
motivos = []
|
||||
erro_geral = False
|
||||
|
||||
velocidade = valores_atuais["velocidade"]
|
||||
frear = valores_atuais["frear"]
|
||||
angulo = valores_atuais["angulo"]
|
||||
tipo_movimento = valores_atuais["tipo_movimento"]
|
||||
simulacao = valores_atuais["simulacao"]
|
||||
latencia = valores_atuais["latencia"]
|
||||
erro_lateral = valores_atuais["erro_lateral"]
|
||||
debug_custo = valores_atuais["debug_custo"]
|
||||
|
||||
atualizar_movimento = False
|
||||
atualizar_direcional = False
|
||||
|
||||
# ============================================================
|
||||
# 1) Regras táticas globais
|
||||
# 1. Bloqueios táticos
|
||||
# ============================================================
|
||||
|
||||
motivos_parada, freio_necessario = self.regras.verifica_controle_liberado()
|
||||
motivos_parada, freio_necessario = (
|
||||
self.regras.verifica_controle_liberado()
|
||||
)
|
||||
|
||||
motivos_parada = motivos_parada or []
|
||||
|
||||
if motivos_parada:
|
||||
self._logar_bloqueio(motivos_parada)
|
||||
|
||||
# Bloqueio global: para movimento e centraliza direcional,
|
||||
# mas respeitando quais módulos estão em automático.
|
||||
atualizar_movimento = movimento_automatico
|
||||
atualizar_direcional = direcional_automatico
|
||||
|
||||
if atualizar_movimento:
|
||||
if movimento_automatico:
|
||||
velocidade = 0.0
|
||||
frear = bool(freio_necessario)
|
||||
|
||||
if atualizar_direcional:
|
||||
atualizar_movimento = (
|
||||
self._movimento_mudou(
|
||||
velocidade,
|
||||
frear
|
||||
)
|
||||
or self._venceu_intervalo_movimento(agora)
|
||||
)
|
||||
|
||||
if direcional_automatico:
|
||||
angulo = 0.0
|
||||
tipo_movimento = TipoMovimentoDirecional.RodasDianteiras.value
|
||||
tipo_movimento = (
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
)
|
||||
|
||||
simulacao = []
|
||||
latencia = 0.0
|
||||
erro_lateral = 0.0
|
||||
debug_custo = {}
|
||||
|
||||
return self._publicar_comando(
|
||||
atualizar_direcional = (
|
||||
self._direcional_mudou(
|
||||
angulo,
|
||||
tipo_movimento
|
||||
)
|
||||
or self._venceu_intervalo_direcional(agora)
|
||||
)
|
||||
|
||||
return self._publicar_se_necessario(
|
||||
agora=agora,
|
||||
velocidade=velocidade,
|
||||
frear=frear,
|
||||
|
|
@ -131,134 +169,69 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
)
|
||||
|
||||
# ============================================================
|
||||
# 2) Movimento automático
|
||||
# 2. Movimento
|
||||
# ============================================================
|
||||
|
||||
if movimento_automatico:
|
||||
comando_mov = definir_comando_mov(self.filtro_mov)
|
||||
comando_mov = definir_comando_mov(
|
||||
self.filtro_mov
|
||||
)
|
||||
|
||||
if isinstance(comando_mov, dict):
|
||||
velocidade_calculada = self._to_float(
|
||||
comando_mov.get("velocidade", velocidade),
|
||||
velocidade
|
||||
)
|
||||
|
||||
freio_calculado = bool(
|
||||
comando_mov.get("frear", False)
|
||||
)
|
||||
|
||||
velocidade = velocidade_calculada
|
||||
frear = freio_calculado
|
||||
|
||||
movimento_mudou = self._movimento_mudou(
|
||||
velocidade,
|
||||
frear
|
||||
)
|
||||
|
||||
envio_movimento_periodico = (
|
||||
agora - self.ultimo_envio_movimento
|
||||
) >= self.intervalo_movimento_s
|
||||
|
||||
atualizar_movimento = (
|
||||
movimento_mudou or
|
||||
envio_movimento_periodico
|
||||
)
|
||||
|
||||
else:
|
||||
# Falha ao calcular movimento automático: falha segura.
|
||||
if not isinstance(comando_mov, dict):
|
||||
erro_geral = True
|
||||
motivos.append("Falha ao definir comando de movimento automático")
|
||||
|
||||
motivos.append(
|
||||
"Falha ao definir comando de movimento automático"
|
||||
)
|
||||
|
||||
velocidade = 0.0
|
||||
frear = True
|
||||
atualizar_movimento = True
|
||||
|
||||
else:
|
||||
velocidade = self._to_float(
|
||||
comando_mov.get(
|
||||
"velocidade",
|
||||
velocidade
|
||||
),
|
||||
velocidade
|
||||
)
|
||||
|
||||
frear = bool(
|
||||
comando_mov.get(
|
||||
"frear",
|
||||
frear
|
||||
)
|
||||
)
|
||||
|
||||
atualizar_movimento = (
|
||||
self._movimento_mudou(
|
||||
velocidade,
|
||||
frear
|
||||
)
|
||||
or self._venceu_intervalo_movimento(agora)
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# 3) Direcional automático
|
||||
# 3. Direcional
|
||||
# ============================================================
|
||||
|
||||
if direcional_automatico:
|
||||
envio_direcional_necessario = (
|
||||
agora - self.ultimo_envio_direcional
|
||||
) >= self.intervalo_direcional_s
|
||||
envio_periodico_necessario = (
|
||||
self._venceu_intervalo_direcional(agora)
|
||||
)
|
||||
|
||||
comando_dir = definir_comando_dir(
|
||||
self.pid_dir,
|
||||
envio_direcional_necessario
|
||||
envio_periodico_necessario
|
||||
)
|
||||
|
||||
if isinstance(comando_dir, dict):
|
||||
erro_dir = bool(comando_dir.get("erro", False))
|
||||
parada_necessaria = bool(
|
||||
comando_dir.get("parada_necessaria", False)
|
||||
)
|
||||
|
||||
if erro_dir:
|
||||
erro_geral = True
|
||||
motivos.extend(
|
||||
comando_dir.get(
|
||||
"motivos",
|
||||
["Erro ao definir comando direcional automático"]
|
||||
) or []
|
||||
)
|
||||
|
||||
# Se o direcional falhou, não deixa o robô seguir andando
|
||||
# sob movimento automático.
|
||||
if movimento_automatico:
|
||||
velocidade = 0.0
|
||||
frear = True
|
||||
atualizar_movimento = True
|
||||
|
||||
# Direcional vai para posição segura.
|
||||
angulo = 0.0
|
||||
tipo_movimento = TipoMovimentoDirecional.RodasDianteiras.value
|
||||
simulacao = []
|
||||
latencia = self._to_float(comando_dir.get("latencia", 0.0), 0.0)
|
||||
erro_lateral = self._to_float(comando_dir.get("erro_lateral", 0.0), 0.0)
|
||||
debug_custo = comando_dir.get("debug_custo", {}) or {}
|
||||
atualizar_direcional = True
|
||||
|
||||
else:
|
||||
enviar_direcional = bool(
|
||||
comando_dir.get("enviar_comando", False)
|
||||
)
|
||||
|
||||
if enviar_direcional:
|
||||
angulo = self._to_float(
|
||||
comando_dir.get("angulo", angulo),
|
||||
angulo
|
||||
)
|
||||
|
||||
tipo_movimento = self._to_int(
|
||||
comando_dir.get("tipo", tipo_movimento),
|
||||
tipo_movimento
|
||||
)
|
||||
|
||||
simulacao = comando_dir.get("simulacao", []) or []
|
||||
latencia = self._to_float(
|
||||
comando_dir.get("latencia", latencia),
|
||||
latencia
|
||||
)
|
||||
erro_lateral = self._to_float(
|
||||
comando_dir.get("erro_lateral", erro_lateral),
|
||||
erro_lateral
|
||||
)
|
||||
debug_custo = comando_dir.get("debug_custo", {}) or {}
|
||||
|
||||
atualizar_direcional = True
|
||||
|
||||
if parada_necessaria:
|
||||
motivos.extend(comando_dir.get("motivos", []) or [])
|
||||
|
||||
if movimento_automatico:
|
||||
velocidade = 0.0
|
||||
frear = True
|
||||
atualizar_movimento = True
|
||||
|
||||
else:
|
||||
# Falha ao calcular direcional automático: falha segura.
|
||||
if not isinstance(comando_dir, dict):
|
||||
erro_geral = True
|
||||
motivos.append("Falha ao definir comando direcional automático")
|
||||
|
||||
motivos.append(
|
||||
"Falha ao definir comando direcional automático"
|
||||
)
|
||||
|
||||
if movimento_automatico:
|
||||
velocidade = 0.0
|
||||
|
|
@ -266,35 +239,204 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
atualizar_movimento = True
|
||||
|
||||
angulo = 0.0
|
||||
tipo_movimento = TipoMovimentoDirecional.RodasDianteiras.value
|
||||
tipo_movimento = (
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
)
|
||||
|
||||
simulacao = []
|
||||
latencia = 0.0
|
||||
erro_lateral = 0.0
|
||||
debug_custo = {}
|
||||
|
||||
atualizar_direcional = True
|
||||
|
||||
# ============================================================
|
||||
# 4) Publicação do envelope
|
||||
# ============================================================
|
||||
else:
|
||||
erro_dir = bool(
|
||||
comando_dir.get("erro", False)
|
||||
)
|
||||
|
||||
precisa_keepalive = (
|
||||
agora - self.ultimo_envio_envelope
|
||||
) >= self.intervalo_keepalive_s
|
||||
if erro_dir:
|
||||
erro_geral = True
|
||||
|
||||
deve_publicar = (
|
||||
atualizar_movimento or
|
||||
atualizar_direcional or
|
||||
precisa_keepalive
|
||||
)
|
||||
motivos.extend(
|
||||
comando_dir.get(
|
||||
"motivos",
|
||||
[
|
||||
"Erro ao definir comando "
|
||||
"direcional automático"
|
||||
]
|
||||
) or []
|
||||
)
|
||||
|
||||
if not deve_publicar:
|
||||
return None
|
||||
if movimento_automatico:
|
||||
velocidade = 0.0
|
||||
frear = True
|
||||
atualizar_movimento = True
|
||||
|
||||
# Keepalive puro: Manager vivo, mas nenhum módulo precisa reaplicar.
|
||||
if precisa_keepalive and not atualizar_movimento and not atualizar_direcional:
|
||||
motivos = []
|
||||
angulo = 0.0
|
||||
tipo_movimento = (
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
)
|
||||
|
||||
return self._publicar_comando(
|
||||
simulacao = []
|
||||
|
||||
latencia = self._to_float(
|
||||
comando_dir.get(
|
||||
"latencia",
|
||||
0.0
|
||||
),
|
||||
0.0
|
||||
)
|
||||
|
||||
erro_lateral = self._to_float(
|
||||
comando_dir.get(
|
||||
"erro_lateral",
|
||||
0.0
|
||||
),
|
||||
0.0
|
||||
)
|
||||
|
||||
debug_custo = (
|
||||
comando_dir.get(
|
||||
"debug_custo",
|
||||
{}
|
||||
)
|
||||
or {}
|
||||
)
|
||||
|
||||
atualizar_direcional = True
|
||||
|
||||
else:
|
||||
angulo_calculado = self._to_float(
|
||||
comando_dir.get(
|
||||
"angulo",
|
||||
angulo
|
||||
),
|
||||
angulo
|
||||
)
|
||||
|
||||
tipo_calculado = self._to_int(
|
||||
comando_dir.get(
|
||||
"tipo",
|
||||
tipo_movimento
|
||||
),
|
||||
tipo_movimento
|
||||
)
|
||||
|
||||
simulacao_calculada = (
|
||||
comando_dir.get(
|
||||
"simulacao",
|
||||
[]
|
||||
)
|
||||
or []
|
||||
)
|
||||
|
||||
latencia_calculada = self._to_float(
|
||||
comando_dir.get(
|
||||
"latencia",
|
||||
latencia
|
||||
),
|
||||
latencia
|
||||
)
|
||||
|
||||
erro_lateral_calculado = self._to_float(
|
||||
comando_dir.get(
|
||||
"erro_lateral",
|
||||
erro_lateral
|
||||
),
|
||||
erro_lateral
|
||||
)
|
||||
|
||||
debug_calculado = (
|
||||
comando_dir.get(
|
||||
"debug_custo",
|
||||
{}
|
||||
)
|
||||
or {}
|
||||
)
|
||||
|
||||
solicitou_envio_mpc = bool(
|
||||
comando_dir.get(
|
||||
"enviar_comando",
|
||||
False
|
||||
)
|
||||
)
|
||||
|
||||
direcional_mudou = (
|
||||
self._direcional_mudou(
|
||||
angulo_calculado,
|
||||
tipo_calculado
|
||||
)
|
||||
)
|
||||
|
||||
envio_periodico = (
|
||||
self._venceu_intervalo_direcional(
|
||||
agora
|
||||
)
|
||||
)
|
||||
|
||||
atualizar_direcional = (
|
||||
solicitou_envio_mpc
|
||||
or direcional_mudou
|
||||
or envio_periodico
|
||||
)
|
||||
|
||||
angulo = angulo_calculado
|
||||
tipo_movimento = tipo_calculado
|
||||
simulacao = simulacao_calculada
|
||||
latencia = latencia_calculada
|
||||
erro_lateral = erro_lateral_calculado
|
||||
debug_custo = debug_calculado
|
||||
|
||||
if not isinstance(debug_custo, dict):
|
||||
debug_custo = {}
|
||||
|
||||
debug_custo[
|
||||
"decisao_envio_direcional"
|
||||
] = {
|
||||
"solicitou_envio_mpc":
|
||||
solicitou_envio_mpc,
|
||||
|
||||
"direcional_mudou":
|
||||
direcional_mudou,
|
||||
|
||||
"envio_periodico":
|
||||
envio_periodico,
|
||||
|
||||
"atualizar_direcional":
|
||||
atualizar_direcional,
|
||||
|
||||
"angulo_calculado":
|
||||
round(angulo, 3),
|
||||
|
||||
"angulo_ultimo_enviado": (
|
||||
None
|
||||
if self.ultimo_angulo_enviado is None
|
||||
else round(
|
||||
self.ultimo_angulo_enviado,
|
||||
3
|
||||
)
|
||||
),
|
||||
|
||||
"tipo_calculado":
|
||||
int(tipo_movimento),
|
||||
|
||||
"tipo_ultimo_enviado": (
|
||||
self.ultimo_tipo_enviado
|
||||
),
|
||||
|
||||
"seq_direcional_atual":
|
||||
self.seq_direcional,
|
||||
|
||||
"sessao_comando":
|
||||
self.sessao_comando,
|
||||
}
|
||||
|
||||
return self._publicar_se_necessario(
|
||||
agora=agora,
|
||||
velocidade=velocidade,
|
||||
frear=frear,
|
||||
|
|
@ -310,30 +452,86 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
atualizar_direcional=atualizar_direcional,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
mostrar_log(f"Erro no processador EmAndamento: {e}")
|
||||
except Exception as ex:
|
||||
mostrar_log(
|
||||
f"Erro no processador EmAndamento: {ex}"
|
||||
)
|
||||
|
||||
# Falha no orquestrador em operação automática:
|
||||
# tenta publicar parada segura.
|
||||
try:
|
||||
return comando_controle(
|
||||
percentual_velocidade=0.0,
|
||||
return self._publicar_comando(
|
||||
agora=agora,
|
||||
velocidade=0.0,
|
||||
frear=True,
|
||||
angulo=0.0,
|
||||
tipo_movimento=TipoMovimentoDirecional.RodasDianteiras.value,
|
||||
tipo_movimento=(
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
),
|
||||
simulacao=[],
|
||||
erro=True,
|
||||
latencia=0.0,
|
||||
erro_lateral=0.0,
|
||||
debug_custo={},
|
||||
motivos=[f"Erro no processador EmAndamento: {e}"],
|
||||
motivos=[
|
||||
f"Erro no processador EmAndamento: {ex}"
|
||||
],
|
||||
atualizar_movimento=True,
|
||||
atualizar_direcional=True,
|
||||
)
|
||||
except Exception as e2:
|
||||
mostrar_log(f"Erro ao publicar parada segura no EmAndamento: {e2}")
|
||||
|
||||
except Exception as ex2:
|
||||
mostrar_log(
|
||||
"Erro ao publicar parada segura no "
|
||||
f"EmAndamento: {ex2}"
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def _publicar_se_necessario(
|
||||
self,
|
||||
*,
|
||||
agora,
|
||||
velocidade,
|
||||
frear,
|
||||
angulo,
|
||||
tipo_movimento,
|
||||
simulacao,
|
||||
erro,
|
||||
latencia,
|
||||
erro_lateral,
|
||||
debug_custo,
|
||||
motivos,
|
||||
atualizar_movimento,
|
||||
atualizar_direcional,
|
||||
):
|
||||
precisa_keepalive = (
|
||||
agora - self.ultimo_envio_envelope
|
||||
) >= self.intervalo_keepalive_s
|
||||
|
||||
if not (
|
||||
atualizar_movimento
|
||||
or atualizar_direcional
|
||||
or precisa_keepalive
|
||||
):
|
||||
return None
|
||||
|
||||
return self._publicar_comando(
|
||||
agora=agora,
|
||||
velocidade=velocidade,
|
||||
frear=frear,
|
||||
angulo=angulo,
|
||||
tipo_movimento=tipo_movimento,
|
||||
simulacao=simulacao,
|
||||
erro=erro,
|
||||
latencia=latencia,
|
||||
erro_lateral=erro_lateral,
|
||||
debug_custo=debug_custo,
|
||||
motivos=motivos,
|
||||
atualizar_movimento=atualizar_movimento,
|
||||
atualizar_direcional=atualizar_direcional,
|
||||
)
|
||||
|
||||
def _publicar_comando(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -351,6 +549,14 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
atualizar_movimento,
|
||||
atualizar_direcional,
|
||||
):
|
||||
# A sequência aumenta somente quando aquele módulo
|
||||
# deve reaplicar o comando.
|
||||
if atualizar_movimento:
|
||||
self.seq_movimento += 1
|
||||
|
||||
if atualizar_direcional:
|
||||
self.seq_direcional += 1
|
||||
|
||||
payload = comando_controle(
|
||||
percentual_velocidade=velocidade,
|
||||
frear=frear,
|
||||
|
|
@ -362,77 +568,189 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
erro_lateral=erro_lateral,
|
||||
debug_custo=debug_custo,
|
||||
motivos=motivos or [],
|
||||
|
||||
atualizar_movimento=atualizar_movimento,
|
||||
atualizar_direcional=atualizar_direcional,
|
||||
|
||||
seq_movimento=self.seq_movimento,
|
||||
seq_direcional=self.seq_direcional,
|
||||
sessao_comando=self.sessao_comando,
|
||||
)
|
||||
|
||||
self.ultimo_envio_envelope = agora
|
||||
|
||||
if atualizar_movimento:
|
||||
self.ultimo_envio_movimento = agora
|
||||
self.ultima_velocidade_enviada = velocidade
|
||||
self.ultimo_freio_enviado = frear
|
||||
self.ultima_velocidade_enviada = float(
|
||||
velocidade
|
||||
)
|
||||
self.ultimo_freio_enviado = bool(frear)
|
||||
|
||||
if atualizar_direcional:
|
||||
self.ultimo_envio_direcional = agora
|
||||
self.ultimo_angulo_enviado = angulo
|
||||
self.ultimo_tipo_enviado = tipo_movimento
|
||||
self.ultimo_angulo_enviado = float(angulo)
|
||||
self.ultimo_tipo_enviado = int(
|
||||
tipo_movimento
|
||||
)
|
||||
|
||||
return payload
|
||||
|
||||
def _ler_valores_atuais(self, _controle):
|
||||
def _venceu_intervalo_movimento(self, agora):
|
||||
return (
|
||||
agora - self.ultimo_envio_movimento
|
||||
) >= self.intervalo_movimento_s
|
||||
|
||||
def _venceu_intervalo_direcional(self, agora):
|
||||
return (
|
||||
agora - self.ultimo_envio_direcional
|
||||
) >= self.intervalo_direcional_s
|
||||
|
||||
def _ler_valores_atuais(self, controle):
|
||||
return {
|
||||
"velocidade": self._to_float(
|
||||
_controle.get("velocidade_sp", 0.0),
|
||||
0.0
|
||||
),
|
||||
"frear": bool(
|
||||
_controle.get("em_freio", False)
|
||||
),
|
||||
"angulo": self._to_float(
|
||||
_controle.get("angulo_sp", 0.0),
|
||||
0.0
|
||||
),
|
||||
"tipo_movimento": self._to_int(
|
||||
_controle.get(
|
||||
"tipo_movimento_direcional",
|
||||
TipoMovimentoDirecional.RodasDianteiras.value
|
||||
controle.get(
|
||||
"velocidade_sp",
|
||||
0.0
|
||||
),
|
||||
TipoMovimentoDirecional.RodasDianteiras.value
|
||||
0.0
|
||||
),
|
||||
"simulacao": _controle.get("simulacao", []) or [],
|
||||
|
||||
"frear": bool(
|
||||
controle.get(
|
||||
"em_freio",
|
||||
False
|
||||
)
|
||||
),
|
||||
|
||||
"angulo": self._to_float(
|
||||
controle.get(
|
||||
"angulo_sp",
|
||||
0.0
|
||||
),
|
||||
0.0
|
||||
),
|
||||
|
||||
"tipo_movimento": self._to_int(
|
||||
controle.get(
|
||||
"tipo_movimento_direcional",
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
),
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
),
|
||||
|
||||
"simulacao": (
|
||||
controle.get(
|
||||
"simulacao",
|
||||
[]
|
||||
)
|
||||
or []
|
||||
),
|
||||
|
||||
"latencia": self._to_float(
|
||||
_controle.get("latencia", 0.0),
|
||||
controle.get(
|
||||
"latencia",
|
||||
0.0
|
||||
),
|
||||
0.0
|
||||
),
|
||||
|
||||
"erro_lateral": self._to_float(
|
||||
_controle.get("erro_lateral", 0.0),
|
||||
controle.get(
|
||||
"erro_lateral",
|
||||
0.0
|
||||
),
|
||||
0.0
|
||||
),
|
||||
"debug_custo": _controle.get("debug_custo", {}) or {},
|
||||
|
||||
"debug_custo": (
|
||||
controle.get(
|
||||
"debug_custo",
|
||||
{}
|
||||
)
|
||||
or {}
|
||||
),
|
||||
}
|
||||
|
||||
def _movimento_mudou(self, velocidade, frear):
|
||||
def _movimento_mudou(
|
||||
self,
|
||||
velocidade,
|
||||
frear,
|
||||
tolerancia_velocidade=0.05
|
||||
):
|
||||
if self.ultima_velocidade_enviada is None:
|
||||
return True
|
||||
|
||||
if self.ultimo_freio_enviado is None:
|
||||
return True
|
||||
|
||||
if bool(frear) != bool(self.ultimo_freio_enviado):
|
||||
if bool(frear) != bool(
|
||||
self.ultimo_freio_enviado
|
||||
):
|
||||
return True
|
||||
|
||||
return abs(
|
||||
float(velocidade) - float(self.ultima_velocidade_enviada)
|
||||
) >= 0.05
|
||||
float(velocidade)
|
||||
- float(self.ultima_velocidade_enviada)
|
||||
) >= tolerancia_velocidade
|
||||
|
||||
def _direcional_mudou(
|
||||
self,
|
||||
angulo,
|
||||
tipo_movimento,
|
||||
tolerancia_angulo=0.10
|
||||
):
|
||||
if self.ultimo_angulo_enviado is None:
|
||||
return True
|
||||
|
||||
if self.ultimo_tipo_enviado is None:
|
||||
return True
|
||||
|
||||
tipo_atual = self._to_int(
|
||||
tipo_movimento,
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
)
|
||||
|
||||
tipo_anterior = self._to_int(
|
||||
self.ultimo_tipo_enviado,
|
||||
TipoMovimentoDirecional
|
||||
.RodasDianteiras
|
||||
.value
|
||||
)
|
||||
|
||||
if tipo_atual != tipo_anterior:
|
||||
return True
|
||||
|
||||
return abs(
|
||||
float(angulo)
|
||||
- float(self.ultimo_angulo_enviado)
|
||||
) >= tolerancia_angulo
|
||||
|
||||
def _logar_bloqueio(self, motivos):
|
||||
agora = time.perf_counter()
|
||||
msg = " | ".join(map(str, motivos))
|
||||
mensagem = " | ".join(
|
||||
map(str, motivos)
|
||||
)
|
||||
|
||||
if msg != self.ultima_msg_bloqueio or (agora - self.ultimo_log_bloqueio) >= 1.0:
|
||||
mostrar_log(f"⛔ Controle bloqueado: {msg}")
|
||||
self.ultima_msg_bloqueio = msg
|
||||
mudou = (
|
||||
mensagem != self.ultima_msg_bloqueio
|
||||
)
|
||||
|
||||
venceu_intervalo = (
|
||||
agora - self.ultimo_log_bloqueio
|
||||
) >= 1.0
|
||||
|
||||
if mudou or venceu_intervalo:
|
||||
mostrar_log(
|
||||
f"⛔ Controle bloqueado: {mensagem}"
|
||||
)
|
||||
|
||||
self.ultima_msg_bloqueio = mensagem
|
||||
self.ultimo_log_bloqueio = agora
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -440,7 +758,9 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
try:
|
||||
if valor is None:
|
||||
return float(padrao)
|
||||
|
||||
return float(valor)
|
||||
|
||||
except Exception:
|
||||
return float(padrao)
|
||||
|
||||
|
|
@ -454,5 +774,6 @@ class ProcessadorEmAndamento(ProcessadorBase):
|
|||
return int(valor.value)
|
||||
|
||||
return int(valor)
|
||||
|
||||
except Exception:
|
||||
return int(padrao)
|
||||
return int(padrao)
|
||||
|
|
@ -29,8 +29,11 @@ def comando_controle(
|
|||
erro_lateral: float = 0.0,
|
||||
debug_custo=None,
|
||||
motivos=None,
|
||||
atualizar_movimento: bool = True,
|
||||
atualizar_direcional: bool = True,
|
||||
atualizar_movimento: bool = False,
|
||||
atualizar_direcional: bool = False,
|
||||
seq_movimento: int = 0,
|
||||
seq_direcional: int = 0,
|
||||
sessao_comando: str =None,
|
||||
):
|
||||
_controle = ContextoGlobalRedis.get_controle()
|
||||
|
||||
|
|
@ -47,9 +50,14 @@ def comando_controle(
|
|||
hb_novo = _proximo_heartbeat(hb_atual)
|
||||
|
||||
payload = {
|
||||
"versao_comando": 2,
|
||||
"versao_comando": 3,
|
||||
"ts_comando": time.time(),
|
||||
|
||||
"sessao_comando": sessao_comando,
|
||||
|
||||
"seq_movimento": int(seq_movimento),
|
||||
"seq_direcional": int(seq_direcional),
|
||||
|
||||
# Flags novas do contrato
|
||||
"atualizar_movimento": bool(atualizar_movimento),
|
||||
"atualizar_direcional": bool(atualizar_direcional),
|
||||
|
|
|
|||
Loading…
Reference in New Issue