diff --git a/AgroBase/AgroBase/Models/TrajetoriaMapaOperacaoModel.cs b/AgroBase/AgroBase/Models/TrajetoriaMapaOperacaoModel.cs index e29f8cf33..1e34a1f0e 100644 --- a/AgroBase/AgroBase/Models/TrajetoriaMapaOperacaoModel.cs +++ b/AgroBase/AgroBase/Models/TrajetoriaMapaOperacaoModel.cs @@ -2795,7 +2795,7 @@ namespace AgroBase.Models if (!Iniciado) { Liberado = true; - Motivos.Add("Não iniciado"); + Motivos = new List() { "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 diff --git a/AgroBase/AgroBase/Models/Variaveis.cs b/AgroBase/AgroBase/Models/Variaveis.cs index d7ed697e1..71ebe8b71 100644 --- a/AgroBase/AgroBase/Models/Variaveis.cs +++ b/AgroBase/AgroBase/Models/Variaveis.cs @@ -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"; diff --git a/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs index dc6f97c51..05919f710 100644 --- a/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs @@ -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 ), diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py index 229a4a99e..122e1aee4 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py @@ -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 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/bateria.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/bateria.py index ff04a53d2..677042ef7 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/bateria.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/bateria.py @@ -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)", diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/sensoriamento.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/sensoriamento.py index b92f9f961..b52625e04 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/sensoriamento.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/sensoriamento.py @@ -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({ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/trajetoria.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/trajetoria.py index b96404a34..bd4ba7f09 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/trajetoria.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/trajetoria.py @@ -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) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/_3_aguardando.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/_3_aguardando.py index 09b792d4d..e3a9f816f 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/_3_aguardando.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/_3_aguardando.py @@ -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}") diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py index 86e737d45..a81e8ce63 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/contexto_global_redis.py @@ -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) diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs index 79300e25f..7ed390648 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoTopViewModel.cs @@ -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; } + } } \ No newline at end of file diff --git a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs index d2efc4b14..f5c8cdea5 100644 --- a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs @@ -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(); + + // ============================ + // 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(); + + 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(); + + 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(); + + 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(); + + 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.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() { "Liberado" }); - string motivos_controle = string.Join("", obj.Controle?.Motivos ?? new List() { "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)