inclusao de motivos de controle e trajetoria
This commit is contained in:
parent
1ff65eeb67
commit
7398624ad7
|
|
@ -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>())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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", [])
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue