diff --git a/AgroBase/AgroBase/Models/GeneralJoystick.cs b/AgroBase/AgroBase/Models/GeneralJoystick.cs index 3079a75fa..8dade58dd 100644 --- a/AgroBase/AgroBase/Models/GeneralJoystick.cs +++ b/AgroBase/AgroBase/Models/GeneralJoystick.cs @@ -28,7 +28,9 @@ namespace AgroBase new DeParaComandos() { key = Keys.Right, Dispositivo = T_Code.Dir, botaoJoy = BotoesJoystick.AEDireita, PararAoSoltar = true }, new DeParaComandos() { key = Keys.R, Dispositivo = T_Code.Dir, botaoJoy = BotoesJoystick.R3, PararAoSoltar = false }, new DeParaComandos() { key = Keys.Up, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.Xis, PararAoSoltar = true }, + new DeParaComandos() { key = Keys.Up, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.R2A, PararAoSoltar = true }, new DeParaComandos() { key = Keys.Down, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.Quadrado, PararAoSoltar = true }, + new DeParaComandos() { key = Keys.Down, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.L2A, PararAoSoltar = true }, new DeParaComandos() { key = Keys.Space, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.Bolinha, PararAoSoltar = true }, new DeParaComandos() { key = Keys.Subtract, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.L1, PararAoSoltar = false }, new DeParaComandos() { key = Keys.Add, Dispositivo = T_Code.Mov, botaoJoy = BotoesJoystick.R1, PararAoSoltar = false }, @@ -602,7 +604,7 @@ namespace AgroBase percent = FuncoesMatematicas.Clamp(percent, 0, 100); - Console.WriteLine($"botao: {botao}, valor: {valor}, percentual: {percent * 100.0:F2}%"); + //Console.WriteLine($"botao: {botao}, valor: {valor}, percentual: {percent * 100.0:F2}%"); var pControle = Variaveis.OperacaoEmAndamento.Parametros.Controle; var _Controle = Variaveis.OperacaoEmAndamento.Controle; @@ -687,13 +689,13 @@ namespace AgroBase Pressionado = true; } // Controle Direcao dos Motores BLDC (MOV) - else if (Comando.Comando == Keys.Up && UltimaDirecao != Direcao.Frente) + else if (Comando.Comando == Keys.Up) // && UltimaDirecao != Direcao.Frente { Comando.Dispositivo = T_Code.Mov; DirecaoAtual = Direcao.Frente; Pressionado = true; } - else if (Comando.Comando == Keys.Down && UltimaDirecao != Direcao.Tras) + else if (Comando.Comando == Keys.Down) // && UltimaDirecao != Direcao.Tras { Comando.Dispositivo = T_Code.Mov; DirecaoAtual = Direcao.Tras; @@ -706,13 +708,13 @@ namespace AgroBase Pressionado = true; } // Controle de Rotação dos Motores de Passo (DIR) - else if (Comando.Comando == Keys.Left && UltimaDirecao != Direcao.Esquerda) + else if (Comando.Comando == Keys.Left) // && UltimaDirecao != Direcao.Esquerda { Comando.Dispositivo = T_Code.Dir; DirecaoAtual = Direcao.Esquerda; Pressionado = true; } - else if (Comando.Comando == Keys.Right && UltimaDirecao != Direcao.Direita) + else if (Comando.Comando == Keys.Right) // && UltimaDirecao != Direcao.Direita { Comando.Dispositivo = T_Code.Dir; DirecaoAtual = Direcao.Direita; @@ -765,7 +767,8 @@ namespace AgroBase { case T_Code.Mov: { - Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.ForEach(x => x.MovMotor.EnviarComandoControle(DirecaoAtual)); + //Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.ForEach(x => x.MovMotor.EnviarComandoControle(DirecaoAtual)); + Variaveis.OperacaoEmAndamento.AtualizarDadosControle(false, new List() { Comando.Dispositivo }); break; } case T_Code.Dir: @@ -783,19 +786,20 @@ namespace AgroBase } else { - foreach (var mod in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos) - { - if (modsControlar == null || modsControlar.Contains(mod.DirMotor.Mod_ID)) - { - mod.DirMotor.EnviarComandoControle(DirecaoAtual); - - if (mod.MovMotor.CompensacaoNecessaria) - { - Direcao ultimaDirecaoMov = Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov)?.UltimaDirecao ?? Direcao.Parado; - mod.MovMotor.EnviarComandoControle(ultimaDirecaoMov); - } - } - }; + Variaveis.OperacaoEmAndamento.AtualizarDadosControle(false, new List() { Comando.Dispositivo }); + //foreach (var mod in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos) + //{ + // if (modsControlar == null || modsControlar.Contains(mod.DirMotor.Mod_ID)) + // { + // mod.DirMotor.EnviarComandoControle(DirecaoAtual); + // + // if (mod.MovMotor.CompensacaoNecessaria) + // { + // Direcao ultimaDirecaoMov = Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov)?.UltimaDirecao ?? Direcao.Parado; + // mod.MovMotor.EnviarComandoControle(ultimaDirecaoMov); + // } + // } + //}; } break; } diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 44133cd60..2317bb9d5 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -510,6 +510,12 @@ namespace AgroBase.Models }; p.ModulosMandatorios = new List() { + new OperacaoModulosMandatoriosModel() + { + Dispositivo = T_Code.Ipb, + Mandatorio = true, + Utilizar = true, + }, new OperacaoModulosMandatoriosModel() { Dispositivo = T_Code.Mov, @@ -772,6 +778,12 @@ namespace AgroBase.Models }; p.ModulosMandatorios = new List() { + new OperacaoModulosMandatoriosModel() + { + Dispositivo = T_Code.Ipb, + Mandatorio = true, + Utilizar = true, + }, new OperacaoModulosMandatoriosModel() { Dispositivo = T_Code.Mov, @@ -832,7 +844,6 @@ namespace AgroBase.Models p.ModulosMandatorios.Add(new OperacaoModulosMandatoriosModel() { Dispositivo = T_Code.Npc, Mandatorio = true, Utilizar = true }); p.ModulosMandatorios.Add(new OperacaoModulosMandatoriosModel() { Dispositivo = T_Code.Can, Mandatorio = true, Utilizar = true }); - p.ModulosMandatorios.Add(new OperacaoModulosMandatoriosModel() { Dispositivo = T_Code.Ipb, Mandatorio = true, Utilizar = true }); p.ModulosMandatorios.Add(new OperacaoModulosMandatoriosModel() { Dispositivo = T_Code.Bat, Mandatorio = true, Utilizar = true }); p.ModulosMandatorios.ForEach(m => m.ComponentesEmUso = Variaveis.OperacaoEmAndamento.PreencherComponentesEmUso(m.Dispositivo)); @@ -1298,7 +1309,7 @@ namespace AgroBase.Models foreach (var Ctrl in _Controle.TiposControle.Where(x => Dispositivos.Contains(x.Tipo))) { - if (!ForcarEnvio) + if (!ForcarEnvio && false) { if (mm.Any(x => x.Dispositivo == Ctrl.Tipo && !x.Utilizar)) continue; @@ -1360,6 +1371,7 @@ namespace AgroBase.Models bool envioNecessario = ForcarEnvio || _Controle.RPM_SP != _ControleAnterior.RPM_SP; if (envioNecessario || modsAtualizar.Any()) { + Console.WriteLine($"Vai enviar comando MOV"); foreach (var Mod in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => envioNecessario ? true : modsAtualizar.Contains(x.Modulo_ID))) { Mod.MovMotor.EnviarComandoControle(_Controle.Direcao); @@ -1402,6 +1414,7 @@ namespace AgroBase.Models var modsAtualizar = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(Mod => !Mod.DirMotor.AtingiuAnguloSP).Select(x => x.Modulo_ID).ToList(); if (!MKS057DCanService.Referenciando && (envioNecessario || modsAtualizar.Any())) { + Console.WriteLine($"Vai enviar comando DIR"); foreach (var Mod in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => envioNecessario ? true : modsAtualizar.Contains(x.Modulo_ID))) { Mod.DirMotor.EnviarComandoControle(_Controle.Direcao); diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/ponte_ip.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/ponte_ip.py index 3a27db0b1..31d542328 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/ponte_ip.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/ponte_ip.py @@ -87,6 +87,15 @@ class ModuloIPBribge(ModuloDiagnosticoBase): self.bw_safe_mbps = 05.0 # alvo saudável (até aqui não penaliza) self.bw_hard_mbps = 2.5 # acima disso é zona vermelha + self.link_state = "OK" # OK | DEGRADED | CRITICAL + self._cycles_degraded = 0 + self._cycles_critical = 0 + self._cycles_recovered = 0 + self._distancia_inicio_degradado = None + self._distancia_inicio_critico = None + self._max_distancia_ok = 0.0 + self._max_distancia_desde_degradado = 0.0 + def _start_ping_async_if_needed(self): @@ -558,11 +567,11 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # LATÊNCIA if rtts: avg_rtt = statistics.mean(rtts) - if avg_rtt <= 30: latency_score = 100 - elif avg_rtt <= 50: latency_score = 80 - elif avg_rtt <= 100: latency_score = 50 - elif avg_rtt <= 200: latency_score = 20 - else: latency_score = 0 + if avg_rtt <= 40: latency_score = 100 + elif avg_rtt <= 70: latency_score = 85 + elif avg_rtt <= 120: latency_score = 65 + elif avg_rtt <= 200: latency_score = 35 + else: latency_score = 10 else: latency_score = 0 @@ -572,10 +581,10 @@ class ModuloIPBribge(ModuloDiagnosticoBase): else: timeout_pct = 100.0 - if timeout_pct <= 5: timeout_score = 100 - elif timeout_pct <= 10: timeout_score = 80 - elif timeout_pct <= 15: timeout_score = 50 - elif timeout_pct <= 30: timeout_score = 20 + if timeout_pct <= 2: timeout_score = 100 + elif timeout_pct <= 5: timeout_score = 85 + elif timeout_pct <= 10: timeout_score = 60 + elif timeout_pct <= 20: timeout_score = 30 else: timeout_score = 0 # LOSS @@ -584,22 +593,24 @@ class ModuloIPBribge(ModuloDiagnosticoBase): else: loss_pct = 100.0 - if loss_pct <= 2: loss_score = 100 - elif loss_pct <= 5: loss_score = 80 - elif loss_pct <= 8: loss_score = 50 - elif loss_pct <= 15: loss_score = 20 + if loss_pct <= 1: loss_score = 100 + elif loss_pct <= 3: loss_score = 90 + elif loss_pct <= 5: loss_score = 75 + elif loss_pct <= 8: loss_score = 55 + elif loss_pct <= 12: loss_score = 30 else: loss_score = 0 # JITTER if len(rtts) > 2: jitter = statistics.pstdev(rtts) - if jitter <= 10: jitter_score = 100 - elif jitter <= 20: jitter_score = 70 - elif jitter <= 40: jitter_score = 30 - else: jitter_score = 0 + if jitter <= 12: jitter_score = 100 + elif jitter <= 25: jitter_score = 85 + elif jitter <= 40: jitter_score = 65 + elif jitter <= 70: jitter_score = 40 + else: jitter_score = 15 else: jitter = 0.0 - jitter_score = 1 + jitter_score = 85 # NIC ERRORS if nic_error_rates: @@ -607,17 +618,18 @@ class ModuloIPBribge(ModuloDiagnosticoBase): else: err_rate = 0.0 - if err_rate <= 0.1: nic_score = 100 - elif err_rate <= 1: nic_score = 70 - elif err_rate <= 5: nic_score = 40 - else: nic_score = 0 + if err_rate <= 0.05: nic_score = 100 + elif err_rate <= 0.2: nic_score = 90 + elif err_rate <= 1.0: nic_score = 70 + elif err_rate <= 3.0: nic_score = 45 + else: nic_score = 20 # HEARTBEAT (igual) - t0, t1 = 1.0, 3.0 - x = (atraso - t0) / (t1 - t0) - x = max(0.0, min(1.0, x)) - gamma = 1.6 - hb_score = int(round(100 * (1.0 - (x ** gamma)))) + if atraso <= 1.5: hb_score = 100 + elif atraso <= 3.0: hb_score = 80 + elif atraso <= 5.0: hb_score = 50 + elif atraso <= 8.0: hb_score = 20 + else: hb_score = 0 return ( latency_score, timeout_score, loss_score, jitter_score, nic_score, hb_score, @@ -797,38 +809,50 @@ class ModuloIPBribge(ModuloDiagnosticoBase): bw_score_s, bw_util_pct_s, bw_state_s = self._bandwidth_score(bw_total_s) health_f = ( - 0.20 * ls_f + - 0.10 * tmout_f + + 0.15 * ls_f + + 0.18 * tmout_f + 0.25 * los_f + - 0.10 * js_f + - 0.10 * ns_f + - 0.10 * hbs_f + - 0.15 * bw_score_f + 0.08 * js_f + + 0.05 * ns_f + + 0.22 * hbs_f + + 0.07 * bw_score_f ) health_s = ( - 0.20 * ls_s + - 0.10 * tmout_s + + 0.15 * ls_s + + 0.18 * tmout_s + 0.25 * los_s + - 0.10 * js_s + - 0.10 * ns_s + - 0.10 * hbs_s + - 0.15 * bw_score_s + 0.08 * js_s + + 0.05 * ns_s + + 0.22 * hbs_s + + 0.07 * bw_score_s ) - health = 0.70 * health_s + 0.30 * health_f - + health = 0.80 * health_s + 0.20 * health_f + # Se o FAST gritar "caos", derruba a saúde na hora (não espera a slow) - if loss_pct_f >= 8 or tmout_pct_f >= 10: + if loss_pct_f >= 12: + health = min(health, 55) + elif loss_pct_f >= 8 and loss_pct_s >= 5: + health = min(health, 65) + + # Timeout + if tmout_pct_f >= 20: health = min(health, 45) + elif tmout_pct_f >= 10 and tmout_pct_s >= 5: + health = min(health, 60) # Se saturou banda no FAST, corta também (comandos atrasam) - if bw_util_pct_f >= 95: - health = min(health, 55) + if bw_util_pct_f >= 98 and (loss_pct_f >= 5 or tmout_pct_f >= 5): + health = min(health, 65) # Se heartbeat atrasou muito, isso é sério - if atraso_f >= 3.0: - health = min(health, 40) + if atraso_f >= 8.0: + health = min(health, 30) + elif atraso_f >= 5.0: + health = min(health, 45) + elif atraso_f >= 3.0 and atraso_s >= 2.0: + health = min(health, 60) saude = round(health, 1) @@ -872,7 +896,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 1) Latência cond_lat = [] if ls_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - ls_f)) + severidade = min(89, int(max(0, 100 - ls_f))) c = { "label": "Latência ICMP", "valor": avg_rtt_f if avg_rtt_f is not None else -1, @@ -905,7 +929,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 2) Timeouts cond_timeout = [] if tmout_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - tmout_f)) + severidade = severidade = min(89, int(max(0, 100 - tmout_f))) c = { "label": "Timeout", "valor": round(tmout_pct_f, 2), @@ -934,7 +958,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 2) Perda de pacotes cond_loss = [] if los_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - los_f)) + severidade = severidade = min(89, int(max(0, 100 - los_f))) c = { "label": "Perda de Pacotes", "valor": round(loss_pct_f, 2), @@ -963,7 +987,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 3) Jitter cond_jit = [] if js_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - js_f)) + severidade = severidade = min(89, int(max(0, 100 - js_f))) c = { "label": "Jitter (variação de latência)", "valor": round(jitter_f, 2), @@ -992,7 +1016,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 4) Erros na NIC cond_nic = [] if ns_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - ns_f)) + severidade = severidade = min(89, int(max(0, 100 - ns_f))) c = { "label": "Erros na interface de rede", "valor": round(err_rate_f, 3), @@ -1021,7 +1045,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 5) Heartbeat cond_hb = [] if hbs_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - hbs_f)) + severidade = severidade = min(89, int(max(0, 100 - hbs_f))) c = { "label": "Atraso de heartbeat", "valor": round(atraso_f, 2), @@ -1050,7 +1074,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 6) Banda / Saturação cond_bw = [] if bw_score_f < SAUDE_MIN_ALERTA: - severidade = int(max(0, 100 - bw_score_f)) + severidade = severidade = min(89, int(max(0, 100 - bw_score_f))) c = { "label": "Uso de banda", "valor": round(bw_util_pct_f, 1), @@ -1079,14 +1103,188 @@ class ModuloIPBribge(ModuloDiagnosticoBase): "em_uso": True, }) + + + # RISCO REAL + risk_score = ( + 0.32 * (100 - los) + + 0.24 * (100 - tmout) + + 0.22 * (100 - hbs) + + 0.10 * (100 - ls) + + 0.07 * (100 - js) + + 0.03 * (100 - bw_score) + + 0.02 * (100 - ns) + ) + risk_score = max(0.0, min(100.0, risk_score)) + + degradado_agora = ( + loss_pct_s >= 5 or + tmout_pct_s >= 4 or + atraso_s >= 4.0 or + (loss_pct_f >= 8 and loss_pct_s >= 3) or + (tmout_pct_f >= 10 and tmout_pct_s >= 3) or + (jitter_s >= 35 and loss_pct_s >= 3) + ) + + critico_agora = ( + loss_pct_s >= 10 or + tmout_pct_s >= 8 or + atraso_s >= 6.0 or + (loss_pct_f >= 15 and loss_pct_s >= 5) or + (tmout_pct_f >= 20 and tmout_pct_s >= 5) or + ((loss_pct_s >= 6) and (atraso_s >= 5.0)) + ) + + colapso_agora = ( + loss_pct_s >= 15 or + tmout_pct_s >= 12 or + atraso_s >= 8.0 or + not conectado + ) + + if colapso_agora: + self._cycles_critical += 2 + self._cycles_degraded += 1 + self._cycles_recovered = 0 + elif critico_agora: + self._cycles_critical += 1 + self._cycles_degraded += 1 + self._cycles_recovered = 0 + elif degradado_agora: + self._cycles_degraded += 1 + self._cycles_critical = max(0, self._cycles_critical - 1) + self._cycles_recovered = 0 + else: + self._cycles_recovered += 1 + self._cycles_degraded = max(0, self._cycles_degraded - 1) + self._cycles_critical = max(0, self._cycles_critical - 1) + + self._cycles_degraded = min(self._cycles_degraded, 30) + self._cycles_critical = min(self._cycles_critical, 30) + self._cycles_recovered = min(self._cycles_recovered, 30) + + prev_state = self.link_state + if self._cycles_critical >= 8: + self.link_state = "CRITICAL" + elif self._cycles_degraded >= 12: + self.link_state = "DEGRADED" + elif self._cycles_recovered >= 20: + self.link_state = "OK" + + # entrou em DEGRADED agora + if prev_state != "DEGRADED" and self.link_state == "DEGRADED": + self._distancia_inicio_degradado = distancia_base if distancia_base >= 0 else 0.0 + self._max_distancia_desde_degradado = self._distancia_inicio_degradado + + # enquanto estiver degradado, atualiza a maior distância observada + if self.link_state == "DEGRADED" and distancia_base is not None and distancia_base >= 0: + self._max_distancia_desde_degradado = max( + self._max_distancia_desde_degradado, + distancia_base + ) + + # entrou em CRITICAL agora + if prev_state != "CRITICAL" and self.link_state == "CRITICAL": + self._distancia_inicio_critico = distancia_base if distancia_base >= 0 else 0.0 + + # voltou para OK, limpa marcos + if prev_state != "OK" and self.link_state == "OK": + self._distancia_inicio_degradado = None + self._distancia_inicio_critico = None + self._max_distancia_desde_degradado = 0.0 + + if self.link_state == "OK" and distancia_base >= 0: + self._max_distancia_ok = max(self._max_distancia_ok, distancia_base) + + # Condição 1: Link degradado persistente + if self.link_state == "DEGRADED": + condicoes.append({ + "label": "Enlace degradado persistente", + "valor": round(risk_score, 1), + "severidade": 75, + "descricao": ( + f"Rede degradando de forma persistente. " + f"Loss slow {loss_pct_s:.1f}%, timeout slow {tmout_pct_s:.1f}%, " + f"heartbeat {atraso_s:.1f}s. Não aumentar distância da base." + ), + "acoes": [ + "Bloquear avanço para longe da base", + "Reduzir vídeo/logs/telemetria não crítica", + "Permitir apenas manter posição ou retornar" + ] + }) + # Condição 2: Cerca invisível de rede violada + dist_violando_cerca = ( + self.link_state == "DEGRADED" and + self._distancia_inicio_degradado is not None and + distancia_base is not None and + distancia_base >= 0 and + self._max_distancia_desde_degradado > self._distancia_inicio_degradado + 2.0 and + distancia_base > self._distancia_inicio_degradado + 1.0 + ) + if dist_violando_cerca: + condicoes.append({ + "label": "Avanço além da margem segura de rede", + "valor": round(distancia_base, 2), + "severidade": 92, + "descricao": ( + f"O rover continuou se afastando após a rede entrar em degradação persistente. " + f"Distância atual {distancia_base:.1f} m." + ), + "acoes": [ + "Parar avanço imediatamente", + "Retornar em direção à base", + "Manter apenas tráfego essencial" + ] + }) + #Condição 3: Link crítico persistente + if self.link_state == "CRITICAL": + condicoes.append({ + "label": "Risco crítico de perda de comunicação", + "valor": round(risk_score, 1), + "severidade": 96, + "descricao": ( + f"Enlace crítico e persistente. " + f"Loss slow {loss_pct_s:.1f}%, timeout slow {tmout_pct_s:.1f}%, " + f"heartbeat {atraso_s:.1f}s." + ), + "acoes": [ + "Parar operação", + "Iniciar retorno seguro", + "Priorizar mensagens de controle e heartbeat" + ] + }) + #Condição 4: Colapso iminente + if colapso_agora and self._cycles_critical >= 1: + condicoes.append({ + "label": "Colapso iminente do enlace", + "valor": round(risk_score, 1), + "severidade": 99, + "descricao": ( + f"Perda iminente de comunicação. " + f"Loss slow {loss_pct_s:.1f}%, timeout slow {tmout_pct_s:.1f}%, " + f"heartbeat {atraso_s:.1f}s." + ), + "acoes": [ + "Parada imediata", + "Abortar avanço", + "Executar fail-safe de retorno" + ] + }) + + + + + # -------- STATUS GERAL DO MÓDULO -------- - status = StatusModulo.OPERANTE if not conectado: status = StatusModulo.DESCONECTADO - elif saude <= 0: + elif self.link_state == "CRITICAL": status = StatusModulo.FALHA - elif saude < SAUDE_MIN_ALERTA: + elif self.link_state == "DEGRADED" or saude < SAUDE_MIN_ALERTA: status = StatusModulo.ALERTA + else: + status = StatusModulo.OPERANTE payload = { "conectado": conectado, @@ -1119,6 +1317,18 @@ class ModuloIPBribge(ModuloDiagnosticoBase): "bw_safe_mbps": self.bw_safe_mbps, "bw_hard_mbps": self.bw_hard_mbps, "bw_pressure_pct": bw_pressure_pct, + + "link_state": self.link_state, + "risk_score": round(risk_score, 1), + "cycles_degraded": self._cycles_degraded, + "cycles_critical": self._cycles_critical, + "cycles_recovered": self._cycles_recovered, + "pode_avancar": self.link_state == "OK", + "deve_conter_avanco": self.link_state == "DEGRADED", + "deve_parar_por_rede": self.link_state == "CRITICAL", + "distancia_inicio_degradado": self._distancia_inicio_degradado, + "distancia_inicio_critico": self._distancia_inicio_critico, + "max_distancia_ok": self._max_distancia_ok, } } diff --git a/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs b/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs index 2df88b2b4..2ab5e94d3 100644 --- a/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs +++ b/AgroBase/OperationControl/Controls/MapViewControl.xaml.cs @@ -127,7 +127,10 @@ namespace OperationControl.Controls public event EventHandler? StreetMapClicked; private bool ParametrizandoOperacao => - (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Modo ?? ModoOperacao.NaoDefinido) == ModoOperacao.MapaGPS && + ( + (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Modo ?? ModoOperacao.NaoDefinido) == ModoOperacao.MapaGPS || + (VariaveisControleOperacao.RoverEmFoco?.Modo ?? ModoOperacao.NaoDefinido) == ModoOperacao.MapaGPS + ) && !(VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Iniciada ?? false) && (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Status ?? StatusOperacao.NaoIniciado) == StatusOperacao.Parametrizando && ((App)System.Windows.Application.Current).Shell.Dock?._vm?._viewOperacaoRight?._vm?.SecaoAtual == ViewModels.Views.Operacao.SecaoRightBar.Parametrizacao; diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ParametrizacaoViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ParametrizacaoViewModel.cs index 19173ab16..4de50602d 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ParametrizacaoViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ParametrizacaoViewModel.cs @@ -35,6 +35,8 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento } } + private AgroBase.Models.Enums.ModoOperacao? modoOperacaoAtual { get; set; } + public ParametrizacaoViewModel() { CarregarParametrosPadraoCommand = new RelayCommand(p => CarregarParametrosPadrao((AgroBase.Models.Enums.ModoOperacao)p)); @@ -56,8 +58,10 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento private void CarregarParametrosPadrao(AgroBase.Models.Enums.ModoOperacao modo) { var p = OperacaoModel.CarregarParametrosOperacaoPadrao(modo, false); + modoOperacaoAtual = p.Modo; p.Descricao = $"{p.Modo} - Padrão"; Parametros = p; + VariaveisControleOperacao.RoverEmFoco.Modo = modo; } private void ReiniciarOperacao() @@ -69,6 +73,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento { if (origem == null) return; Parametros = origem; + modoOperacaoAtual = origem.Modo; OperacaoConcluida = (VariaveisControleOperacao.RoverEmFoco?.DadosLeitura?.Operacao?.Status ?? AgroBase.Models.Enums.StatusOperacao.NaoIniciado) == AgroBase.Models.Enums.StatusOperacao.Concluido; }