Ajustes de regras no health worker, Sen, Atu, Bat, Trj, preenchido dinamicamente top bar do operation control

This commit is contained in:
Diego Freitas 2026-03-13 15:58:02 -03:00
parent 7398624ad7
commit ffbd86d8a4
11 changed files with 344 additions and 78 deletions

View File

@ -2795,7 +2795,7 @@ namespace AgroBase.Models
if (!Iniciado)
{
Liberado = true;
Motivos.Add("Não iniciado");
Motivos = new List<string>() { "Não iniciado" };
Status = AutonomiaCorredorStatus.Desconhecido;
return;
}
@ -2948,7 +2948,10 @@ namespace AgroBase.Models
motivoFinal = $"Corredor {idxCorredor + 1}: LIBERAÇÃO MANUAL (override). " +
(string.IsNullOrEmpty(motivoBase) ? "Autonomia insuficiente, mas liberado." : motivoBase);
motivosBloqueio.Add($"Corredor {idxCorredor + 1}: LIBERAÇÃO MANUAL. Autonomia insuficiente, mas houver liberação supervisionada.");
motivosBloqueio.Add($"Corredor {idxCorredor + 1}: LIBERAÇÃO MANUAL. Autonomia insuficiente, mas houve liberação supervisionada.");
motivosBloqueio.Clear();
// Destrava o corredor (o operador assumiu)
_corredoresTravados.Remove(idxCorredor);
@ -3010,7 +3013,7 @@ namespace AgroBase.Models
else
{
CorredoresLiberados.Add(idxCorredor, res);
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(T_Code.Trj, liberadoFinal ? StatusModulo.Operante : StatusModulo.Alerta, liberadoFinal ? 100 : 0, $"Corredor {idxCorredor + 1} bloqueado por autonomia insuficiente");
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(T_Code.Trj, liberadoFinal ? StatusModulo.Operante : StatusModulo.Alerta, liberadoFinal ? 100 : 0, $"{motivoFinal}");
}
// Atualiza estado exposto

View File

@ -532,8 +532,8 @@ namespace AgroBase.Models
{ TipoMovimentoDirecional.MovimentoLateral, 90 },
{ TipoMovimentoDirecional.Diagnostico, 25 },
};
public static double AnguloInclinacaoRollMax { get; set; } = 30.0;
public static double AnguloInclinacaoPitchMax { get; set; } = 45.0;
public static double AnguloInclinacaoRollMax { get; set; } = 30.0; // Frontal
public static double AnguloInclinacaoPitchMax { get; set; } = 45.0; // Lateral
public static string VozAlerta { get; set; } = "Microsoft Maria Desktop";

View File

@ -147,10 +147,11 @@ namespace AgroBase.Services.Operadores
dist_dir = _t.DistanciaDireita,
angulo = _t.AnguloMedioCorredor,
pontos_restantes = _t.CorredorAtual.QtdPontos - pontoComparar.idxPontoCorredor,
//liberado = _t.AutonomiaCooredor?.Liberado ?? false,
//motivo = _t.AutonomiaCooredor?.Motivo ?? "",
//bateria_ok = _t.AutonomiaCooredor?.BateriaSuficiente ?? false,
//herbicida_ok = _t.AutonomiaCooredor?.HerbicidaSuficiente ?? false
liberado = _t.AutonomiaCooredor?.Liberado ?? false,
motivo_bat = _t.AutonomiaCooredor?.Motivos?.FirstOrDefault(x => x.Contains("Bateria")) ?? "",
bateria_ok = _t.AutonomiaCooredor?.BateriaSuficiente ?? false,
motivo_hrb = _t.AutonomiaCooredor?.Motivos?.FirstOrDefault(x => x.Contains("Herbicida")) ?? "",
herbicida_ok = _t.AutonomiaCooredor?.HerbicidaSuficiente ?? false
},
} : null
),

View File

@ -30,7 +30,8 @@ class ModuloAtuador(ModuloDiagnosticoBase):
FREQ_MIN = FREQ_BASE * 0.5
SAUDE_MIN_ALERTA = 80
sensor_massa_ativado = False
sensor_massa_em_uso = False
sensor_massa_conectado = False
sensores_ativos = dados_atu.get("sensores", {}).get("ativos", [])
for key in sensores_ativos:
@ -48,7 +49,9 @@ class ModuloAtuador(ModuloDiagnosticoBase):
padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
conectado = dados_sensor.get("conectado", False)
mandatorio = dados_sensor.get("mandatorio", False)
if dados_sensor.get("label") == "MASRS" and conectado and mandatorio and aferir: sensor_massa_ativado = True
if dados_sensor.get("label") == "MASRS":
sensor_massa_em_uso = mandatorio and aferir
sensor_massa_conectado = conectado
if not conectado:
saude = 0
motivos.append(f"desconectado")
@ -284,39 +287,39 @@ class ModuloAtuador(ModuloDiagnosticoBase):
cond_op_mod = []
penalidade_percentual_herbicida = 0
if sensor_massa_ativado:
_contexto = ContextoGlobalRedis.get_contexto()
herbicida_suficiente_corredor = _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("herbicida_ok", True)
percent_atual = round(_contexto.get("Gerais", {}).get("percentual_reservatorio", 0.0), 2)
percent_min = ContextoGlobalRedis.get_equipamento().get("percentual_reservatorio_min", 5.0)
if percent_atual < percent_min:
penalidade_percentual_herbicida = 50
if sensor_massa_em_uso:
if sensor_massa_conectado:
_contexto = ContextoGlobalRedis.get_contexto()
herbicida_suficiente_corredor = _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("herbicida_ok", True)
percent_atual = round(_contexto.get("Gerais", {}).get("percentual_reservatorio", 0.0), 2)
percent_min = ContextoGlobalRedis.get_equipamento().get("percentual_reservatorio_min", 5.0)
if percent_atual < percent_min:
penalidade_percentual_herbicida = 50
cond_op_mod.append({
"valor": percent_atual,
"severidade": 100,
"descricao": f"Nível do reservatório de herbicida abaixo do limite mínimo!"
})
elif percent_atual < 20.0:
penalidade_percentual_herbicida = 21
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": f"Nível do reservatório de herbicida baixo!"
})
if not herbicida_suficiente_corredor:
_motivo_herb = _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("motivo_hrb", "Herbicida insuficiente")
cond_op_mod.append({
"valor": percent_atual,
"severidade": 90,
"descricao": _motivo_herb
})
else:
cond_op_mod.append({
"valor": percent_atual,
"valor": 0,
"severidade": 100,
"descricao": f"Nível do reservatório de herbicida abaixo do limite mínimo!"
"descricao": f"Sensor do nível do reservatório desconectado!"
})
elif percent_atual < 20.0:
penalidade_percentual_herbicida = 21
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": f"Nível do reservatório de herbicida baixo!"
})
if not herbicida_suficiente_corredor:
penalidade_percentual_herbicida = 21
_motivo_herb = _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("motivo", "Herbicida insuficiente")
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": _motivo_herb
})
else:
cond_op_mod.append({
"valor": 0,
"severidade": 100,
"descricao": f"Sensor do nível do reservatório desconectado!"
})
saude_final = (saude_total // total_ativos) if total_ativos > 0 else 0
saude_final = saude_final - penalidade_percentual_herbicida

View File

@ -59,7 +59,7 @@ class ModuloBateria(ModuloDiagnosticoBase):
delta_cells = v_max - v_min
# ------------ SCORES PARCIAIS ------------
# 1) Score de comunicação / frescor
if not dados:
score_com = 0.0
@ -73,20 +73,23 @@ class ModuloBateria(ModuloDiagnosticoBase):
# 2) Score de SOC
if soc is None:
score_soc = 60.0 # penaliza por não ter SOC
score_soc = 10.0 # penaliza por não ter SOC
motivos.append("SOC da bateria indisponível / não informado pelo BMS.")
else:
score_soc = 100.0
percent_min = ContextoGlobalRedis.get_equipamento().get("percentual_tensao_bateria_min", 5.0)
if soc <= 3:
score_soc = 20.0
score_soc = 10.0
motivos.append(f"SOC crítico ({soc:.1f}%).")
elif soc <= 10:
score_soc = 40.0
score_soc = 19.0
motivos.append(f"SOC muito baixo ({soc:.1f}%).")
elif soc <= 20:
score_soc = 60.0
score_soc = 40.0
motivos.append(f"SOC baixo ({soc:.1f}%).")
else:
score_soc = 100.0
if soc < percent_min:
score_soc -= 15.0
motivos.append(f"Nível da bateria abaixo do limite mínimo ({soc:.1f}/{percent_min:.1f}%)")
# 3) Score de desbalanceamento entre células
if delta_cells is None:
@ -165,7 +168,21 @@ class ModuloBateria(ModuloDiagnosticoBase):
# 1) SOC
cond_soc = []
if score_soc < SAUDE_MIN_ALERTA:
_corredor_atual = ContextoGlobalRedis.get_contexto().get("Trajetoria", {}).get("CorredorAtual", {})
bateria_suficiente_corredor = _corredor_atual.get("bateria_ok", True)
if not bateria_suficiente_corredor:
c = {
"label": "Estado de carga (SOC)",
"valor": soc if soc is not None else -1,
"severidade": 100,
"descricao": _corredor_atual.get("motivo_bat", "Bateria insuficiente"),
"acoes": [
"Trocar a bateria para retomar a operação em andamento."
]
}
condicoes.append(c)
cond_soc.append(c)
if score_soc < 50:
severidade = int(max(0, 100 - score_soc))
c = {
"label": "Estado de carga (SOC)",

View File

@ -31,8 +31,8 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
FREQ_MIN = FREQ_BASE * 0.5
SAUDE_MIN_ALERTA = 80
bms_ligado = dados_sen.get("bms_ligado", False)
sensor_bateria_ativado = False
sensor_bateria_em_uso = False
sensor_bateria_conectado = False
sensores_ativos = dados_sen.get("sensores", {}).get("ativos", [])
for key in sensores_ativos:
@ -50,7 +50,10 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
conectado = dados_sensor.get("conectado", False)
mandatorio = dados_sensor.get("mandatorio", False)
if dados_sensor.get("label") == "AB36V" and conectado and mandatorio and aferir: sensor_bateria_ativado = True
if dados_sensor.get("label") == "AB36V":
bms_conetado = dados_sen.get("bms_ligado", False)
sensor_bateria_em_uso = not bms_conetado and mandatorio and aferir
sensor_bateria_conectado = conectado
if not conectado:
saude = 0
motivos.append(f"desconectado")
@ -272,33 +275,46 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
cond_op_mod = []
penalidade_percentual_bateria = 0
if not bms_ligado:
if sensor_bateria_ativado:
if sensor_bateria_em_uso:
if sensor_bateria_conectado:
_contexto = ContextoGlobalRedis.get_contexto()
bateria_suficiente_corredor = _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("bateria_ok", True)
percent_atual = _contexto.get("Gerais", {}).get("percentual_bateria", 0.0)
percent_min = ContextoGlobalRedis.get_equipamento().get("percentual_tensao_bateria_min", 5.0)
if percent_atual < percent_min:
penalidade_percentual_bateria = 70
if percent_atual <= 3.0:
penalidade_percentual_bateria = 90
cond_op_mod.append({
"valor": percent_atual,
"severidade": 100,
"descricao": f"Nível da bateria abaixo do limite mínimo!"
"descricao": f"SOC crítico ({percent_atual:.1f}%)!"
})
elif percent_atual < 25.0:
penalidade_percentual_bateria = 25
elif percent_atual <= 10.0:
penalidade_percentual_bateria = 75
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": f"Nível da bateria baixo!"
"severidade": 90,
"descricao": f"SOC muito baixo ({percent_atual:.1f}%)!"
})
elif percent_atual <= 20.0:
penalidade_percentual_bateria = 30
cond_op_mod.append({
"valor": percent_atual,
"severidade": 50,
"descricao": f"SOC baixo ({percent_atual:.1f}%)!"
})
if percent_atual < percent_min:
penalidade_percentual_bateria += 20
cond_op_mod.append({
"valor": percent_atual,
"severidade": 100,
"descricao": f"Nível da bateria abaixo do limite mínimo ({percent_atual:.1f}/{percent_min:.1f}%)"
})
if not bateria_suficiente_corredor:
penalidade_percentual_bateria = 25
_motivo_bat = _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("motivo", "Bateria insuficiente")
penalidade_percentual_bateria += 15
cond_op_mod.append({
"valor": percent_atual,
"severidade": 30,
"descricao": _motivo_bat
"severidade": 100,
"descricao": _contexto.get("Trajetoria", {}).get("CorredorAtual", {}).get("motivo_bat", "Bateria insuficiente")
})
else:
cond_op_mod.append({

View File

@ -12,23 +12,26 @@ class ModuloTrajetorira(ModuloDiagnosticoBase):
try:
m = ContextoGlobalRedis.get_modulo(self.t_code) or {}
SAUDE_MIN_ALERTA = 50
SAUDE_MIN_ALERTA = 80
iniciado = bool(m.get("iniciado", False))
liberado = bool(m.get("liberado", False))
bateria_ok = bool(m.get("bateria_ok", False))
herbicida_ok = bool(m.get("herbicida_ok", False))
motivo = m.get("motivo", "")
motivos = m.get("motivos", [])
saude = 100
motivos = []
if iniciado:
if not bateria_ok:
saude -= 50
saude -= 30
if not herbicida_ok:
saude -= 50
motivos.append(motivo)
saude -= 30
if (not bateria_ok or not herbicida_ok) and liberado:
saude += 15
motivos.append(f"Realizada liberação supervisionada! Bateria {'OK' if bateria_ok else 'ALERTA'}, Herbicida {'OK' if herbicida_ok else 'ALERTA'}")
else:
motivos = []
saude = max(saude, 0)

View File

@ -1,10 +1,12 @@
from .base import ProcessadorBase
from manager_worker.processadores.padroes import comando_parado
from manager_worker.modulos.regras_taticas import verifica_controle_liberado
class ProcessadorAguardando(ProcessadorBase):
def processar(self):
try:
return comando_parado()
motivos_parada = verifica_controle_liberado(ignorar_parada=True)
return comando_parado(motivos=motivos_parada)
except Exception as e:
from manager_worker.config import mostrar_log
mostrar_log(f"Erro no processador Aguardando: {e}")

View File

@ -240,7 +240,7 @@ class ContextoGlobalRedis:
condicoes_operacionais = _saude.get("condicoes_operacionais", [])
detalhes = _saude.get("detalhes")
tem_condicao_critica = any(cond.get("severidade") == 100 for cond in condicoes_operacionais)
tem_condicao_critica = any(cond.get("severidade") >= 90 for cond in condicoes_operacionais)
mod_mandatorio = t_code.value in modulos_mandatorios
mod_operante = status in [StatusModulo.OPERANTE.value, StatusModulo.ALERTA.value]
mod_inoperante = mod_mandatorio and tem_condicao_critica
@ -252,7 +252,7 @@ class ContextoGlobalRedis:
descricoes_criticas = [
cond.get("descricao", "Condição crítica sem descrição")
for cond in condicoes_operacionais
if cond.get("severidade") == 100
if cond.get("severidade") >= 90
]
# adiciona ao array de motivos
motivos_dos_modulos_mandatorios.extend(descricoes_criticas)

View File

@ -163,4 +163,14 @@ namespace OperationControl.ViewModels.Views.Operacao
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome));
}
}
public class MotivoTopBarModel
{
public int Prioridade { get; set; }
public string Fonte { get; set; } = "";
public string Titulo { get; set; } = "";
public string Descricao { get; set; } = "";
public StatusModulo StatusVisual { get; set; }
public T_Code? Modulo { get; set; }
}
}

View File

@ -362,6 +362,219 @@ namespace OperationControl.ViewModels
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome));
}
public void AtualizarBarraSuperior(OperacaoParametrosModel rover)
{
var obj = rover?.DadosLeitura;
if (rover == null || obj == null)
return;
var eventos = new List<MotivoTopBarModel>();
// ============================
// 1) Emergência
// ============================
if (obj.Operacao?.Emergencia == true)
{
eventos.Add(new MotivoTopBarModel
{
Prioridade = 1000,
Fonte = "Emergencia",
Titulo = "OPERAÇÃO BLOQUEADA POR EMERGÊNCIA",
Descricao = "Parada de emergência solicitada.",
StatusVisual = StatusModulo.Falha
});
}
// ============================
// 2) Pausa
// ============================
if (obj.Operacao?.Pausa == true)
{
eventos.Add(new MotivoTopBarModel
{
Prioridade = 900,
Fonte = "Pausa",
Titulo = "OPERAÇÃO PAUSADA",
Descricao = "Pausa operacional solicitada.",
StatusVisual = StatusModulo.Alerta
});
}
// ============================
// 3) Controle
// ============================
var motivosControle = obj.Controle?.Motivos?
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct()
.ToList() ?? new List<string>();
foreach (var motivo in motivosControle)
{
eventos.Add(new MotivoTopBarModel
{
Prioridade = 800,
Fonte = "Controle",
Titulo = "CONTROLE SEM LIBERAÇÃO",
Descricao = motivo,
StatusVisual = StatusModulo.Alerta
});
}
// ============================
// 4) Trajetória / autonomia
// ============================
var motivosTrajetoria = obj.Trajetoria?.AutonomiaCorredor?.Motivos?
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct()
.ToList() ?? new List<string>();
foreach (var motivo in motivosTrajetoria)
{
eventos.Add(new MotivoTopBarModel
{
Prioridade = 700,
Fonte = "Trajetoria",
Titulo = "TRAJETÓRIA BLOQUEADA",
Descricao = motivo,
StatusVisual = StatusModulo.Alerta
});
}
// ============================
// 5) Módulos com condições operacionais críticas
// ============================
var modulosCriticos = obj.ModulosSaude?
.Where(mod => mod?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false)
.ToList() ?? new List<AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel>();
foreach (var modulo in modulosCriticos)
{
var nomeModulo = (modulo?.modulo ?? T_Code.Vzo).ToString();
var descricoesCriticas = modulo?.condicoes_operacionais?
.Where(cond => cond?.severidade > 90)
.Select(cond => cond?.descricao ?? "")
.Where(desc => !string.IsNullOrWhiteSpace(desc))
.Distinct()
.ToList() ?? new List<string>();
foreach (var desc in descricoesCriticas)
{
eventos.Add(new MotivoTopBarModel
{
Prioridade = 600,
Fonte = "ModuloCritico",
Titulo = "CONDIÇÕES OPERACIONAIS CRÍTICAS",
Descricao = $"{nomeModulo}: {desc}",
StatusVisual = StatusModulo.Alerta,
Modulo = modulo?.modulo
});
}
}
var ordemModulos = new Dictionary<T_Code, int>
{
{ T_Code.Ipb, 1 },
{ T_Code.Npc, 2 },
{ T_Code.Bat, 3 },
{ T_Code.Atu, 4 },
{ T_Code.Sen, 5 }
};
// ============================
// 6) Remove vazios / repetidos
// ============================
var eventosOrdenados = eventos
.Where(x => !string.IsNullOrWhiteSpace(x.Titulo) && !string.IsNullOrWhiteSpace(x.Descricao))
.GroupBy(x => new { x.Fonte, x.Titulo, x.Descricao, x.Prioridade, x.StatusVisual })
.Select(g => g.First())
.OrderByDescending(x => x.Prioridade)
.ThenBy(x => x.Modulo.HasValue && ordemModulos.ContainsKey(x.Modulo.Value)? ordemModulos[x.Modulo.Value] : int.MaxValue)
.ThenBy(x => x.Fonte)
.ToList();
// ============================
// 7) Monta visual final
// ============================
StatusModulo statusVisual;
string linha1;
string linha2;
string badge;
string resumo;
if (eventosOrdenados.Any())
{
var principal = eventosOrdenados.First();
statusVisual = principal.StatusVisual;
linha1 = principal.Titulo;
// Pega até 3 descrições distintas, por prioridade
var descricoes = eventosOrdenados
.Select(x => x.Descricao?.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct()
.Take(3)
.ToList();
linha2 = string.Join(" • ", descricoes);
// Badge curto
badge = principal.Fonte switch
{
"Emergencia" => "EMERGÊNCIA",
"Pausa" => "PAUSADO",
"Trajetoria" => "TRAJETÓRIA",
"Controle" => "CONTROLE",
"ModuloCritico" => "ALERTA",
_ => statusVisual.ToString().ToUpper()
};
// Resumo curto
resumo = principal.Fonte switch
{
"Emergencia" => "Parada imediata",
"Pausa" => "Aguardando retomada",
"Trajetoria" => "Operação bloqueada",
"Controle" => "Controle bloqueado",
"ModuloCritico" => "Atenção operacional",
_ => (obj.Operacao?.Status ?? StatusOperacao.NaoIniciado).ToString()
};
}
else
{
statusVisual = obj.StatusRover == StatusModulo.Desconectado
? StatusModulo.Desconectado
: StatusModulo.Operante;
linha1 = "ROVER OPERANDO NORMALMENTE";
linha2 = "Trajetória, controle e operação em condição normal.";
badge = statusVisual.ToString().ToUpper();
resumo = (obj.Operacao?.Status ?? StatusOperacao.NaoIniciado).ToString();
}
// Segurança extra: evita linha vazia
if (string.IsNullOrWhiteSpace(linha2))
linha2 = "Sem detalhes adicionais.";
Variaveis.MostrarLog($"LINHA 1: {linha1}");
// ============================
// 8) Atualiza UI
// ============================
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_viewOperacaoTop?._vm.AtualizarStatus(
statusVisual,
rover.RoverId,
linha1,
linha2,
badge,
resumo
);
}));
}
public void AtualizarBarraSuperior(StatusModulo status, string titulo, string linha1, string linha2, string status_str, string resumo)
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
@ -546,9 +759,7 @@ namespace OperationControl.ViewModels
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());
AtualizarBarraSuperior(rover);
// ESQUERDA
var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
@ -715,7 +926,7 @@ namespace OperationControl.ViewModels
AtualizarDadosMapa(VariaveisControleOperacao.RoverEmFoco);
AtualizarBarraSuperior(rover.Status, rover.NumeroSerie, "VISÃO GERAL", VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.ImpedimentoErro ?? "Sistema operante", rover.Status.ToString(), (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Status ?? StatusOperacao.NaoIniciado).ToString());
AtualizarBarraSuperior(VariaveisControleOperacao.RoverEmFoco);
}
}
private void OnSecaoSelecionadaChanged(SecaoRightBar secao)