inclusao de motivos de controle e trajetoria

This commit is contained in:
Diego Freitas 2026-03-13 11:05:41 -03:00
parent 1ff65eeb67
commit 7398624ad7
14 changed files with 221 additions and 43 deletions

View File

@ -2167,6 +2167,7 @@ namespace AgroBase.Models
public double Latencia { get; set; } public double Latencia { get; set; }
public double ErroLateral { get; set; } = 0.0; public double ErroLateral { get; set; } = 0.0;
public Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel> DebugCustoMpc { get; set; } = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>(); public Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel> DebugCustoMpc { get; set; } = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>();
public List<string> Motivos { get; set; } = new List<string>();
public void Reiniciar() public void Reiniciar()
{ {
@ -2197,6 +2198,7 @@ namespace AgroBase.Models
EmFreio = EmFreio, EmFreio = EmFreio,
ErroLateral = ErroLateral, ErroLateral = ErroLateral,
DebugCustoMpc = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>(DebugCustoMpc ?? new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>()), DebugCustoMpc = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>(DebugCustoMpc ?? new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>()),
Motivos = new List<string>(Motivos ?? new List<string>())
}; };
} }
} }

View File

@ -534,6 +534,7 @@ namespace AgroBase.Models.Operacoes
HerbicidaSuficiente = t.AutonomiaCooredor.HerbicidaSuficiente, HerbicidaSuficiente = t.AutonomiaCooredor.HerbicidaSuficiente,
Liberado = t.AutonomiaCooredor.Liberado, Liberado = t.AutonomiaCooredor.Liberado,
Motivo = t.AutonomiaCooredor.Motivo, Motivo = t.AutonomiaCooredor.Motivo,
Motivos = t.AutonomiaCooredor.Motivos.ToList(),
Status = t.AutonomiaCooredor.Status Status = t.AutonomiaCooredor.Status
}, },
AnguloCaminho = t.AnguloCaminho, AnguloCaminho = t.AnguloCaminho,
@ -566,6 +567,7 @@ namespace AgroBase.Models.Operacoes
PercentualVelocidadeSPKmh = c.PercentualVelocidadeSPKmh, PercentualVelocidadeSPKmh = c.PercentualVelocidadeSPKmh,
TipoMovimento = c.TipoMovimento, TipoMovimento = c.TipoMovimento,
SimulacaoMPC = c.SimulacaoMPC, SimulacaoMPC = c.SimulacaoMPC,
Motivos = c.Motivos
} : new OperacaoParametrosDadosControleModel(); } : new OperacaoParametrosDadosControleModel();
ModulosSaude = new List<ManagerWorkerMessageResponseModulosPendentesModel>(sen?.ModulosSaude ?? new List<ManagerWorkerMessageResponseModulosPendentesModel>()); ModulosSaude = new List<ManagerWorkerMessageResponseModulosPendentesModel>(sen?.ModulosSaude ?? new List<ManagerWorkerMessageResponseModulosPendentesModel>());
DispositivosMapeados = new List<DispositivoDetalhesModel>(sen?.DispositivosMapeados ?? new List<DispositivoDetalhesModel>()); DispositivosMapeados = new List<DispositivoDetalhesModel>(sen?.DispositivosMapeados ?? new List<DispositivoDetalhesModel>());
@ -888,6 +890,7 @@ namespace AgroBase.Models.Operacoes
public bool HerbicidaSuficiente { get; set; } public bool HerbicidaSuficiente { get; set; }
public bool Liberado { get; set; } public bool Liberado { get; set; }
public string Motivo { get; set; } public string Motivo { get; set; }
public List<string> Motivos { get; set; }
} }
@ -898,6 +901,7 @@ namespace AgroBase.Models.Operacoes
public TipoMovimentoDirecional TipoMovimento { get; set; } public TipoMovimentoDirecional TipoMovimento { get; set; }
public double PercentualVelocidadeSPKmh { get; set; } public double PercentualVelocidadeSPKmh { get; set; }
public List<MPCSimulacaoModel> SimulacaoMPC { get; set; } = new List<MPCSimulacaoModel>(); public List<MPCSimulacaoModel> SimulacaoMPC { get; set; } = new List<MPCSimulacaoModel>();
public List<string> Motivos { get; set; } = new List<string>();
public OperacaoParametrosDadosControleModel Clone() public OperacaoParametrosDadosControleModel Clone()
{ {
@ -907,7 +911,8 @@ namespace AgroBase.Models.Operacoes
Angulo = Angulo, Angulo = Angulo,
PercentualVelocidadeSPKmh = PercentualVelocidadeSPKmh, PercentualVelocidadeSPKmh = PercentualVelocidadeSPKmh,
TipoMovimento = TipoMovimento, TipoMovimento = TipoMovimento,
SimulacaoMPC = SimulacaoMPC SimulacaoMPC = SimulacaoMPC,
Motivos = new List<string>(Motivos ?? new List<string>())
}; };
} }
} }

View File

@ -57,6 +57,7 @@ namespace AgroBase.Models.Operadores
public double latencia { get; set; } public double latencia { get; set; }
public double erro_lateral { get; set; } public double erro_lateral { get; set; }
public Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel> debug_custo { get; set; } public Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel> debug_custo { get; set; }
public string[] motivos { get; set; }
} }
public class ManagerWorkerMessageResponseDebugCustoModel public class ManagerWorkerMessageResponseDebugCustoModel

View File

@ -1014,6 +1014,7 @@ namespace AgroBase.Models
dictParams.TryGetValue("latencia", out var _latencia); dictParams.TryGetValue("latencia", out var _latencia);
dictParams.TryGetValue("erro_lateral", out var _erro_lateral); dictParams.TryGetValue("erro_lateral", out var _erro_lateral);
dictParams.TryGetValue("debug_custo", out var _debug_custo); dictParams.TryGetValue("debug_custo", out var _debug_custo);
dictParams.TryGetValue("motivos", out var _motivos);
novoComando = new ManagerWorkerMessageResponseComandoModel() novoComando = new ManagerWorkerMessageResponseComandoModel()
{ {
angulo = Convert.ToDouble(_angulo), angulo = Convert.ToDouble(_angulo),
@ -1025,6 +1026,7 @@ namespace AgroBase.Models
latencia = Convert.ToDouble(_latencia.ToString().Replace(".", ",")), latencia = Convert.ToDouble(_latencia.ToString().Replace(".", ",")),
erro_lateral = Convert.ToDouble(_erro_lateral.ToString().Replace(".", ",")), erro_lateral = Convert.ToDouble(_erro_lateral.ToString().Replace(".", ",")),
debug_custo = JsonConvert.DeserializeObject<Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>>(_debug_custo.ToString()), debug_custo = JsonConvert.DeserializeObject<Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>>(_debug_custo.ToString()),
motivos = JsonConvert.DeserializeObject<string[]>((_motivos ?? "").ToString())
}; };
} }
catch (Exception ex) catch (Exception ex)
@ -1073,11 +1075,15 @@ namespace AgroBase.Models
_Controle.ticks_sem_resposta = DateTime.MinValue; _Controle.ticks_sem_resposta = DateTime.MinValue;
} }
if (novoComando != null) if (novoComando != null)
{
_Controle.heartbeat = novoComando.heartbeat; _Controle.heartbeat = novoComando.heartbeat;
_Controle.Motivos = novoComando.motivos.ToList();
}
if (_Controle.ticks_sem_resposta != DateTime.MinValue) if (_Controle.ticks_sem_resposta != DateTime.MinValue)
{ {
if (_Controle.ticks_sem_resposta.AddMilliseconds(max_ticks_sem_resposta) < agora) if (_Controle.ticks_sem_resposta.AddMilliseconds(max_ticks_sem_resposta) < agora)
{ {
Variaveis.OperacaoEmAndamento.Sensoriamento?.InserirLog(T_Code.Npc, StatusModulo.Falha, 0, "Muito tempo sem receber novo comando do Manager Worker! Parando Movimento!");
Console.WriteLine("Muito tempo sem receber novo comando do Manager Worker! Parando Movimento!"); Console.WriteLine("Muito tempo sem receber novo comando do Manager Worker! Parando Movimento!");
RedisService.AtualizarCampos( RedisService.AtualizarCampos(
CtxKey.DadosControle, CtxKey.DadosControle,
@ -1086,6 +1092,7 @@ namespace AgroBase.Models
} }
else if (_Controle.ticks_sem_resposta.AddMilliseconds(min_ticks_sem_resposta) < agora) else if (_Controle.ticks_sem_resposta.AddMilliseconds(min_ticks_sem_resposta) < agora)
{ {
Variaveis.OperacaoEmAndamento.Sensoriamento?.InserirLog(T_Code.Npc, StatusModulo.Alerta, 80, "Muito tempo sem receber novo comando do Manager Worker! Reduzindo Velocidade!");
Console.WriteLine("Muito tempo sem receber novo comando do Manager Worker! Reduzindo Velocidade!"); Console.WriteLine("Muito tempo sem receber novo comando do Manager Worker! Reduzindo Velocidade!");
RedisService.AtualizarCampos( RedisService.AtualizarCampos(
CtxKey.DadosControle, CtxKey.DadosControle,
@ -2742,6 +2749,9 @@ namespace AgroBase.Models
[JsonProperty] [JsonProperty]
public string Motivo { get; private set; } = ""; public string Motivo { get; private set; } = "";
[JsonProperty]
public List<string> Motivos { get; private set; } = new List<string>();
// ========================== // ==========================
// Flags de conveniência // Flags de conveniência
// ========================== // ==========================
@ -2785,7 +2795,7 @@ namespace AgroBase.Models
if (!Iniciado) if (!Iniciado)
{ {
Liberado = true; Liberado = true;
Motivo = "Não iniciado"; Motivos.Add("Não iniciado");
Status = AutonomiaCorredorStatus.Desconhecido; Status = AutonomiaCorredorStatus.Desconhecido;
return; return;
} }
@ -2798,6 +2808,9 @@ namespace AgroBase.Models
if (CorredoresLiberados.ContainsKey(idxCorredor) && CorredoresLiberados[idxCorredor].Liberado) if (CorredoresLiberados.ContainsKey(idxCorredor) && CorredoresLiberados[idxCorredor].Liberado)
return; return;
var motivosBloqueio = new List<string>();
Motivos = new List<string>();
// Atalho: interpretar que o operador “clicou em liberar” (pelo menos um sinal veio) // Atalho: interpretar que o operador “clicou em liberar” (pelo menos um sinal veio)
bool solicitouLiberacaoHumana = bateria_liberada || reservatorio_liberado; bool solicitouLiberacaoHumana = bateria_liberada || reservatorio_liberado;
@ -2839,23 +2852,26 @@ namespace AgroBase.Models
statusBase = AutonomiaCorredorStatus.Critico; statusBase = AutonomiaCorredorStatus.Critico;
if (criticoBateria) if (criticoBateria)
{
motivosBloqueio.Add("Bateria insuficiente para concluir o corredor com segurança.");
motivoBase += motivoBat; motivoBase += motivoBat;
}
if (!herbOk) if (criticoHerbicida)
{
motivosBloqueio.Add("Herbicida insuficiente para pulverizar o corredor completo.");
motivoBase += (string.IsNullOrEmpty(motivoBase) ? "" : "; ") + motivoHerb; motivoBase += (string.IsNullOrEmpty(motivoBase) ? "" : "; ") + motivoHerb;
}
} }
else else
{ {
// Não crítico
if (!herbOk && !exigirPulverizacaoTotal) if (!herbOk && !exigirPulverizacaoTotal)
{ {
// Pode atravessar, mas não pulveriza tudo
statusBase = AutonomiaCorredorStatus.OkSomenteTransito; statusBase = AutonomiaCorredorStatus.OkSomenteTransito;
motivoBase = motivoHerb; motivoBase = motivoHerb;
} }
else else
{ {
// Tudo ok
statusBase = AutonomiaCorredorStatus.OkCompleto; statusBase = AutonomiaCorredorStatus.OkCompleto;
} }
} }
@ -2906,6 +2922,7 @@ namespace AgroBase.Models
motivoFinal = $"Corredor {idxCorredor + 1}: BLOQUEADO. {detalhes} " + motivoFinal = $"Corredor {idxCorredor + 1}: BLOQUEADO. {detalhes} " +
$"Aguardando liberação do operador pela base."; $"Aguardando liberação do operador pela base.";
motivosBloqueio.Add($"Corredor {idxCorredor + 1}: BLOQUEADO. Aguardando solução ou liberação manual do operador.");
} }
else else
{ {
@ -2931,6 +2948,7 @@ namespace AgroBase.Models
motivoFinal = $"Corredor {idxCorredor + 1}: LIBERAÇÃO MANUAL (override). " + motivoFinal = $"Corredor {idxCorredor + 1}: LIBERAÇÃO MANUAL (override). " +
(string.IsNullOrEmpty(motivoBase) ? "Autonomia insuficiente, mas liberado." : motivoBase); (string.IsNullOrEmpty(motivoBase) ? "Autonomia insuficiente, mas liberado." : motivoBase);
motivosBloqueio.Add($"Corredor {idxCorredor + 1}: LIBERAÇÃO MANUAL. Autonomia insuficiente, mas houver liberação supervisionada.");
// Destrava o corredor (o operador assumiu) // Destrava o corredor (o operador assumiu)
_corredoresTravados.Remove(idxCorredor); _corredoresTravados.Remove(idxCorredor);
@ -2958,26 +2976,30 @@ namespace AgroBase.Models
// ========================== // ==========================
Status = statusFinal; Status = statusFinal;
Motivos = motivosBloqueio
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct()
.ToList();
var res = new AutonomiaCorredorModel() var res = new AutonomiaCorredorModel()
{ {
Iniciado = this.Iniciado, Iniciado = this.Iniciado,
Liberado = liberadoFinal, Liberado = liberadoFinal,
Motivo = motivoFinal, Motivo = motivoFinal,
Motivos = Motivos,
Status = statusFinal, Status = statusFinal,
DistanciaCorredor_m = DistanciaCorredor_m, DistanciaCorredor_m = DistanciaCorredor_m,
DistanciaSeguraBateria_m = DistanciaSeguraBateria_m, DistanciaSeguraBateria_m = DistanciaSeguraBateria_m,
DistanciaSeguraHerbicida_m = DistanciaSeguraHerbicida_m, DistanciaSeguraHerbicida_m = DistanciaSeguraHerbicida_m,
}; };
bool addLog = false;
if (CorredoresLiberados.ContainsKey(idxCorredor)) if (CorredoresLiberados.ContainsKey(idxCorredor))
{ {
// Se estava bloqueado e agora liberou, registra mudança // Se estava bloqueado e agora liberou, registra mudança
if (CorredoresLiberados[idxCorredor].Liberado == false && liberadoFinal) if (CorredoresLiberados[idxCorredor].Liberado == false && liberadoFinal)
{ {
CorredoresLiberados[idxCorredor] = res; CorredoresLiberados[idxCorredor] = res;
addLog = true; Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(T_Code.Trj, liberadoFinal ? StatusModulo.Operante : StatusModulo.Alerta, liberadoFinal ? 100 : 0, $"Corredor {idxCorredor + 1} liberado pelo operador");
} }
else else
{ {
@ -2988,23 +3010,13 @@ namespace AgroBase.Models
else else
{ {
CorredoresLiberados.Add(idxCorredor, res); CorredoresLiberados.Add(idxCorredor, res);
addLog = true; Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(T_Code.Trj, liberadoFinal ? StatusModulo.Operante : StatusModulo.Alerta, liberadoFinal ? 100 : 0, $"Corredor {idxCorredor + 1} bloqueado por autonomia insuficiente");
}
if (addLog)
{
// Mantive tua ideia: loga operante quando liberado, alerta quando não
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(
T_Code.Trj,
liberadoFinal ? StatusModulo.Operante : StatusModulo.Alerta,
liberadoFinal ? 100 : 0,
res.Motivo
);
} }
// Atualiza estado exposto // Atualiza estado exposto
Liberado = res.Liberado; Liberado = res.Liberado;
Motivo = res.Motivo; Motivo = res.Motivo;
Motivos = res.Motivos;
} }
// ========================== // ==========================
@ -3016,7 +3028,7 @@ namespace AgroBase.Models
{ {
Iniciado = Iniciado, Iniciado = Iniciado,
Liberado = Liberado, Liberado = Liberado,
Motivo = Motivo, Motivos = new List<string>(Motivos ?? new List<string>()),
Status = Status, Status = Status,
DistanciaCorredor_m = DistanciaCorredor_m, DistanciaCorredor_m = DistanciaCorredor_m,
DistanciaSeguraBateria_m = DistanciaSeguraBateria_m, DistanciaSeguraBateria_m = DistanciaSeguraBateria_m,

View File

@ -94,6 +94,7 @@ namespace AgroBase.Services.Operadores
("iniciado", Variaveis.OperacaoEmAndamento.Iniciado), ("iniciado", Variaveis.OperacaoEmAndamento.Iniciado),
("finalizando", Variaveis.OperacaoEmAndamento.Finalizando), ("finalizando", Variaveis.OperacaoEmAndamento.Finalizando),
("calibrando", Variaveis.OperacaoEmAndamento.Calibrando), ("calibrando", Variaveis.OperacaoEmAndamento.Calibrando),
("simulando", Variaveis.OperacaoEmAndamento.Simulando),
("emergencia", Variaveis.OperacaoEmAndamento.Emergencia), ("emergencia", Variaveis.OperacaoEmAndamento.Emergencia),
("pausa", Variaveis.OperacaoEmAndamento.Pausa) ("pausa", Variaveis.OperacaoEmAndamento.Pausa)
); );
@ -818,7 +819,7 @@ namespace AgroBase.Services.Operadores
("liberado", DadosTrj?.AutonomiaCooredor?.Liberado ?? false), ("liberado", DadosTrj?.AutonomiaCooredor?.Liberado ?? false),
("bateria_ok", DadosTrj?.AutonomiaCooredor?.BateriaSuficiente ?? false), ("bateria_ok", DadosTrj?.AutonomiaCooredor?.BateriaSuficiente ?? false),
("herbicida_ok", DadosTrj?.AutonomiaCooredor?.HerbicidaSuficiente ?? false), ("herbicida_ok", DadosTrj?.AutonomiaCooredor?.HerbicidaSuficiente ?? false),
("motivo", DadosTrj?.AutonomiaCooredor?.Motivo) ("motivos", DadosTrj?.AutonomiaCooredor?.Motivos ?? new List<string>())
); );
double distanciaBase = -1; double distanciaBase = -1;

View File

@ -60,6 +60,10 @@ namespace AgroBase.Services.Operadores
dictParams.TryGetValue("tipo_movimento_direcional", out object _movimento); dictParams.TryGetValue("tipo_movimento_direcional", out object _movimento);
dictParams.TryGetValue("simulacao", out object _simulacao); dictParams.TryGetValue("simulacao", out object _simulacao);
dictParams.TryGetValue("heartbeat", out object _heartbeat); dictParams.TryGetValue("heartbeat", out object _heartbeat);
dictParams.TryGetValue("latencia", out var _latencia);
dictParams.TryGetValue("erro_lateral", out var _erro_lateral);
dictParams.TryGetValue("debug_custo", out var _debug_custo);
dictParams.TryGetValue("motivos", out var _motivos);
novoComando = new ManagerWorkerMessageResponseComandoModel() novoComando = new ManagerWorkerMessageResponseComandoModel()
{ {
angulo = Convert.ToDouble(_angulo), angulo = Convert.ToDouble(_angulo),
@ -67,7 +71,11 @@ namespace AgroBase.Services.Operadores
em_freio = Convert.ToBoolean(_em_freio), em_freio = Convert.ToBoolean(_em_freio),
tipo_movimento = (Enums.TipoMovimentoDirecional)Convert.ToInt32(_movimento), tipo_movimento = (Enums.TipoMovimentoDirecional)Convert.ToInt32(_movimento),
simulacao = JsonConvert.DeserializeObject<List<double[]>>((_simulacao ?? "").ToString()), simulacao = JsonConvert.DeserializeObject<List<double[]>>((_simulacao ?? "").ToString()),
heartbeat = Convert.ToInt32(_heartbeat) heartbeat = Convert.ToInt32(_heartbeat),
latencia = Convert.ToDouble(_latencia.ToString().Replace(".", ",")),
erro_lateral = Convert.ToDouble(_erro_lateral.ToString().Replace(".", ",")),
debug_custo = JsonConvert.DeserializeObject<Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>>(_debug_custo.ToString()),
motivos = JsonConvert.DeserializeObject<string[]>((_motivos ?? "").ToString())
}; };
} }
catch (Exception ex) catch (Exception ex)
@ -76,6 +84,7 @@ namespace AgroBase.Services.Operadores
} }
var _Controle = Variaveis.OperacaoEmAndamento.Controle; var _Controle = Variaveis.OperacaoEmAndamento.Controle;
_Controle.Motivos = (novoComando?.motivos ?? (new List<string>()).ToArray()).ToList();
bool comandoMudou = (_Controle.Angulo != novoComando.angulo || _Controle.PercentualVelocidadeSP != novoComando.percentual_velocidade || _Controle.TipoMovimento != novoComando.tipo_movimento || _Controle.EmFreio != novoComando.em_freio); bool comandoMudou = (_Controle.Angulo != novoComando.angulo || _Controle.PercentualVelocidadeSP != novoComando.percentual_velocidade || _Controle.TipoMovimento != novoComando.tipo_movimento || _Controle.EmFreio != novoComando.em_freio);
if (novoComando != null && comandoMudou) if (novoComando != null && comandoMudou)
{ {

View File

@ -57,6 +57,7 @@ def definir_comando(pid: PIDAdaptativo, envio_necessario: bool):
Vmin = 0.1 # if _trajetoria.get("status", StatusCarroMapa.Parado.value) == StatusCarroMapa.CaminhandoRua.value else 0.8 Vmin = 0.1 # if _trajetoria.get("status", StatusCarroMapa.Parado.value) == StatusCarroMapa.CaminhandoRua.value else 0.8
contexto = { contexto = {
"Simulando": _operacao.get("simulando", False),
"GPS": { "GPS": {
"Latitude": _gps.get("lat", 0), "Latitude": _gps.get("lat", 0),
"Longitude": _gps.get("lon", 0), "Longitude": _gps.get("lon", 0),

View File

@ -656,10 +656,12 @@ class ControladorMPC:
pos_timestamp = GPS.get("Timestamp", 0.0) pos_timestamp = GPS.get("Timestamp", 0.0)
pos_latencia = now() - pos_timestamp pos_latencia = now() - pos_timestamp
simulando = contexto.get("Simulando", False)
LAT_MAX = 2.0 LAT_MAX = 2.0
if pos_latencia >= LAT_MAX: if pos_latencia >= LAT_MAX:
mostrar_log(f"[GPS] Grande latencia entre coordenadas detectado ({pos_latencia:.3f}/{LAT_MAX}): parando por fallback") mostrar_log(f"[GPS] Grande latencia entre coordenadas detectado ({pos_latencia:.3f}/{LAT_MAX}): parando por fallback")
return self._comando_fallback_hot_stop(comando_anterior, latencia=pos_latencia) return self._comando_fallback_hot_stop(comando_anterior, motivos=[] if simulando else [f"Grande latencia entre coordenadas detectado ({pos_latencia:.3f}/{LAT_MAX}): parando por fallback"], latencia=pos_latencia)
if (pos_passos_atraso > 3): if (pos_passos_atraso > 3):
mostrar_log(f"[GPS] Atraso detectado de {(pos_passos_atraso / 3.0):.1f} passos, {pos_latencia:.3f} s") mostrar_log(f"[GPS] Atraso detectado de {(pos_passos_atraso / 3.0):.1f} passos, {pos_latencia:.3f} s")
@ -966,7 +968,8 @@ class ControladorMPC:
"erro_lateral": erro_lateral, "erro_lateral": erro_lateral,
"erro_orientacao": erro_orientacao, "erro_orientacao": erro_orientacao,
"debug_custo": debug_custo, "debug_custo": debug_custo,
"candidatos_testados": K "candidatos_testados": K,
"motivos": []
} }
# -------------------- Debug opcional da matriz de custo -------------------- # -------------------- Debug opcional da matriz de custo --------------------
@ -996,7 +999,7 @@ class ControladorMPC:
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao processar MPC: {e}") mostrar_log(f"❌ Erro ao processar MPC: {e}")
return self._comando_fallback_hot_stop(comando_anterior, latencia=pos_latencia) return self._comando_fallback_hot_stop(comando_anterior, motivos=[f"Erro ao processar MPC: {e}"], latencia=pos_latencia)
def _verifica_tempo_maximo_execucao(self, t_init, processo): def _verifica_tempo_maximo_execucao(self, t_init, processo):
@ -1007,7 +1010,7 @@ class ControladorMPC:
mostrar_log(f"🚨 Tempo maximo de execucao excedido: {delta}/{limite}, processo: {processo}") mostrar_log(f"🚨 Tempo maximo de execucao excedido: {delta}/{limite}, processo: {processo}")
return hot_stop return hot_stop
def _comando_fallback_hot_stop(self, comando_anterior, latencia=0): def _comando_fallback_hot_stop(self, comando_anterior, motivos, latencia=0):
_cmd = { _cmd = {
"enviar_comando": True, "enviar_comando": True,
"parada_necessaria": True, "parada_necessaria": True,
@ -1019,7 +1022,8 @@ class ControladorMPC:
"erro_latreral": 0, "erro_latreral": 0,
"erro_orientacao": 0, "erro_orientacao": 0,
"debug_custo": {}, "debug_custo": {},
"candidatos_testados": 0 "candidatos_testados": 0,
"motivos": motivos
} }
return _cmd return _cmd

View File

@ -73,7 +73,7 @@ class ProcessadorEmAndamento(ProcessadorBase):
latencia=comando_dir.get("latencia", 0.0), latencia=comando_dir.get("latencia", 0.0),
erro_lateral=comando_dir.get("erro_lateral", 0.0), erro_lateral=comando_dir.get("erro_lateral", 0.0),
debug_custo=comando_dir.get("debug_custo", {}), debug_custo=comando_dir.get("debug_custo", {}),
motivos=[] motivos=comando_dir.get("motivos", []) if comando_dir.get("erro", False) else []
) )
except Exception as e: except Exception as e:
from manager_worker.config import mostrar_log from manager_worker.config import mostrar_log

View File

@ -40,7 +40,8 @@ def comando_controle(percentual_velocidade: float, frear: bool, angulo: float, t
"heartbeat": hb_novo, "heartbeat": hb_novo,
"latencia": latencia, "latencia": latencia,
"erro_lateral": erro_lateral, "erro_lateral": erro_lateral,
"debug_custo": debug_custo "debug_custo": debug_custo,
"motivos": motivos
} }
def comando_parado(motivos=None): def comando_parado(motivos=None):

View File

@ -382,9 +382,9 @@ class ContextoGlobalRedis:
trajetoria_ok = True trajetoria_ok = True
if not trajetoria_liberada: if not trajetoria_liberada:
trajetoria_ok = False trajetoria_ok = False
_motivo_traj = _t.get("motivo") _motivos_traj = _t.get("motivos", [])
if _motivo_traj: if len(_motivos_traj) > 0:
motivos_dos_modulos_mandatorios.append(_motivo_traj) motivos_dos_modulos_mandatorios.extend(_motivos_traj)
controle_ok = True controle_ok = True
_motivos_controle = cls.get_controle().get("motivos", []) _motivos_controle = cls.get_controle().get("motivos", [])

View File

@ -0,0 +1,139 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace OperationControl.Controls
{
public class AutoFitTextBlock : TextBlock
{
public static readonly DependencyProperty MinFontSizeProperty =
DependencyProperty.Register(
nameof(MinFontSize),
typeof(double),
typeof(AutoFitTextBlock),
new PropertyMetadata(10.0, OnAutoFitPropertyChanged));
public static readonly DependencyProperty MaxLinesProperty =
DependencyProperty.Register(
nameof(MaxLines),
typeof(int),
typeof(AutoFitTextBlock),
new PropertyMetadata(2, OnAutoFitPropertyChanged));
private double _baseFontSize = 14;
private bool _isAdjusting;
public double MinFontSize
{
get => (double)GetValue(MinFontSizeProperty);
set => SetValue(MinFontSizeProperty, value);
}
public int MaxLines
{
get => (int)GetValue(MaxLinesProperty);
set => SetValue(MaxLinesProperty, value);
}
public AutoFitTextBlock()
{
Loaded += AutoFitTextBlock_Loaded;
SizeChanged += (_, _) => AdjustFontSize();
}
private void AutoFitTextBlock_Loaded(object sender, RoutedEventArgs e)
{
_baseFontSize = FontSize;
WatchProperty(TextProperty);
WatchProperty(FontSizeProperty);
WatchProperty(FontFamilyProperty);
WatchProperty(FontStyleProperty);
WatchProperty(FontWeightProperty);
WatchProperty(FontStretchProperty);
WatchProperty(TextWrappingProperty);
AdjustFontSize();
}
private void WatchProperty(DependencyProperty dp)
{
var descriptor = DependencyPropertyDescriptor.FromProperty(dp, typeof(AutoFitTextBlock));
descriptor?.AddValueChanged(this, (_, _) =>
{
if (!_isAdjusting && dp == FontSizeProperty)
_baseFontSize = FontSize;
AdjustFontSize();
});
}
private static void OnAutoFitPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is AutoFitTextBlock tb)
tb.AdjustFontSize();
}
private void AdjustFontSize()
{
if (_isAdjusting || !IsLoaded)
return;
if (ActualWidth <= 0 || string.IsNullOrWhiteSpace(Text))
return;
_isAdjusting = true;
try
{
double startFont = _baseFontSize;
double minFont = MinFontSize;
FontSize = startFont;
while (FontSize > minFont)
{
if (Fits(FontSize))
return;
FontSize -= 0.5;
}
FontSize = minFont;
}
finally
{
_isAdjusting = false;
}
}
private bool Fits(double fontSize)
{
double pixelsPerDip = VisualTreeHelper.GetDpi(this).PixelsPerDip;
var formattedText = new FormattedText(
Text ?? string.Empty,
CultureInfo.CurrentUICulture,
FlowDirection,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
fontSize,
Foreground,
pixelsPerDip)
{
MaxTextWidth = Math.Max(0, ActualWidth),
Trimming = TextTrimming.None
};
if (TextWrapping == TextWrapping.Wrap)
formattedText.MaxLineCount = MaxLines;
double lineHeight = fontSize * 1.25;
double maxAllowedHeight = lineHeight * MaxLines;
return formattedText.Height <= maxAllowedHeight + 1;
}
}
}

View File

@ -366,7 +366,7 @@ namespace OperationControl.ViewModels
{ {
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{ {
_viewOperacaoTop?._vm.AtualizarStatus(status, titulo, linha1, linha2, status_str, resumo); _viewOperacaoTop?._vm.AtualizarStatus(status, titulo, linha1, linha2.Replace("\n\n", " • "), status_str, resumo);
})); }));
} }
@ -546,8 +546,12 @@ namespace OperationControl.ViewModels
if (rover.RoverId != VariaveisControleOperacao.SelectedRoverId) return; if (rover.RoverId != VariaveisControleOperacao.SelectedRoverId) return;
string motivos_trajetoria = string.Join("", obj.Trajetoria?.AutonomiaCorredor?.Motivos ?? new List<string>() { "Liberado" });
string motivos_controle = string.Join("", obj.Controle?.Motivos ?? new List<string>() { "Liberado" });
AtualizarBarraSuperior(obj.StatusRover, rover.RoverId, $"Trajetória: {motivos_trajetoria}", $"Controle: {motivos_controle}", obj.StatusRover.ToString(), (obj.Operacao?.Status ?? StatusOperacao.NaoIniciado).ToString());
// ESQUERDA // ESQUERDA
var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == AgroBase.Models.Enums.T_Code.Ipb); var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
double c = conexao?.saude ?? 0; double c = conexao?.saude ?? 0;
double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value<double?>() ?? 0.0; double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value<double?>() ?? 0.0;
double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value<double?>() ?? 0.0; double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value<double?>() ?? 0.0;
@ -559,12 +563,12 @@ namespace OperationControl.ViewModels
rover?.Descricao ?? "", rover?.Descricao ?? "",
obj.Controle?.PercentualVelocidadeSPKmh ?? 0, obj.Controle?.PercentualVelocidadeSPKmh ?? 0,
obj.Controle?.Angulo ?? 0, obj.Controle?.Angulo ?? 0,
obj.Controle?.TipoMovimento ?? AgroBase.Models.Enums.TipoMovimentoDirecional.Diagnostico, obj.Controle?.TipoMovimento ?? TipoMovimentoDirecional.Diagnostico,
obj.Refrigeracao?.Temperatura ?? 0, obj.Refrigeracao?.Temperatura ?? 0,
obj.Bateria?.PercentualBateria ?? 0, obj.Bateria?.PercentualBateria ?? 0,
obj.Atuador?.PercentualReservatorio ?? 0, obj.Atuador?.PercentualReservatorio ?? 0,
$"{c}%", $"{c}%",
$"{obj.Gnss?.QualidadeFix ?? AgroBase.Models.Enums.TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm" $"{obj.Gnss?.QualidadeFix ?? TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm"
); );
// DIREITA (RESUMO) // DIREITA (RESUMO)

View File

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:OperationControl.Controls"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="1400" d:DesignWidth="1400"
d:DesignHeight="92"> d:DesignHeight="92">
@ -41,8 +42,8 @@
<Border Grid.Column="2" Margin="10,0,10,0" Background="#14000000" CornerRadius="10" Padding="14,8"> <Border Grid.Column="2" Margin="10,0,10,0" Background="#14000000" CornerRadius="10" Padding="14,8">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="30"/> <RowDefinition Height="26"/>
<RowDefinition Height="24"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- STATUS PRINCIPAL --> <!-- STATUS PRINCIPAL -->
@ -51,9 +52,7 @@
</Viewbox> </Viewbox>
<!-- MOTIVO --> <!-- MOTIVO -->
<Viewbox Grid.Row="1" Stretch="Uniform" StretchDirection="DownOnly" HorizontalAlignment="Left" VerticalAlignment="Center" Height="18"> <controls:AutoFitTextBlock Grid.Row="1" Text="{Binding MotivoPrincipal}" Foreground="#F2F2F2" FontSize="14" FontWeight="SemiBold" MinFontSize="10" MaxLines="2" TextWrapping="Wrap" VerticalAlignment="Center"/>
<TextBlock Text="{Binding MotivoPrincipal}" Foreground="#F2F2F2" FontSize="14" FontWeight="SemiBold" TextWrapping="NoWrap"/>
</Viewbox>
</Grid> </Grid>
</Border> </Border>