From a7ac637032c86fd433e9ecf4983ddbf037e185fe Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Thu, 19 Mar 2026 07:55:56 -0300 Subject: [PATCH] Ajustes em IMU, IPB, Alertas, Top Bar, Left Bar, Controle, Redis --- AgroBase/AgroBase/Models/OAKCameraModel.cs | 7 + .../Models/Operacoes/OperacaoModel.cs | 15 + AgroBase/AgroBase/Models/Variaveis.cs | 5 + .../Operadores/HealthWorkerService.cs | 1454 +++++++++-------- .../workers/health_worker/modulos/imu.py | 311 +++- .../workers/health_worker/modulos/ponte_ip.py | 99 +- .../manager_worker/modulos/regras_taticas.py | 2 +- AgroBase/OperationControl/Models/AppShell.cs | 21 + AgroBase/OperationControl/Models/Variaveis.cs | 2 +- .../Monitoramento/ControleManualViewModel.cs | 4 + .../Diagnostico/DiagnosticoViewModel.cs | 11 +- .../Monitoramento/MonitoramentoViewModel.cs | 15 + .../Views/Operacao/OperacaoBottomViewModel.cs | 42 +- .../Views/Operacao/OperacaoLeftViewModel.cs | 43 +- .../ViewModels/Windows/DockWindowViewModel.cs | 88 +- 15 files changed, 1233 insertions(+), 886 deletions(-) diff --git a/AgroBase/AgroBase/Models/OAKCameraModel.cs b/AgroBase/AgroBase/Models/OAKCameraModel.cs index af7f0edcc..7d64a7201 100644 --- a/AgroBase/AgroBase/Models/OAKCameraModel.cs +++ b/AgroBase/AgroBase/Models/OAKCameraModel.cs @@ -170,10 +170,17 @@ namespace AgroBase.Models { public double timestamp { get; set; } public double roll { get; set; } + public double roll_seg { get; set; } public double pitch { get; set; } + public double pitch_seg { get; set; } public double yaw { get; set; } public double yaw_fixed { get; set; } public double vel_mps { get; set; } + public bool em_movimento { get; set; } + public double movimento_idx { get; set; } + public double rugosidade_idx { get; set; } + public double impacto_idx { get; set; } + public double estabilidade_idx { get; set; } public double latencia { get; set; } public double frequencia { get; set; } } diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index f99818f48..9e68f8edb 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -2407,6 +2407,11 @@ namespace AgroBase.Models InclinacaoFrontal = imu_iniciado ? sIMU.roll : 0, Rotacao = imu_iniciado ? sIMU.yaw : 0, RotacaoCorrigida = imu_iniciado ? sIMU.yaw_fixed : 0, + EmMovimento = imu_iniciado ? sIMU.em_movimento : false, + IndiceMovimento = imu_iniciado ? sIMU.movimento_idx : 0, + IndiceRugosidade = imu_iniciado ? sIMU.rugosidade_idx : 0, + IndiceEstabilidade = imu_iniciado ? sIMU.estabilidade_idx : 0, + IndiceImpacto = imu_iniciado ? sIMU.impacto_idx : 0, } : new OperacaoSensoriamentoLogImuModel(); var dadosAtuador = _DispAtu != null ? new OperacaoSensoriamentoLogAtuModel() @@ -2693,6 +2698,11 @@ namespace AgroBase.Models public double InclinacaoFrontal { get; set; } public double Rotacao { get; set; } public double RotacaoCorrigida { get; set; } + public bool EmMovimento { get; set; } + public double IndiceMovimento { get; set; } + public double IndiceRugosidade { get; set; } + public double IndiceEstabilidade { get; set; } + public double IndiceImpacto { get; set; } public OperacaoSensoriamentoLogImuModel Clone() { @@ -2703,6 +2713,11 @@ namespace AgroBase.Models InclinacaoFrontal = InclinacaoFrontal, Rotacao = Rotacao, RotacaoCorrigida = RotacaoCorrigida, + EmMovimento = EmMovimento, + IndiceMovimento = IndiceMovimento, + IndiceImpacto = IndiceImpacto, + IndiceEstabilidade = IndiceEstabilidade, + IndiceRugosidade = IndiceRugosidade, }; } } diff --git a/AgroBase/AgroBase/Models/Variaveis.cs b/AgroBase/AgroBase/Models/Variaveis.cs index fe7692302..7b6066b41 100644 --- a/AgroBase/AgroBase/Models/Variaveis.cs +++ b/AgroBase/AgroBase/Models/Variaveis.cs @@ -402,6 +402,11 @@ namespace AgroBase.Models + public static void MostrarLog(string Mensagem) + { + Console.WriteLine($"[AGROBASE] {Mensagem}"); + } + } diff --git a/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs index 0c42f2af8..723a10972 100644 --- a/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/HealthWorkerService.cs @@ -17,7 +17,7 @@ namespace AgroBase.Services.Operadores public static List ModulosSaude = new List(); private bool DebugMode = true; - private static bool AtualizandoRedis = false; + //private static bool AtualizandoRedis = false; public void MostrarLog(string message, int limite = 500) { @@ -82,760 +82,808 @@ namespace AgroBase.Services.Operadores public void AtualizarSaude() { - Saude = OperadoresModels.AtualizarSaude(CtxKey.DadosHealthWorker); + try + { + Saude = OperadoresModels.AtualizarSaude(CtxKey.DadosHealthWorker); + } + catch (Exception ex) + { + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarSaude: {ex.Message}"); + } } private static void AtualizarDadosContextoLocal() { - RedisService.AtualizarCampos( - CtxKey.DadosOperacao, - ("debug_mode", Variaveis.DebugMode), - ("iniciado", Variaveis.OperacaoEmAndamento.Iniciado), - ("finalizando", Variaveis.OperacaoEmAndamento.Finalizando), - ("calibrando", Variaveis.OperacaoEmAndamento.Calibrando), - ("simulando", Variaveis.OperacaoEmAndamento.Simulando), - ("emergencia", Variaveis.OperacaoEmAndamento.Emergencia), - ("pausa", Variaveis.OperacaoEmAndamento.Pausa) - ); + try + { + RedisService.AtualizarCampos( + CtxKey.DadosOperacao, + ("debug_mode", Variaveis.DebugMode), + ("iniciado", Variaveis.OperacaoEmAndamento.Iniciado), + ("finalizando", Variaveis.OperacaoEmAndamento.Finalizando), + ("calibrando", Variaveis.OperacaoEmAndamento.Calibrando), + ("simulando", Variaveis.OperacaoEmAndamento.Simulando), + ("emergencia", Variaveis.OperacaoEmAndamento.Emergencia), + ("pausa", Variaveis.OperacaoEmAndamento.Pausa) + ); + } + catch (Exception ex) + { + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosContextoLocal: {ex.Message}"); + } } public static void AtualizarDadosContexto() { - if (AtualizandoRedis) - return; - AtualizandoRedis = true; - - AtualizarDadosContextoLocal(); - AtualizarDadosModulos(); - AtualizarDadosControleOperacao(); - - var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento; - - if (_Sensoriamento == null) + try { - AtualizandoRedis = false; - return; - } + //if (AtualizandoRedis) + // return; + //AtualizandoRedis = true; - var _t = _Sensoriamento.Trajetoria; - PontoTrajetoriaModel pontoComparar = _t != null ? (_t.PontoAtual.Aproximando ? _t.PontoAtual : _t.ProximoPonto != null ? _t.ProximoPonto : _t.PontoAtual) : null; - int idx_p_alvo = _t != null ? - (pontoComparar.DistanciaAtual > 1.0 ? pontoComparar.idxPonto : - pontoComparar.idxPonto < _t.QtdPontos - 1 ? pontoComparar.idxPonto + 1 : - pontoComparar.idxPonto) : 0; - RedisService.AtualizarCampos( - CtxKey.DadosContexto, - ("Trajetoria", _t != null ? new - { - status = (int)_t.StatusCarro, - concluido = _t.TrajetoriaConcluida, - manobrando = _t.ManobrandoEntreRuas, - erro_angular = _t.ErroOrientacaoAngularCombinado, - erro_angular_caminho = _t.ErroOrientacaoAngularCaminho, - erro_angular_proximo_ponto = _t.ErroOrientacaoAngularProximoPonto, - idx_proximo_ponto = _t.ProximoPonto.idxPonto, - idx_ponto_alvo = idx_p_alvo, - CorredorAtual = new + AtualizarDadosContextoLocal(); + AtualizarDadosModulos(); + AtualizarDadosControleOperacao(); + + var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento; + + if (_Sensoriamento == null) + { + //AtualizandoRedis = false; + return; + } + + var _t = _Sensoriamento.Trajetoria; + PontoTrajetoriaModel pontoComparar = _t != null ? (_t.PontoAtual.Aproximando ? _t.PontoAtual : _t.ProximoPonto != null ? _t.ProximoPonto : _t.PontoAtual) : null; + int idx_p_alvo = _t != null ? + (pontoComparar.DistanciaAtual > 1.0 ? pontoComparar.idxPonto : + pontoComparar.idxPonto < _t.QtdPontos - 1 ? pontoComparar.idxPonto + 1 : + pontoComparar.idxPonto) : 0; + RedisService.AtualizarCampos( + CtxKey.DadosContexto, + ("Trajetoria", _t != null ? new { - concluido = _t.CorredorAtual.Concluido, - dentro = _t.CorredorAtual.Dentro, - ultimo = _t.CorredorAtual.Ultimo, - largura = _t.CorredorAtual.Largura, - progresso = _t.CorredorAtual.Progresso, - dist_esq = _t.DistanciaEsquerda, - dist_dir = _t.DistanciaDireita, - angulo = _t.AnguloMedioCorredor, - pontos_restantes = _t.CorredorAtual.QtdPontos - pontoComparar.idxPontoCorredor, - 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 - ), - ("Gerais", new - { - velocidade_ms = Variaveis.OperacaoEmAndamento.Simulando ? FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.Controle.RPM_SP) : _Sensoriamento.Movimentacao?.VelocidadeMedia ?? 0, - ervas_no_radar = _Sensoriamento.Atuador?.ErvasNoRadar ?? false, - percentual_ervas_no_radar = _Sensoriamento.Atuador?.PercentualErvasNoRadar ?? 0, - percentual_reservatorio = _Sensoriamento.Atuador?.PercentualReservatorio ?? 0, - percentual_bateria = _Sensoriamento.Bateria?.PercentualBateria ?? 0, - } - ) - ); + status = (int)_t.StatusCarro, + concluido = _t.TrajetoriaConcluida, + manobrando = _t.ManobrandoEntreRuas, + erro_angular = _t.ErroOrientacaoAngularCombinado, + erro_angular_caminho = _t.ErroOrientacaoAngularCaminho, + erro_angular_proximo_ponto = _t.ErroOrientacaoAngularProximoPonto, + idx_proximo_ponto = _t.ProximoPonto.idxPonto, + idx_ponto_alvo = idx_p_alvo, + CorredorAtual = new + { + concluido = _t.CorredorAtual.Concluido, + dentro = _t.CorredorAtual.Dentro, + ultimo = _t.CorredorAtual.Ultimo, + largura = _t.CorredorAtual.Largura, + progresso = _t.CorredorAtual.Progresso, + dist_esq = _t.DistanciaEsquerda, + dist_dir = _t.DistanciaDireita, + angulo = _t.AnguloMedioCorredor, + pontos_restantes = _t.CorredorAtual.QtdPontos - pontoComparar.idxPontoCorredor, + 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 + ), + ("Gerais", new + { + velocidade_ms = Variaveis.OperacaoEmAndamento.Simulando ? FuncoesMatematicas.CalculaVelocidadeMsPercentual(Variaveis.OperacaoEmAndamento.Controle.RPM_SP) : _Sensoriamento.Movimentacao?.VelocidadeMedia ?? 0, + ervas_no_radar = _Sensoriamento.Atuador?.ErvasNoRadar ?? false, + percentual_ervas_no_radar = _Sensoriamento.Atuador?.PercentualErvasNoRadar ?? 0, + percentual_reservatorio = _Sensoriamento.Atuador?.PercentualReservatorio ?? 0, + percentual_bateria = _Sensoriamento.Bateria?.PercentualBateria ?? 0, + } + ) + ); - AtualizandoRedis = false; + //AtualizandoRedis = false; + } + catch (Exception ex) + { + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosContexto: {ex.Message}"); + } } private static void AtualizarDadosEquipamento() { - RedisService.AtualizarCampos( - CtxKey.DadosEquipamento, - ("base_ip", VariaveisEquipamento.Parametros.base_ip), - ("base_porta_caminho", VariaveisEquipamento.Parametros.base_porta_caminho), - ("base_porta_ervas", VariaveisEquipamento.Parametros.base_porta_ervas), - ("serial_number", VariaveisEquipamento.Parametros.serial_number), - ("largura", VariaveisEquipamento.LarguraEquipamentoMm / 1000.0), - ("distancia_entre_eixos", VariaveisEquipamento.DistanciaEntreEixos / 100.0), - ("qtd_bicos", VariaveisEquipamento.QuantidadeBicosPulverizadores), - ("percentual_reservatorio_min", VariaveisEquipamento.PercentualReservatorioMin), - ("percentual_tensao_bateria_min", VariaveisEquipamento.PercentualTensaoBateriaMin), - ("camera_caminho_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id ?? ""), - ("camera_solo_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CamerasSolo?.FirstOrDefault()?.Id ?? ""), - ("path_ia_model_ruas_seg", VersionamentoService.ArquivoModeloSegStreetDetector.CaminhoCompleto), - ("path_ia_labelmap_ruas_seg", VersionamentoService.ArquivoLabelmapSegStreetDetector.CaminhoCompleto), - ("path_ia_norm_stats_ruas_seg", VersionamentoService.ArquivoModeloNormstatsWeedDetector.CaminhoCompleto), - ("ia_backbone_ruas_seg", "nvidia/segformer-b0-finetuned-ade-512-512"), - ("path_ia_model_ruas_det", VersionamentoService.ArquivoModeloDetStreetDetector.CaminhoCompleto), - ("path_ia_model_ervas", VersionamentoService.ArquivoModeloWeedDetector.CaminhoCompleto), - ("path_ia_labelmap_ervas", VersionamentoService.ArquivoModeloLabelmapWeedDetector.CaminhoCompleto), - ("path_ia_norm_stats_ervas", VersionamentoService.ArquivoModeloNormstatsWeedDetector.CaminhoCompleto), - ("ia_backbone_ervas", "nvidia/segformer-b1-finetuned-ade-512-512"), - ("angulo_roll_max", VariaveisEquipamento.AnguloInclinacaoRollMax), - ("angulo_pitch_max", VariaveisEquipamento.AnguloInclinacaoPitchMax) - ); + try + { + RedisService.AtualizarCampos( + CtxKey.DadosEquipamento, + ("base_ip", VariaveisEquipamento.Parametros.base_ip), + ("base_porta_caminho", VariaveisEquipamento.Parametros.base_porta_caminho), + ("base_porta_ervas", VariaveisEquipamento.Parametros.base_porta_ervas), + ("serial_number", VariaveisEquipamento.Parametros.serial_number), + ("largura", VariaveisEquipamento.LarguraEquipamentoMm / 1000.0), + ("distancia_entre_eixos", VariaveisEquipamento.DistanciaEntreEixos / 100.0), + ("qtd_bicos", VariaveisEquipamento.QuantidadeBicosPulverizadores), + ("percentual_reservatorio_min", VariaveisEquipamento.PercentualReservatorioMin), + ("percentual_tensao_bateria_min", VariaveisEquipamento.PercentualTensaoBateriaMin), + ("camera_caminho_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id ?? ""), + ("camera_solo_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CamerasSolo?.FirstOrDefault()?.Id ?? ""), + ("path_ia_model_ruas_seg", VersionamentoService.ArquivoModeloSegStreetDetector.CaminhoCompleto), + ("path_ia_labelmap_ruas_seg", VersionamentoService.ArquivoLabelmapSegStreetDetector.CaminhoCompleto), + ("path_ia_norm_stats_ruas_seg", VersionamentoService.ArquivoModeloNormstatsWeedDetector.CaminhoCompleto), + ("ia_backbone_ruas_seg", "nvidia/segformer-b0-finetuned-ade-512-512"), + ("path_ia_model_ruas_det", VersionamentoService.ArquivoModeloDetStreetDetector.CaminhoCompleto), + ("path_ia_model_ervas", VersionamentoService.ArquivoModeloWeedDetector.CaminhoCompleto), + ("path_ia_labelmap_ervas", VersionamentoService.ArquivoModeloLabelmapWeedDetector.CaminhoCompleto), + ("path_ia_norm_stats_ervas", VersionamentoService.ArquivoModeloNormstatsWeedDetector.CaminhoCompleto), + ("ia_backbone_ervas", "nvidia/segformer-b1-finetuned-ade-512-512"), + ("angulo_roll_max", VariaveisEquipamento.AnguloInclinacaoRollMax), + ("angulo_pitch_max", VariaveisEquipamento.AnguloInclinacaoPitchMax) + ); + } + catch (Exception ex) + { + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosEquipamento: {ex.Message}"); + } } public static void AtualizarDadosOperacao(bool forcar = false) { - if (AtualizandoRedis && !forcar) - return; - - while (AtualizandoRedis && forcar) {} - - AtualizandoRedis = true; - - AtualizarDadosEquipamento(); - - if (Variaveis.OperacaoEmAndamento.DispMvd == null) + try { - AtualizandoRedis = false; - return; - } + //if (AtualizandoRedis && !forcar) + // return; - var _Parametros = Variaveis.OperacaoEmAndamento.Parametros; - var pControle = _Parametros.Controle; - var _Controle = Variaveis.OperacaoEmAndamento.Controle; + //while (AtualizandoRedis && forcar) {} - RedisService.AtualizarCampos( - CtxKey.DadosOperacao, - ("configurado", true), - ("modo", Variaveis.OperacaoEmAndamento.Modo), - ("tempo_aguardar_inicio_operacao", Variaveis.OperacaoEmAndamento.TempoIniciarOperacao), - ("modulos_mandatorios", _Parametros.ModulosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()), - ("modulos_opcionais", _Parametros.ModulosMandatorios.Where(x => !x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()), - ("parametros_mandatorios", _Parametros.ParametrosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Parametro).ToArray()), - ("ponto_mapa_ref", Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?.Select(p => new double[] { p.Posicao.Latitude, p.Posicao.Longitude }).FirstOrDefault()), - ("Mov", new - { - percent_vel_max = pControle.MovVelocidadeSErvasPercent, - percent_vel_min = pControle.MovVelocidadeCErvasPercent, - } - ), - ("Dir", new - { - angulo_max = pControle.DirAnguloMaximo, - vel_movimento = pControle.DirVelocidadeMovimento, - tipo_controle = (int)pControle.DirTipoMovimento, - mpc = new - { - pontos = Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa? - .Select(p => new - { - lat = p.Posicao.Latitude, - lon = p.Posicao.Longitude, - tipo = (int)p.Tipo, - distanciaMargem = p.LarguraCorredor * 0.8 - }) - .ToList(), - angulo_max_graus = pControle.DirAnguloMaximo, - velocidade_min = Math.Round(FuncoesMatematicas.CalculaVelocidadeMsPercentual(pControle.MovVelocidadeCErvasPercent), 4), - velocidade_max = Math.Round(FuncoesMatematicas.CalculaVelocidadeMsPercentual(pControle.MovVelocidadeSErvasPercent), 4), - tempo_entre_comandos = (Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == Enums.T_Code.Dir)?.DelayEnvioComando ?? 500) / 1000.0, - horizonte = pControle.MpcHorizonte, - usa_matriz_custo = pControle.MpcMatrizCusto, - passos_atraso = 1, - } - } - ), - ("Atu", new - { - qtd_bicos = _Parametros.QtdBicos, - tempo_atuacao = pControle.AtuDuracaoAtuacao, - capacidade_reservatorio = _Parametros.CapacidadeReservatorio, - pressao_linha = pControle.AtuPressaoLinha, - percent_vertical_deteccao = pControle.AtuPercentualInicioPulverizacao / 100.0, - height_area_deteccao = pControle.AtuAlturaAreaPulverizacao / 100.0, - pct_erva_bico_on = pControle.AtuPercentualErvasBicoOn / 100.0, - pct_erva_bico_off = pControle.AtuPercentualErvasBicoOff / 100.0 - } - ), - ("Snr", new - { - percentual_altura_solo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.PercentualAlturaSolo, - distancia_maxima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.DistanciaMaximaVisao, - distancia_minima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.DistanciaMinimaVisao - } - ) - ); + //AtualizandoRedis = true; - if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS && (Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?.Count() ?? 0) > 0) - { - var comando = new + AtualizarDadosEquipamento(); + + if (Variaveis.OperacaoEmAndamento.DispMvd == null) { - cmd = HealthWorkerCommandType.AtualizarPontosMapa - }; - RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando)); + //AtualizandoRedis = false; + return; + } + + var _Parametros = Variaveis.OperacaoEmAndamento.Parametros; + var pControle = _Parametros.Controle; + var _Controle = Variaveis.OperacaoEmAndamento.Controle; + + RedisService.AtualizarCampos( + CtxKey.DadosOperacao, + ("configurado", true), + ("modo", Variaveis.OperacaoEmAndamento.Modo), + ("tempo_aguardar_inicio_operacao", Variaveis.OperacaoEmAndamento.TempoIniciarOperacao), + ("modulos_mandatorios", _Parametros.ModulosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()), + ("modulos_opcionais", _Parametros.ModulosMandatorios.Where(x => !x.Mandatorio && x.Utilizar).Select(x => (int)x.Dispositivo).ToArray()), + ("parametros_mandatorios", _Parametros.ParametrosMandatorios.Where(x => x.Mandatorio && x.Utilizar).Select(x => (int)x.Parametro).ToArray()), + ("ponto_mapa_ref", Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?.Select(p => new double[] { p.Posicao.Latitude, p.Posicao.Longitude }).FirstOrDefault()), + ("Mov", new + { + percent_vel_max = pControle.MovVelocidadeSErvasPercent, + percent_vel_min = pControle.MovVelocidadeCErvasPercent, + } + ), + ("Dir", new + { + angulo_max = pControle.DirAnguloMaximo, + vel_movimento = pControle.DirVelocidadeMovimento, + tipo_controle = (int)pControle.DirTipoMovimento, + mpc = new + { + pontos = Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa? + .Select(p => new + { + lat = p.Posicao.Latitude, + lon = p.Posicao.Longitude, + tipo = (int)p.Tipo, + distanciaMargem = p.LarguraCorredor * 0.8 + }) + .ToList(), + angulo_max_graus = pControle.DirAnguloMaximo, + velocidade_min = Math.Round(FuncoesMatematicas.CalculaVelocidadeMsPercentual(pControle.MovVelocidadeCErvasPercent), 4), + velocidade_max = Math.Round(FuncoesMatematicas.CalculaVelocidadeMsPercentual(pControle.MovVelocidadeSErvasPercent), 4), + tempo_entre_comandos = (Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == Enums.T_Code.Dir)?.DelayEnvioComando ?? 500) / 1000.0, + horizonte = pControle.MpcHorizonte, + usa_matriz_custo = pControle.MpcMatrizCusto, + passos_atraso = 1, + } + } + ), + ("Atu", new + { + qtd_bicos = _Parametros.QtdBicos, + tempo_atuacao = pControle.AtuDuracaoAtuacao, + capacidade_reservatorio = _Parametros.CapacidadeReservatorio, + pressao_linha = pControle.AtuPressaoLinha, + percent_vertical_deteccao = pControle.AtuPercentualInicioPulverizacao / 100.0, + height_area_deteccao = pControle.AtuAlturaAreaPulverizacao / 100.0, + pct_erva_bico_on = pControle.AtuPercentualErvasBicoOn / 100.0, + pct_erva_bico_off = pControle.AtuPercentualErvasBicoOff / 100.0 + } + ), + ("Snr", new + { + percentual_altura_solo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.PercentualAlturaSolo, + distancia_maxima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.DistanciaMaximaVisao, + distancia_minima = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.DistanciaMinimaVisao + } + ) + ); + + if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS && (Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?.Any() ?? false)) + { + var comando = new + { + cmd = HealthWorkerCommandType.AtualizarPontosMapa + }; + RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando)); + } + + //Console.WriteLine("Dados de operacao atualizados com sucesso!"); + + //AtualizandoRedis = false; + } + catch (Exception ex) + { + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosOperacao: {ex.Message}"); } - - //Console.WriteLine("Dados de operacao atualizados com sucesso!"); - - AtualizandoRedis = false; } public static void AtualizarDadosControleOperacao() { - var pControle = Variaveis.OperacaoEmAndamento.Parametros.Controle; - if (pControle == null) - return; - RedisService.AtualizarCampos( - CtxKey.DadosControle, - ("pulverizador_automatico", pControle.PulverizadorAutomatico), - ("movimento_automatico", pControle.MovimentoAutomatico), - ("direcional_automatico", pControle.DirecionalAutomatico), - ("frenagem_automatica", pControle.FrenagemAutomaticaAoParar), - ("oak_parada_por_bloqueio", pControle.OakParadaPorObstaculo), - ("imu_parada_por_inclinacao", pControle.ImuParadaPorInclinacao), + try + { + var pControle = Variaveis.OperacaoEmAndamento.Parametros.Controle; + if (pControle == null) + return; + RedisService.AtualizarCampos( + CtxKey.DadosControle, + ("pulverizador_automatico", pControle.PulverizadorAutomatico), + ("movimento_automatico", pControle.MovimentoAutomatico), + ("direcional_automatico", pControle.DirecionalAutomatico), + ("frenagem_automatica", pControle.FrenagemAutomaticaAoParar), + ("oak_parada_por_bloqueio", pControle.OakParadaPorObstaculo), + ("imu_parada_por_inclinacao", pControle.ImuParadaPorInclinacao), - ("registrar_dados_pos_processamento", pControle.RegistrarDadosPosProcessamento) - ); + ("registrar_dados_pos_processamento", pControle.RegistrarDadosPosProcessamento) + ); + } + catch (Exception ex) + { + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosControleOperacao: {ex.Message}"); + } } private static void AtualizarDadosModulos() { - if (Variaveis.OperacaoEmAndamento.DispMvd == null) return; - if (Variaveis.OperacaoEmAndamento.DispAtu == null) return; - if (Variaveis.OperacaoEmAndamento.DispSen == null) return; - - var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento; - var agora = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - - //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Gps))); - //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Dir))); - //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Mov))); - //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Sen))); - - //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Gps), string.Empty); - //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Dir), string.Empty); - //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Mov), string.Empty); - //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Sen), string.Empty); - - var dirAtivos = new List(); - var movAtivos = new List(); - foreach (var mod in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos) + try { - int endereco_dir = mod.DirMotor._EnderecoCAN_Tx; - RedisService.AtualizarCampos( - RedisService.ModKey(Enums.T_Code.Dir), - ($"{endereco_dir}.timestamp", agora), - ($"{endereco_dir}.conectado", mod.DirMotor.Inicializado), - ($"{endereco_dir}.freq_base", Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorDIR.IntervalHz), - ($"{endereco_dir}.endereco", mod.DirMotor._EnderecoCAN_Tx), - ($"{endereco_dir}.mod_id", mod.Modulo_ID), - ($"{endereco_dir}.angulo", mod.DirMotor.AnguloLeitura), - ($"{endereco_dir}.angulo_sp", mod.DirMotor.Angulo_SP), - ($"{endereco_dir}.comandar", mod.DirMotor.Comandar), - ($"{endereco_dir}.referenciado", mod.DirMotor.Referenciado), - ($"{endereco_dir}.in1_atuado", mod.DirMotor.IN1_Atuado), - ($"{endereco_dir}.sentido", mod.DirMotor.Sentido), - ($"{endereco_dir}.sentido_sp", mod.DirMotor.Sentido_SP), - ($"{endereco_dir}.sentido_real", mod.DirMotor.SentidoReal), - ($"{endereco_dir}.freq.pulsos", mod.DirMotor.Leitura?.NumeroPulsos?.frequencia ?? 0), - ($"{endereco_dir}.freq.io", mod.DirMotor.Leitura?.EntradaIN1?.frequencia ?? 0) + if (Variaveis.OperacaoEmAndamento.DispMvd == null) return; + if (Variaveis.OperacaoEmAndamento.DispAtu == null) return; + if (Variaveis.OperacaoEmAndamento.DispSen == null) return; + + var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento; + var agora = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + + //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Gps))); + //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Dir))); + //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Mov))); + //Console.WriteLine(RedisService.Get(RedisService.ModKey(Enums.T_Code.Sen))); + + //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Gps), string.Empty); + //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Dir), string.Empty); + //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Mov), string.Empty); + //RedisService.SetObject(RedisService.ModKey(Enums.T_Code.Sen), string.Empty); + + var dirAtivos = new List(); + var movAtivos = new List(); + foreach (var mod in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos) + { + int endereco_dir = mod.DirMotor._EnderecoCAN_Tx; + RedisService.AtualizarCampos( + RedisService.ModKey(Enums.T_Code.Dir), + ($"{endereco_dir}.timestamp", agora), + ($"{endereco_dir}.conectado", mod.DirMotor.Inicializado), + ($"{endereco_dir}.freq_base", Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorDIR.IntervalHz), + ($"{endereco_dir}.endereco", mod.DirMotor._EnderecoCAN_Tx), + ($"{endereco_dir}.mod_id", mod.Modulo_ID), + ($"{endereco_dir}.angulo", mod.DirMotor.AnguloLeitura), + ($"{endereco_dir}.angulo_sp", mod.DirMotor.Angulo_SP), + ($"{endereco_dir}.comandar", mod.DirMotor.Comandar), + ($"{endereco_dir}.referenciado", mod.DirMotor.Referenciado), + ($"{endereco_dir}.in1_atuado", mod.DirMotor.IN1_Atuado), + ($"{endereco_dir}.sentido", mod.DirMotor.Sentido), + ($"{endereco_dir}.sentido_sp", mod.DirMotor.Sentido_SP), + ($"{endereco_dir}.sentido_real", mod.DirMotor.SentidoReal), + ($"{endereco_dir}.freq.pulsos", mod.DirMotor.Leitura?.NumeroPulsos?.frequencia ?? 0), + ($"{endereco_dir}.freq.io", mod.DirMotor.Leitura?.EntradaIN1?.frequencia ?? 0) + ); + + int endereco_mov = mod.MovMotor._EnderecoCAN_Tx; + RedisService.AtualizarCampos( + RedisService.ModKey(Enums.T_Code.Mov), + ($"{endereco_mov}.timestamp", agora), + ($"{endereco_mov}.conectado", mod.MovMotor.Inicializado), + ($"{endereco_mov}.freq_base", Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorMOV.IntervalHz), + ($"{endereco_mov}.endereco", mod.MovMotor._EnderecoCAN_Tx), + ($"{endereco_mov}.mod_id", mod.Modulo_ID), + ($"{endereco_mov}.cod_erro", mod.MovMotor.CodigoAlarme), + ($"{endereco_mov}.rpm", mod.MovMotor.RPM_Roda), + ($"{endereco_mov}.rpm_sp", mod.MovMotor.RPM_SP_Leitura), + ($"{endereco_mov}.tensao", mod.MovMotor.Tensao), + ($"{endereco_mov}.corrente_motor", mod.MovMotor.Corrente_Motor), + ($"{endereco_mov}.corrente_barramento", mod.MovMotor.Corrente_Barramento), + ($"{endereco_mov}.temperatura_driver", mod.MovMotor.TemperaturaDriver), + ($"{endereco_mov}.temperatura_motor", mod.MovMotor.Temperatura), + ($"{endereco_mov}.freado", mod.MovMotor.Leitura?.Freado ?? false), + ($"{endereco_mov}.freq.erro", mod.MovMotor.Leitura?.CodigoAlarme?.frequencia ?? 0), + ($"{endereco_mov}.freq.rpm", mod.MovMotor.Leitura?.ERPM?.frequencia ?? 0), + ($"{endereco_mov}.freq.tensao", mod.MovMotor.Leitura?.Tensao?.frequencia ?? 0), + ($"{endereco_mov}.freq.corrente_motor", mod.MovMotor.Leitura?.CorrenteMotor?.frequencia ?? 0), + ($"{endereco_mov}.freq.corrente_barramento", mod.MovMotor.Leitura?.CorrenteBarramento?.frequencia ?? 0), + ($"{endereco_mov}.freq.temperatura", mod.MovMotor.Leitura?.Temperatura?.frequencia ?? 0) + ); + + dirAtivos.Add(endereco_dir); + movAtivos.Add(endereco_mov); + } + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Dir), ($"ativos", dirAtivos.ToList())); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Mov), ($"ativos", movAtivos.ToList())); + + var DadosSen = Variaveis.OperacaoEmAndamento.DispSen.Dados; + //Console.WriteLine("[HEALTH SEN] " + DadosSen.Conectado); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), + ("timestamp", agora), + ("conectado", DadosSen.Conectado), + ("freq_base", DadosSen.FrequenciaBase), + ("bms_ligado", _Sensoriamento.Bateria?.BmsLigado ?? false) + ); + var senSensoresAtualizados = new List<(string caminho, object valor)>(); + var senSensoresAtivos = new List(); + foreach (var sensor in DadosSen.Sensores) + { + int tipo = (int)sensor.Componente; + int id = sensor.ID_Num; + string basePath = $"sensores.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); + + var dados = sensor.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + nominal = y.nominal, + maximo = y.maximo, + minimo = y.minimo, + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var sensorObj = new + { + conectado = sensor.Inicializado, + id, + label = sensor.ID, + tipo, + aferir = sensor.Aferir, + mandatorio = sensor.Mandatorio, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + senSensoresAtivos.Add($"{tipo}.{id}"); + senSensoresAtualizados.Add(($"{basePath}", sensorObj)); + } + senSensoresAtualizados.Add(("sensores.ativos", senSensoresAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senSensoresAtualizados.ToArray()); + + var senServosAtualizados = new List<(string caminho, object valor)>(); + var senServosAtivos = new List(); + foreach (var servo in DadosSen.Servos) + { + int tipo = (int)servo.Componente; + int id = servo.ID_Num; + string basePath = $"servos.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); + + var dados = servo.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var servoObj = new + { + conectado = servo.Inicializado, + id, + label = servo.ID, + tipo, + controlar = servo.Controlar, + mandatorio = servo.Mandatorio, + angulo_sp = servo._AnguloControle, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + senServosAtivos.Add($"{tipo}.{id}"); + senServosAtualizados.Add(($"{basePath}", servoObj)); + } + senServosAtualizados.Add(("servos.ativos", senServosAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senServosAtualizados.ToArray()); + + var senRelesAtualizados = new List<(string caminho, object valor)>(); + var senRelesAtivos = new List(); + foreach (var rele in DadosSen.Reles) + { + int tipo = (int)rele.Componente; + int id = rele.ID_Num; + string basePath = $"reles.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); + + var dados = rele.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var releObj = new + { + conectado = rele.Inicializado, + id, + label = rele.ID, + tipo, + controlar = rele.Controlar, + mandatorio = rele.Mandatorio, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + senRelesAtivos.Add($"{tipo}.{id}"); + senRelesAtualizados.Add(($"{basePath}", releObj)); + } + senRelesAtualizados.Add(("reles.ativos", senRelesAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senRelesAtualizados.ToArray()); + + var senLedsAtualizados = new List<(string caminho, object valor)>(); + var senLedsAtivos = new List(); + foreach (var led in DadosSen.Sinaleiros) + { + int tipo = (int)led.Componente; + int id = led.ID_Num; + string basePath = $"sinaleiros.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); + + var dados = led.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var ledObj = new + { + conectado = led.Inicializado, + id, + label = led.ID, + tipo, + controlar = led.Controlar, + mandatorio = led.Mandatorio, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + senLedsAtivos.Add($"{tipo}.{id}"); + senLedsAtualizados.Add(($"{basePath}", ledObj)); + } + senLedsAtualizados.Add(("sinaleiros.ativos", senLedsAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senLedsAtualizados.ToArray()); + + + var DadosAtu = Variaveis.OperacaoEmAndamento.DispAtu.Dados; + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), + ("timestamp", agora), + ("conectado", DadosAtu.Conectado), + ("freq_base", DadosAtu.FrequenciaBase) + ); + var atuSensoresAtualizados = new List<(string caminho, object valor)>(); + var atuSensoresAtivos = new List(); + foreach (var sensor in DadosAtu.Sensores) + { + int tipo = (int)sensor.Componente; + int id = sensor.ID_Num; + string basePath = $"sensores.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); + + var dados = sensor.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + nominal = y.nominal, + maximo = y.maximo, + minimo = y.minimo, + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var sensorObj = new + { + conectado = sensor.Inicializado, + id, + label = sensor.ID, + tipo, + aferir = sensor.Aferir, + mandatorio = sensor.Mandatorio, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + atuSensoresAtivos.Add($"{tipo}.{id}"); + atuSensoresAtualizados.Add(($"{basePath}", sensorObj)); + } + atuSensoresAtualizados.Add(("sensores.ativos", atuSensoresAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuSensoresAtualizados.ToArray()); + + var atuBicosAtualizados = new List<(string caminho, object valor)>(); + var atuBicosAtivos = new List(); + foreach (var bico in DadosAtu.BicosPulverizadores) + { + int tipo = (int)bico.Componente; + int id = bico.ID_Num; + string basePath = $"bicos.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); + + var dados = bico.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + nominal = y.nominal, + maximo = y.maximo, + minimo = y.minimo, + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var bicoObj = new + { + conectado = bico.Inicializado, + id, + label = bico.ID, + tipo, + comandar = bico.Comandar, + mandatorio = bico.Mandatorio, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + atuBicosAtualizados.Add(($"{basePath}", bicoObj)); + atuBicosAtivos.Add($"{tipo}.{id}"); + } + atuBicosAtualizados.Add(("bicos.ativos", atuBicosAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuBicosAtualizados.ToArray()); + + var atuBombasAtualizados = new List<(string caminho, object valor)>(); + var atuBombasAtivos = new List(); + foreach (var bomba in DadosAtu.BombasPressurizadoras) + { + int tipo = (int)bomba.Componente; + int id = bomba.ID_Num; + string basePath = $"bombas.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); + + var dados = bomba.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + nominal = y.nominal, + maximo = y.maximo, + minimo = y.minimo, + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var sensorObj = new + { + conectado = bomba.Inicializado, + id, + label = bomba.ID, + tipo, + comandar = bomba.Comandar, + mandatorio = bomba.Mandatorio, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + atuBombasAtivos.Add($"{tipo}.{id}"); + atuBombasAtualizados.Add(($"{basePath}", sensorObj)); + } + atuBombasAtualizados.Add(("bombas.ativos", atuBombasAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuBombasAtualizados.ToArray()); + + var atuServosAtualizados = new List<(string caminho, object valor)>(); + var atuServosAtivos = new List(); + foreach (var servo in DadosAtu.Servos) + { + int tipo = (int)servo.Componente; + int id = servo.ID_Num; + string basePath = $"servos.{tipo}.{id}"; + + var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); + + var dados = servo.ValoresLeituras + .Where(y => y.analise_health) + .Select(y => new + { + label = y.funcao.ToString(), + atual = y.atual.valor, + freq = y.atual.frequencia + }) + .ToList(); + + var servoObj = new + { + conectado = servo.Inicializado, + id, + label = servo.ID, + tipo, + controlar = servo.Controlar, + mandatorio = servo.Mandatorio, + angulo_sp = servo._AnguloControle, + dados, + timestamp = agora, + saude = saude ?? new { } + }; + + atuServosAtivos.Add($"{tipo}.{id}"); + atuServosAtualizados.Add(($"{basePath}", servoObj)); + } + atuServosAtualizados.Add(("servos.ativos", atuServosAtivos)); + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuServosAtualizados.ToArray()); + + + var DadosLra = Variaveis.LoraService; + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Lra), + ("timestamp", agora), + ("conectado", DadosLra.Conectado), + ("configurado", DadosLra.Configurado), + ("tempo_base_tx", DadosLra.TempoEnvioDadosBase / 1000.0), + ("tempo_base_rx", VariaveisEquipamento.TempoEntrePingsConexao / 1000.0), + ("last_tx", DadosLra.UltimoTxDados), + ("last_rx", DadosLra.UltimoRxDados) ); - int endereco_mov = mod.MovMotor._EnderecoCAN_Tx; - RedisService.AtualizarCampos( - RedisService.ModKey(Enums.T_Code.Mov), - ($"{endereco_mov}.timestamp", agora), - ($"{endereco_mov}.conectado", mod.MovMotor.Inicializado), - ($"{endereco_mov}.freq_base", Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorMOV.IntervalHz), - ($"{endereco_mov}.endereco", mod.MovMotor._EnderecoCAN_Tx), - ($"{endereco_mov}.mod_id", mod.Modulo_ID), - ($"{endereco_mov}.cod_erro", mod.MovMotor.CodigoAlarme), - ($"{endereco_mov}.rpm", mod.MovMotor.RPM_Roda), - ($"{endereco_mov}.rpm_sp", mod.MovMotor.RPM_SP_Leitura), - ($"{endereco_mov}.tensao", mod.MovMotor.Tensao), - ($"{endereco_mov}.corrente_motor", mod.MovMotor.Corrente_Motor), - ($"{endereco_mov}.corrente_barramento", mod.MovMotor.Corrente_Barramento), - ($"{endereco_mov}.temperatura_driver", mod.MovMotor.TemperaturaDriver), - ($"{endereco_mov}.temperatura_motor", mod.MovMotor.Temperatura), - ($"{endereco_mov}.freado", mod.MovMotor.Leitura?.Freado ?? false), - ($"{endereco_mov}.freq.erro", mod.MovMotor.Leitura?.CodigoAlarme?.frequencia ?? 0), - ($"{endereco_mov}.freq.rpm", mod.MovMotor.Leitura?.ERPM?.frequencia ?? 0), - ($"{endereco_mov}.freq.tensao", mod.MovMotor.Leitura?.Tensao?.frequencia ?? 0), - ($"{endereco_mov}.freq.corrente_motor", mod.MovMotor.Leitura?.CorrenteMotor?.frequencia ?? 0), - ($"{endereco_mov}.freq.corrente_barramento", mod.MovMotor.Leitura?.CorrenteBarramento?.frequencia ?? 0), - ($"{endereco_mov}.freq.temperatura", mod.MovMotor.Leitura?.Temperatura?.frequencia ?? 0) + var DadosMod = _Sensoriamento.DadosPerformance; + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Npc), + ("timestamp", agora), + ("freq_base", 1.0 / Variaveis.OperacaoEmAndamento.tmrLeiturasInterval), + ("cpu_load", DadosMod.CPU_load ?? 0), + ("gpu_load", DadosMod.GPU_load ?? 0), + ("hdd_load", DadosMod.HDD_load ?? 0), + ("ram_usage", DadosMod.RAM_usage ?? 0), + ("cpu_temp", DadosMod.CPU_temp ?? 0), + ("gpu_temp", DadosMod.GPU_temp ?? 0) ); - dirAtivos.Add(endereco_dir); - movAtivos.Add(endereco_mov); - } - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Dir), ($"ativos", dirAtivos.ToList())); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Mov), ($"ativos", movAtivos.ToList())); + var DadosCan = CanManager.CanService; + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Can), + ("timestamp", agora), + ("conectado", DadosCan.Iniciado), + ("iniciado", DadosCan.IsConnected), + ("executando", SerialService.DispositivosMapeados.Any(x => SerialService.DispositivosCan.Contains(x.Dispositivo))), + ("erros_criticos", DadosCan.ErrosCriticos), + ("freq_tx", DadosCan.FreqTx), + ("freq_rx", DadosCan.FreqRx), + ("last_tx", DadosCan.UltimoTx), + ("last_rx", DadosCan.UltimoRx), + ("last_ec", DadosCan.UltimoErroCritico) + ); - var DadosSen = Variaveis.OperacaoEmAndamento.DispSen.Dados; - //Console.WriteLine("[HEALTH SEN] " + DadosSen.Conectado); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), - ("timestamp", agora), - ("conectado", DadosSen.Conectado), - ("freq_base", DadosSen.FrequenciaBase), - ("bms_ligado", _Sensoriamento.Bateria?.BmsLigado ?? false) - ); - var senSensoresAtualizados = new List<(string caminho, object valor)>(); - var senSensoresAtivos = new List(); - foreach (var sensor in DadosSen.Sensores) - { - int tipo = (int)sensor.Componente; - int id = sensor.ID_Num; - string basePath = $"sensores.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); - - var dados = sensor.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - nominal = y.nominal, - maximo = y.maximo, - minimo = y.minimo, - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var sensorObj = new + double MinOrDefault(List items) { - conectado = sensor.Inicializado, - id, - label = sensor.ID, - tipo, - aferir = sensor.Aferir, - mandatorio = sensor.Mandatorio, - dados, - timestamp = agora, - saude = saude ?? new { } - }; + if (items.Count == 0) return 0; + return items.Min(); + } - senSensoresAtivos.Add($"{tipo}.{id}"); - senSensoresAtualizados.Add(($"{basePath}", sensorObj)); - } - senSensoresAtualizados.Add(("sensores.ativos", senSensoresAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senSensoresAtualizados.ToArray()); - - var senServosAtualizados = new List<(string caminho, object valor)>(); - var senServosAtivos = new List(); - foreach (var servo in DadosSen.Servos) - { - int tipo = (int)servo.Componente; - int id = servo.ID_Num; - string basePath = $"servos.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); - - var dados = servo.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var servoObj = new + double MaxOrDefault(List items) { - conectado = servo.Inicializado, - id, - label = servo.ID, - tipo, - controlar = servo.Controlar, - mandatorio = servo.Mandatorio, - angulo_sp = servo._AnguloControle, - dados, - timestamp = agora, - saude = saude ?? new { } - }; + if (items.Count == 0) return 0; + return items.Max(); + } - senServosAtivos.Add($"{tipo}.{id}"); - senServosAtualizados.Add(($"{basePath}", servoObj)); - } - senServosAtualizados.Add(("servos.ativos", senServosAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senServosAtualizados.ToArray()); + var DadosBateria = DalyBMSService.DadosLeitura; + RedisService.AtualizarCampos( + RedisService.ModKey(T_Code.Bat), // ajusta o T_Code se o seu enum tiver outro nome + ("timestamp", agora), + ("iniciado", DadosBateria.Iniciado), + ("momento_bms", DadosBateria.Momento), + // Dados principais do pack + ("tensao_total_v", DadosBateria.Pack.TensaoTotal_V), + ("tensao_medida_v", DadosBateria.Pack.TensaoMedida_V), + ("corrente_a", DadosBateria.Pack.Corrente_A), + ("soc_pct", DadosBateria.Pack.SOC_percent), + ("num_celulas_serie", DadosBateria.Pack.NumeroCelulasSerie), + ("num_sensores_temp", DadosBateria.Pack.NumeroSensoresTemperatura), + ("estado_geral", DadosBateria.Pack.EstadoGeral), + ("charge_mos_on", DadosBateria.Pack.ChargeMosOn), + ("discharge_mos_on", DadosBateria.Pack.DischargeMosOn), + ("cap_restante_ah", DadosBateria.Pack.CapacidadeRestante_Ah), + ("ciclos_aproximados", DadosBateria.Pack.CiclosAproximados), + // Tensão de células + ("tensao_min_celula_v", DadosBateria.TensaoMin > 0 ? DadosBateria.TensaoMin : MinOrDefault(DadosBateria.Celulas.Select(c => c.Tensao_V).ToList())), + ("tensao_max_celula_v", DadosBateria.TensaoMax > 0 ? DadosBateria.TensaoMax : MaxOrDefault(DadosBateria.Celulas.Select(c => c.Tensao_V).ToList())), + ("idx_celula_min", DadosBateria.Celulas.FirstOrDefault(c => c.EhMinima)?.Index ?? 0), + ("idx_celula_max", DadosBateria.Celulas.FirstOrDefault(c => c.EhMaxima)?.Index ?? 0), + // Temperaturas + ("temp_min_c", DadosBateria.TemperaturaMin > -200 ? DadosBateria.TemperaturaMin : DadosBateria.Temperaturas.Min(t => t.Temperatura_C)), + ("temp_max_c", DadosBateria.TemperaturaMax > -200 ? DadosBateria.TemperaturaMax : DadosBateria.Temperaturas.Max(t => t.Temperatura_C)), + // Balanceamento + ("celulas_em_balanceamento", DadosBateria.Balanceamento.QtdeCelulasEmBalanceamento), + // Alarmes + ("tem_alarme", DadosBateria.Alarmes?.TemAlarme ?? false), + ("fault_code", DadosBateria.Alarmes?.FaultCode ?? 0x00), + ("qtd_alarmes", DadosBateria.Alarmes?.Mensagens?.Count ?? 0), + ("alarmes_texto", DadosBateria.Alarmes.Mensagens != null && DadosBateria.Alarmes.Mensagens.Count > 0 ? string.Join(" | ", DadosBateria.Alarmes.Mensagens) : string.Empty), + // Opcional: bytes brutos de alarme se quiser debugar depois + ("alarm_bytes_raw", DadosBateria.Alarmes.AlarmBytesRaw != null ? BitConverter.ToString(DadosBateria.Alarmes.AlarmBytesRaw) : string.Empty) + ); - var senRelesAtualizados = new List<(string caminho, object valor)>(); - var senRelesAtivos = new List(); - foreach (var rele in DadosSen.Reles) - { - int tipo = (int)rele.Componente; - int id = rele.ID_Num; - string basePath = $"reles.{tipo}.{id}"; + var DadosTrj = _Sensoriamento?.Trajetoria; + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Trj), + ("timestamp", agora), + ("iniciado", DadosTrj?.AutonomiaCooredor?.Iniciado ?? false), + ("liberado", DadosTrj?.AutonomiaCooredor?.Liberado ?? false), + ("bateria_ok", DadosTrj?.AutonomiaCooredor?.BateriaSuficiente ?? false), + ("herbicida_ok", DadosTrj?.AutonomiaCooredor?.HerbicidaSuficiente ?? false), + ("motivos", DadosTrj?.AutonomiaCooredor?.Motivos ?? new List()) + ); - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); - - var dados = rele.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var releObj = new + double distanciaBase = -1; + if (VariaveisOperacao.PosicaoBase?.Inicializado == true && _Sensoriamento?.Gps?.Inicializado == true) { - conectado = rele.Inicializado, - id, - label = rele.ID, - tipo, - controlar = rele.Controlar, - mandatorio = rele.Mandatorio, - dados, - timestamp = agora, - saude = saude ?? new { } - }; - - senRelesAtivos.Add($"{tipo}.{id}"); - senRelesAtualizados.Add(($"{basePath}", releObj)); + distanciaBase = GPSUtils.DistanciaEntrePontos(VariaveisOperacao.PosicaoBase, _Sensoriamento.Gps); + } + RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Ipb), + ("timestamp", agora), + ("iniciado", Variaveis.MqttServiceBase?.StatusConexao() ?? false), + ("distancia_base", distanciaBase) + ); } - senRelesAtualizados.Add(("reles.ativos", senRelesAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senRelesAtualizados.ToArray()); - - var senLedsAtualizados = new List<(string caminho, object valor)>(); - var senLedsAtivos = new List(); - foreach (var led in DadosSen.Sinaleiros) + catch (Exception ex) { - int tipo = (int)led.Componente; - int id = led.ID_Num; - string basePath = $"sinaleiros.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Sen), $"{basePath}.saude"); - - var dados = led.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var ledObj = new - { - conectado = led.Inicializado, - id, - label = led.ID, - tipo, - controlar = led.Controlar, - mandatorio = led.Mandatorio, - dados, - timestamp = agora, - saude = saude ?? new { } - }; - - senLedsAtivos.Add($"{tipo}.{id}"); - senLedsAtualizados.Add(($"{basePath}", ledObj)); + Variaveis.MostrarLog($"[HealthWorkerService] Erro em AtualizarDadosModulos: {ex.Message}"); } - senLedsAtualizados.Add(("sinaleiros.ativos", senLedsAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), senLedsAtualizados.ToArray()); - - - var DadosAtu = Variaveis.OperacaoEmAndamento.DispAtu.Dados; - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), - ("timestamp", agora), - ("conectado", DadosAtu.Conectado), - ("freq_base", DadosAtu.FrequenciaBase) - ); - var atuSensoresAtualizados = new List<(string caminho, object valor)>(); - var atuSensoresAtivos = new List(); - foreach (var sensor in DadosAtu.Sensores) - { - int tipo = (int)sensor.Componente; - int id = sensor.ID_Num; - string basePath = $"sensores.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); - - var dados = sensor.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - nominal = y.nominal, - maximo = y.maximo, - minimo = y.minimo, - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var sensorObj = new - { - conectado = sensor.Inicializado, - id, - label = sensor.ID, - tipo, - aferir = sensor.Aferir, - mandatorio = sensor.Mandatorio, - dados, - timestamp = agora, - saude = saude ?? new { } - }; - - atuSensoresAtivos.Add($"{tipo}.{id}"); - atuSensoresAtualizados.Add(($"{basePath}", sensorObj)); - } - atuSensoresAtualizados.Add(("sensores.ativos", atuSensoresAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuSensoresAtualizados.ToArray()); - - var atuBicosAtualizados = new List<(string caminho, object valor)>(); - var atuBicosAtivos = new List(); - foreach (var bico in DadosAtu.BicosPulverizadores) - { - int tipo = (int)bico.Componente; - int id = bico.ID_Num; - string basePath = $"bicos.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); - - var dados = bico.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - nominal = y.nominal, - maximo = y.maximo, - minimo = y.minimo, - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var bicoObj = new - { - conectado = bico.Inicializado, - id, - label = bico.ID, - tipo, - comandar = bico.Comandar, - mandatorio = bico.Mandatorio, - dados, - timestamp = agora, - saude = saude ?? new { } - }; - - atuBicosAtualizados.Add(($"{basePath}", bicoObj)); - atuBicosAtivos.Add($"{tipo}.{id}"); - } - atuBicosAtualizados.Add(("bicos.ativos", atuBicosAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuBicosAtualizados.ToArray()); - - var atuBombasAtualizados = new List<(string caminho, object valor)>(); - var atuBombasAtivos = new List(); - foreach (var bomba in DadosAtu.BombasPressurizadoras) - { - int tipo = (int)bomba.Componente; - int id = bomba.ID_Num; - string basePath = $"bombas.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); - - var dados = bomba.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - nominal = y.nominal, - maximo = y.maximo, - minimo = y.minimo, - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var sensorObj = new - { - conectado = bomba.Inicializado, - id, - label = bomba.ID, - tipo, - comandar = bomba.Comandar, - mandatorio = bomba.Mandatorio, - dados, - timestamp = agora, - saude = saude ?? new { } - }; - - atuBombasAtivos.Add($"{tipo}.{id}"); - atuBombasAtualizados.Add(($"{basePath}", sensorObj)); - } - atuBombasAtualizados.Add(("bombas.ativos", atuBombasAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuBombasAtualizados.ToArray()); - - var atuServosAtualizados = new List<(string caminho, object valor)>(); - var atuServosAtivos = new List(); - foreach (var servo in DadosAtu.Servos) - { - int tipo = (int)servo.Componente; - int id = servo.ID_Num; - string basePath = $"servos.{tipo}.{id}"; - - var saude = RedisService.GetField(RedisService.ModKey(Enums.T_Code.Atu), $"{basePath}.saude"); - - var dados = servo.ValoresLeituras - .Where(y => y.analise_health) - .Select(y => new - { - label = y.funcao.ToString(), - atual = y.atual.valor, - freq = y.atual.frequencia - }) - .ToList(); - - var servoObj = new - { - conectado = servo.Inicializado, - id, - label = servo.ID, - tipo, - controlar = servo.Controlar, - mandatorio = servo.Mandatorio, - angulo_sp = servo._AnguloControle, - dados, - timestamp = agora, - saude = saude ?? new { } - }; - - atuServosAtivos.Add($"{tipo}.{id}"); - atuServosAtualizados.Add(($"{basePath}", servoObj)); - } - atuServosAtualizados.Add(("servos.ativos", atuServosAtivos)); - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Atu), atuServosAtualizados.ToArray()); - - - var DadosLra = Variaveis.LoraService; - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Lra), - ("timestamp", agora), - ("conectado", DadosLra.Conectado), - ("configurado", DadosLra.Configurado), - ("tempo_base_tx", DadosLra.TempoEnvioDadosBase / 1000.0), - ("tempo_base_rx", VariaveisEquipamento.TempoEntrePingsConexao / 1000.0), - ("last_tx", DadosLra.UltimoTxDados), - ("last_rx", DadosLra.UltimoRxDados) - ); - - var DadosMod = _Sensoriamento.DadosPerformance; - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Npc), - ("timestamp", agora), - ("freq_base", 1.0 / Variaveis.OperacaoEmAndamento.tmrLeiturasInterval), - ("cpu_load", DadosMod.CPU_load ?? 0), - ("gpu_load", DadosMod.GPU_load ?? 0), - ("hdd_load", DadosMod.HDD_load ?? 0), - ("ram_usage", DadosMod.RAM_usage ?? 0), - ("cpu_temp", DadosMod.CPU_temp ?? 0), - ("gpu_temp", DadosMod.GPU_temp ?? 0) - ); - - var DadosCan = CanManager.CanService; - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Can), - ("timestamp", agora), - ("conectado", DadosCan.Iniciado), - ("iniciado", DadosCan.IsConnected), - ("executando", SerialService.DispositivosMapeados.Any(x => SerialService.DispositivosCan.Contains(x.Dispositivo))), - ("erros_criticos", DadosCan.ErrosCriticos), - ("freq_tx", DadosCan.FreqTx), - ("freq_rx", DadosCan.FreqRx), - ("last_tx", DadosCan.UltimoTx), - ("last_rx", DadosCan.UltimoRx), - ("last_ec", DadosCan.UltimoErroCritico) - ); - - double MinOrDefault(List items) - { - if (items.Count == 0) return 0; - return items.Min(); - } - - double MaxOrDefault(List items) - { - if (items.Count == 0) return 0; - return items.Max(); - } - - var DadosBateria = DalyBMSService.DadosLeitura; - RedisService.AtualizarCampos( - RedisService.ModKey(T_Code.Bat), // ajusta o T_Code se o seu enum tiver outro nome - ("timestamp", agora), - ("iniciado", DadosBateria.Iniciado), - ("momento_bms", DadosBateria.Momento), - // Dados principais do pack - ("tensao_total_v", DadosBateria.Pack.TensaoTotal_V), - ("tensao_medida_v", DadosBateria.Pack.TensaoMedida_V), - ("corrente_a", DadosBateria.Pack.Corrente_A), - ("soc_pct", DadosBateria.Pack.SOC_percent), - ("num_celulas_serie", DadosBateria.Pack.NumeroCelulasSerie), - ("num_sensores_temp", DadosBateria.Pack.NumeroSensoresTemperatura), - ("estado_geral", DadosBateria.Pack.EstadoGeral), - ("charge_mos_on", DadosBateria.Pack.ChargeMosOn), - ("discharge_mos_on", DadosBateria.Pack.DischargeMosOn), - ("cap_restante_ah", DadosBateria.Pack.CapacidadeRestante_Ah), - ("ciclos_aproximados", DadosBateria.Pack.CiclosAproximados), - // Tensão de células - ("tensao_min_celula_v", DadosBateria.TensaoMin > 0 ? DadosBateria.TensaoMin : MinOrDefault(DadosBateria.Celulas.Select(c => c.Tensao_V).ToList())), - ("tensao_max_celula_v", DadosBateria.TensaoMax > 0 ? DadosBateria.TensaoMax : MaxOrDefault(DadosBateria.Celulas.Select(c => c.Tensao_V).ToList())), - ("idx_celula_min", DadosBateria.Celulas.FirstOrDefault(c => c.EhMinima)?.Index ?? 0), - ("idx_celula_max", DadosBateria.Celulas.FirstOrDefault(c => c.EhMaxima)?.Index ?? 0), - // Temperaturas - ("temp_min_c", DadosBateria.TemperaturaMin > -200 ? DadosBateria.TemperaturaMin : DadosBateria.Temperaturas.Min(t => t.Temperatura_C)), - ("temp_max_c", DadosBateria.TemperaturaMax > -200 ? DadosBateria.TemperaturaMax : DadosBateria.Temperaturas.Max(t => t.Temperatura_C)), - // Balanceamento - ("celulas_em_balanceamento", DadosBateria.Balanceamento.QtdeCelulasEmBalanceamento), - // Alarmes - ("tem_alarme", DadosBateria.Alarmes?.TemAlarme ?? false), - ("fault_code", DadosBateria.Alarmes?.FaultCode ?? 0x00), - ("qtd_alarmes", DadosBateria.Alarmes?.Mensagens?.Count ?? 0), - ("alarmes_texto", DadosBateria.Alarmes.Mensagens != null && DadosBateria.Alarmes.Mensagens.Count > 0 ? string.Join(" | ", DadosBateria.Alarmes.Mensagens) : string.Empty), - // Opcional: bytes brutos de alarme se quiser debugar depois - ("alarm_bytes_raw", DadosBateria.Alarmes.AlarmBytesRaw != null ? BitConverter.ToString(DadosBateria.Alarmes.AlarmBytesRaw) : string.Empty) - ); - - var DadosTrj = _Sensoriamento?.Trajetoria; - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Trj), - ("timestamp", agora), - ("iniciado", DadosTrj?.AutonomiaCooredor?.Iniciado ?? false), - ("liberado", DadosTrj?.AutonomiaCooredor?.Liberado ?? false), - ("bateria_ok", DadosTrj?.AutonomiaCooredor?.BateriaSuficiente ?? false), - ("herbicida_ok", DadosTrj?.AutonomiaCooredor?.HerbicidaSuficiente ?? false), - ("motivos", DadosTrj?.AutonomiaCooredor?.Motivos ?? new List()) - ); - - double distanciaBase = -1; - if (VariaveisOperacao.PosicaoBase?.Inicializado == true && _Sensoriamento?.Gps?.Inicializado == true) - { - distanciaBase = GPSUtils.DistanciaEntrePontos(VariaveisOperacao.PosicaoBase, _Sensoriamento.Gps); - } - RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Ipb), - ("timestamp", agora), - ("iniciado", Variaveis.MqttServiceBase?.StatusConexao() ?? false), - ("distancia_base", distanciaBase) - ); - } diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/imu.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/imu.py index b005ca738..1bf8fd463 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/imu.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/imu.py @@ -1,6 +1,7 @@ import numpy as np import time import threading +from collections import deque from ahrs.filters import Madgwick from scipy.spatial.transform import Rotation as R from shared.contexto_global_redis import ContextoGlobalRedis @@ -10,27 +11,54 @@ from health_worker.modulos.base import ModuloDiagnosticoBase class IMUCamera(ModuloDiagnosticoBase): def __init__(self, queue=None, freq=100, angulo_inicial=26.3): self.freq = freq - - if queue is None: return + + if queue is None: + return self.imu_queue = queue - self.filtro_imu = Madgwick(beta=0.8, frequency=freq) - - self.roll_inicial = 90 - angulo_inicial # graus + + # beta bem mais conservador + self.filtro_imu = Madgwick(beta=0.05, frequency=freq) + + self.roll_inicial = 90 - angulo_inicial self.pitch_inicial = 0.0 self.yaw_inicial = 0.0 r = R.from_euler('xyz', [self.roll_inicial, self.pitch_inicial, self.yaw_inicial], degrees=True) - q = r.as_quat() # formato: [x, y, z, w] - self.q = np.array([q[3], q[0], q[1], q[2]]) # reordenando para [w, x, y, z] como espera o filtro + q = r.as_quat() + self.q = np.array([q[3], q[0], q[1], q[2]], dtype=np.float64) self.imu_em_falha = False self.tempo_erro = 0 - self.v_world = np.zeros(3, dtype=np.float32) - self.last_imu_ts = None + self.v_world = np.zeros(3, dtype=np.float64) self.g = 9.80665 + self.last_loop_ts = None + + # filtros de segurança + self.roll_seg = 0.0 + self.pitch_seg = 0.0 + self.alpha_seg = 0.12 + + # mediana curta para matar espinhos + self.roll_hist = deque(maxlen=5) + self.pitch_hist = deque(maxlen=5) + + self.acc_norm_hist = deque(maxlen=100) # ~1s + self.acc_z_hist = deque(maxlen=100) # ~1s + self.gyro_norm_hist = deque(maxlen=60) # ~0.6s + self.roll_seg_hist = deque(maxlen=60) # ~0.6s + self.pitch_seg_hist = deque(maxlen=60) # ~0.6s + self.impacto_hist = deque(maxlen=20) # ~0.2s para pico recente + self.em_movimento = False + self.movimento_idx = 0.0 + self.rugosidade_idx = 0.0 + self.impacto_idx = 0.0 + self.estabilidade_idx = 100.0 + self.last_redis_ts = 0.0 + self.redis_period = 0.10 # 100 ms + self.ativo = True self.imu_thread = threading.Thread(target=self.imu_task_loop, daemon=True) self.imu_thread.start() @@ -42,48 +70,185 @@ class IMUCamera(ModuloDiagnosticoBase): self.ativo = False self.mostrar_log("Task parada") + def _mediana_curta(self, hist, valor): + hist.append(valor) + return float(np.median(hist)) + + def clamp(self, valor, vmin, vmax): + return max(vmin, min(vmax, valor)) + + def norm_to_100(self, valor, faixa_min, faixa_max): + if faixa_max <= faixa_min: + return 0.0 + x = (valor - faixa_min) / (faixa_max - faixa_min) + return self.clamp(x * 100.0, 0.0, 100.0) + + def rms(self, valores): + if not valores: + return 0.0 + arr = np.asarray(valores, dtype=np.float64) + return float(np.sqrt(np.mean(arr ** 2))) + + def stddev(self, valores): + if not valores: + return 0.0 + arr = np.asarray(valores, dtype=np.float64) + return float(np.std(arr)) + + def _atualizar_indices(self, velocidade_mps, gx, gy, gz, a_lin): + acc_lin_norm = float(np.linalg.norm(a_lin)) + gyro_norm_deg = float(np.rad2deg(np.linalg.norm([gx, gy, gz]))) + + # Históricos + self.acc_norm_hist.append(acc_lin_norm) + self.acc_z_hist.append(float(a_lin[2])) + self.gyro_norm_hist.append(gyro_norm_deg) + self.roll_seg_hist.append(float(self.roll_seg)) + self.pitch_seg_hist.append(float(self.pitch_seg)) + self.impacto_hist.append(acc_lin_norm) + + # ============================ + # 1) Flag em movimento + # ============================ + vel_ok = velocidade_mps > 0.05 + gyro_ok = gyro_norm_deg > 3.0 + acc_ok = acc_lin_norm > 0.20 + self.em_movimento = bool(vel_ok or gyro_ok or acc_ok) + + # ============================ + # 2) Movimento idx + # ============================ + vel_score = self.norm_to_100(velocidade_mps, 0.0, 1.8) + gyro_score = self.norm_to_100(gyro_norm_deg, 0.0, 40.0) + acc_score = self.norm_to_100(acc_lin_norm, 0.0, 1.5) + + movimento_idx = ( + 0.50 * vel_score + + 0.25 * gyro_score + + 0.25 * acc_score + ) + self.movimento_idx = round(self.clamp(movimento_idx, 0.0, 100.0), 1) + + # ============================ + # 3) Rugosidade idx + # ============================ + rug_z = self.rms(self.acc_z_hist) + rug_total = self.stddev(self.acc_norm_hist) + + rug_z_score = self.norm_to_100(rug_z, 0.02, 0.80) + rug_total_score = self.norm_to_100(rug_total, 0.01, 0.60) + + rugosidade_idx = ( + 0.65 * rug_z_score + + 0.35 * rug_total_score + ) + self.rugosidade_idx = round(self.clamp(rugosidade_idx, 0.0, 100.0), 1) + + # ============================ + # 4) Impacto idx + # ============================ + pico_impacto = max(self.impacto_hist) if self.impacto_hist else 0.0 + impacto_bruto = self.norm_to_100(pico_impacto, 0.4, 4.0) + + # decaimento suave + self.impacto_idx = max(impacto_bruto, self.impacto_idx * 0.85) + self.impacto_idx = round(self.clamp(self.impacto_idx, 0.0, 100.0), 1) + + # ============================ + # 5) Estabilidade idx + # ============================ + roll_std = self.stddev(self.roll_seg_hist) + pitch_std = self.stddev(self.pitch_seg_hist) + osc_ang = float(np.sqrt(roll_std**2 + pitch_std**2)) + + osc_score = self.norm_to_100(osc_ang, 0.2, 8.0) + + instabilidade = ( + 0.60 * osc_score + + 0.40 * self.rugosidade_idx + ) + + estabilidade_idx = 100.0 - instabilidade + self.estabilidade_idx = round(self.clamp(estabilidade_idx, 0.0, 100.0), 1) + def imu_task_loop(self): - t0 = time.time() + t0 = time.perf_counter() + while self.ativo: - latencia = 0 + latencia = 0.0 try: - agora = time.time() + agora = time.perf_counter() t_tick = agora - t0 t0 = agora - f_tick = 1.0 / (t_tick + 1e-9) + f_tick = 1.0 / max(t_tick, 1e-6) + imuData = self.imu_queue.tryGet() + if imuData is not None and self.imu_em_falha: self.mostrar_log("Reconectado com sucesso, voltando ao modo normal") self.imu_em_falha = False - if imuData is not None: - # dt estimado por pacote - now = time.time() - if self.last_imu_ts is None: - dt = 1.0 / max(1e-3, f_tick) # fallback + if imuData is not None and len(imuData.packets) > 0: + now = time.perf_counter() + if self.last_loop_ts is None: + dt_total = 1.0 / self.freq else: - dt = max(0.0, now - self.last_imu_ts) - self.last_imu_ts = now - dt = min(dt, 0.05) # clamp anti-bursts (<=50 ms) - self.filtro_imu.Dt = float(dt) + dt_total = max(1e-4, now - self.last_loop_ts) + self.last_loop_ts = now + + dt_total = min(dt_total, 0.05) + num_packets = max(1, len(imuData.packets)) + dt_packet = max(1e-4, dt_total / num_packets) for packet in imuData.packets: accel = packet.acceleroMeter gyro = packet.gyroscope - ax = accel.x - ay = accel.y - az = accel.z + ax = float(accel.x) + ay = float(accel.y) + az = float(accel.z) - gx = np.deg2rad(gyro.x) - gy = np.deg2rad(gyro.y) - gz = np.deg2rad(gyro.z) + gx = float(np.deg2rad(gyro.x)) + gy = float(np.deg2rad(gyro.y)) + gz = float(np.deg2rad(gyro.z)) + + a_body = np.array([ax, ay, az], dtype=np.float64) + acc_norm = np.linalg.norm(a_body) + + self.filtro_imu.Dt = float(dt_packet) + + # Confia no acelerômetro só quando ele parece representar gravidade + usa_acc = abs(acc_norm - self.g) < 0.20 + + if usa_acc: + self.q = self.filtro_imu.updateIMU( + q=self.q, + gyr=np.array([gx, gy, gz], dtype=np.float64), + acc=a_body + ) + else: + # fallback: mantém última orientação integrando com gyro "na mão" + # caso sua lib não aceite acc=None + omega = np.array([gx, gy, gz], dtype=np.float64) + omega_norm = np.linalg.norm(omega) + if omega_norm > 1e-9: + theta = omega_norm * dt_packet + axis = omega / omega_norm + dq_xyz = axis * np.sin(theta / 2.0) + dq_w = np.cos(theta / 2.0) + dq = np.array([dq_w, dq_xyz[0], dq_xyz[1], dq_xyz[2]], dtype=np.float64) + + w1, x1, y1, z1 = self.q + w2, x2, y2, z2 = dq + self.q = np.array([ + w1*w2 - x1*x2 - y1*y2 - z1*z2, + w1*x2 + x1*w2 + y1*z2 - z1*y2, + w1*y2 - x1*z2 + y1*w2 + z1*x2, + w1*z2 + x1*y2 - y1*x2 + z1*w2 + ], dtype=np.float64) + + self.q /= max(np.linalg.norm(self.q), 1e-9) - self.q = self.filtro_imu.updateIMU( - q=self.q, - gyr=np.array([gx, gy, gz]), - acc=np.array([ax, ay, az]) - ) r = R.from_quat([self.q[1], self.q[2], self.q[3], self.q[0]]) roll, pitch, yaw = r.as_euler('xyz', degrees=True) @@ -91,48 +256,67 @@ class IMUCamera(ModuloDiagnosticoBase): pitch -= self.pitch_inicial yaw -= self.yaw_inicial + # mata picos curtíssimos + roll = self._mediana_curta(self.roll_hist, roll) + pitch = self._mediana_curta(self.pitch_hist, pitch) + + # filtro leve para segurança + self.roll_seg = (1.0 - self.alpha_seg) * self.roll_seg + self.alpha_seg * roll + self.pitch_seg = (1.0 - self.alpha_seg) * self.pitch_seg + self.alpha_seg * pitch + + # velocidade linear Rwb = r.as_matrix() + a_world = Rwb @ a_body + a_lin = a_world - np.array([0.0, 0.0, self.g], dtype=np.float64) - # 1) aceleração no mundo - a_body = np.array([ax, ay, az], dtype=np.float64) - a_world = Rwb @ a_body - - # 2) remove gravidade - a_lin = a_world - np.array([0.0, 0.0, self.g], dtype=np.float64) - - # 3) filtra ruído muito baixo (deadzone) - dead = 0.05 # m/s² + dead = 0.08 a_lin[np.abs(a_lin) < dead] = 0.0 - # 4) ZUPT (Zero-velocity update) se parado: - # - giros muito baixos e módulo da aceleração ~ g - if (np.linalg.norm([gx, gy, gz]) < np.deg2rad(2.0) - and abs(np.linalg.norm(a_body) - self.g) < 0.12): - # parado: puxe para zero agressivo - self.v_world *= 0.2 - else: - # 5) integra velocidade - self.v_world += (a_lin * dt) + parado = ( + np.linalg.norm([gx, gy, gz]) < np.deg2rad(1.5) and + abs(np.linalg.norm(a_body) - self.g) < 0.10 + ) + + if parado: + self.v_world *= 0.1 + else: + self.v_world += (a_lin * dt_packet) - # 6) projeta no plano do chão (z=0) e computa velocidade escalar v_planar = self.v_world.copy() v_planar[2] = 0.0 velocidade_mps = float(np.linalg.norm(v_planar)) - t1 = time.time() - latencia = t1 - t0 - f_exec = 1.0 / (latencia + 1e-9) + self._atualizar_indices( + velocidade_mps=velocidade_mps, + gx=gx, gy=gy, gz=gz, + a_lin=a_lin + ) + + t1 = time.perf_counter() + latencia = t1 - agora + + t_pub = time.perf_counter() + if (t_pub - self.last_redis_ts) >= self.redis_period: + self.last_redis_ts = t_pub + ContextoGlobalRedis.atualizar_ctx_dict( - ContextoGlobalRedis.ModKey(T_Code.Imu), - roll=round(-roll, 2), # frontal - pitch=round(pitch, 2), # lateral + ContextoGlobalRedis.ModKey(T_Code.Imu), + roll=round(-roll, 2), + pitch=round(pitch, 2), yaw=round(yaw, 2), + roll_seg=round(-self.roll_seg, 2), + pitch_seg=round(self.pitch_seg, 2), vel_mps=round(velocidade_mps, 3), - timestamp=(t1 * 1000), + em_movimento=self.em_movimento, + movimento_idx=round(self.movimento_idx, 1), + rugosidade_idx=round(self.rugosidade_idx, 1), + impacto_idx=round(self.impacto_idx, 1), + estabilidade_idx=round(self.estabilidade_idx, 1), + timestamp=(time.time() * 1000), latencia=latencia, frequencia=f_tick ) - #self.mostrar_log(f"IMU | Roll: {roll:.2f} | Pitch: {pitch:.2f} | Yaw: {yaw:.2f} | Freq: {f_tick:.2f} Hz | Latência: {latencia:.3f} s") + except Exception as e: self.mostrar_log(f"Erro no loop: {e}") if not self.imu_em_falha: @@ -143,14 +327,12 @@ class IMUCamera(ModuloDiagnosticoBase): self.ativo = False finally: if self.imu_em_falha: - # Espera mais tempo pra evitar flood de erro time.sleep(5.0) else: - delay_corrigido = max(0, (1.0 / self.freq) - latencia) + delay_corrigido = max(0.0, (1.0 / self.freq) - latencia) time.sleep(delay_corrigido) def atualizar_saude(self): - #self.mostrar_log("Atualizando saude") try: modulo = ContextoGlobalRedis.get_modulo(T_Code.Imu) FREQ_BASE = self.freq @@ -178,7 +360,6 @@ class IMUCamera(ModuloDiagnosticoBase): if p > 0: motivos.append(f"Frequência abaixo do ideal: {freq_hz:.2f} Hz (penalidade {p}%)") - # Penaliza se latência está alta (> 0.5s) if latencia > 0.5: p = int(min(latencia * 100, 30)) saude -= p @@ -210,4 +391,4 @@ class IMUCamera(ModuloDiagnosticoBase): self.mostrar_log(f"Erro ao atualizar saude: {e}") def mostrar_log(self, mensagem): - print(f"{time.time()} - [IMU] {mensagem}") + print(f"{time.time()} - [IMU] {mensagem}") \ No newline at end of file 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 31d542328..ae85f235f 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 @@ -4,6 +4,7 @@ import time import threading import statistics import socket +import numpy as np import psutil import subprocess from concurrent.futures import ThreadPoolExecutor @@ -82,6 +83,8 @@ class ModuloIPBribge(ModuloDiagnosticoBase): self._ping_timeout_ms = 400 # recomendado: 300–500 self._ping_count = 2 # recomendado: 1 (janela já suaviza) self._ping_min_interval = 0.6 # não precisa pingar a cada 100ms + self._hb_last_rx_monotonic = 0.0 + self._last_ping_sample_applied_ts = 0.0 self.bw_max_mbps = 10.0 # capacidade aproximada do link (ajuste por teste) self.bw_safe_mbps = 05.0 # alvo saudável (até aqui não penaliza) @@ -366,6 +369,7 @@ class ModuloIPBribge(ModuloDiagnosticoBase): now = time.time() with self._mqtt_lock: self.last_heartbeat_ts = now + self._hb_last_rx_monotonic = time.perf_counter() self._heartbeat_ok = True self._mqtt_mostra_log(f"[client={cid}] heartbeat recebido | t={now:.3f}") @@ -449,45 +453,6 @@ class ModuloIPBribge(ModuloDiagnosticoBase): self.last_bw_counters = counters self.last_bw_ts = now - def _ping_once_old(self, pings=5, timeout=500): - """ - Retorna RTT em ms se ok, ou None se timeout. - Aqui exemplo pra Windows usando 'ping -n 1'. - Ajuste pro teu ambiente se precisar. - """ - try: - base_ip = self.get_base_ip() - if base_ip is None: - return None, None - - # timeout de 1000 ms - proc = subprocess.run( - ["ping", "-n", f"{pings}", "-w", f"{timeout}", base_ip], - capture_output=True, text=True - ) - if proc.returncode != 0: - print("timeout") - return None, 100 - - print(proc.stdout) - - perda = None - media_tempo = None - for line in proc.stdout.splitlines(): - line = line.lower() - if ("%" in line and perda is None): - perda = line.split('(')[1].split('%')[0] - perda = float(perda) if perda.isdigit() else None - elif (" = " in line and "ms" in line and media_tempo is None): - media_tempo = line.split(' = ')[3].split('ms')[0] - media_tempo = float(media_tempo) if media_tempo.isdigit() else None - if perda is not None and media_tempo is not None: - break - return media_tempo, perda - except Exception: - print("erro timeout") - return None, 100 - def _ping_once(self, base_ip: str, pings: int, timeout_ms: int): """ Retorna (ok: bool, avg_rtt_ms: float|None, loss_pct: float) @@ -771,24 +736,19 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # 3) usa snapshot pra alimentar suas janelas rtts/loses/timeouts ok, rtt, loss, ping_ts = self._get_ping_snapshot() #print(f"ok: {ok}, rtt: {rtt}, loss: {loss}, ping_ts: {ping_ts}") - self.timeouts_f.append(loss == 100) - self.timeouts_s.append(loss == 100) + if ping_ts > self._last_ping_sample_applied_ts: + self._last_ping_sample_applied_ts = ping_ts - if ok and rtt is not None: - self.rtts_f.append(rtt) - self.rtts_s.append(rtt) + self.timeouts_f.append(loss == 100) + self.timeouts_s.append(loss == 100) - if loss is not None: - self.loses_f.append(loss) - self.loses_s.append(loss) + if ok and rtt is not None: + self.rtts_f.append(rtt) + self.rtts_s.append(rtt) - #(rtt, loss) = self._ping_once_old(pings=3, timeout=200) - #print(f"rtt: {rtt}, loss: {loss}") - #self.timeouts.append(loss == 100) - #if rtt is not None: - # self.rtts.append(rtt) - #if loss is not None: - # self.loses.append(loss) + if loss is not None: + self.loses_f.append(loss) + self.loses_s.append(loss) self._update_nic_errors() self._update_nic_bandwidth() @@ -1118,21 +1078,21 @@ class ModuloIPBribge(ModuloDiagnosticoBase): 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) + loss_pct_s >= 7 or + tmout_pct_s >= 6 or + atraso_s >= 5.0 or + (loss_pct_f >= 12 and loss_pct_s >= 4) or + (tmout_pct_f >= 15 and tmout_pct_s >= 4) or + (jitter_s >= 45 and loss_pct_s >= 4) ) 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)) + loss_pct_s >= 15 or + tmout_pct_s >= 12 or + atraso_s >= 8.0 or + (loss_pct_f >= 20 and loss_pct_s >= 6) or + (tmout_pct_f >= 25 and tmout_pct_s >= 6) or + ((loss_pct_s >= 8) and (atraso_s >= 6.0)) ) colapso_agora = ( @@ -1216,11 +1176,12 @@ class ModuloIPBribge(ModuloDiagnosticoBase): # Condição 2: Cerca invisível de rede violada dist_violando_cerca = ( self.link_state == "DEGRADED" and + self._cycles_degraded >= 15 and + risk_score >= 70 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 + distancia_base > self._distancia_inicio_degradado + 5.0 ) if dist_violando_cerca: condicoes.append({ @@ -1338,3 +1299,5 @@ class ModuloIPBribge(ModuloDiagnosticoBase): ) except Exception as e: print(f"Erro ao atualizar saude do modulo {self.t_code.name}: {e}") + + diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/regras_taticas.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/regras_taticas.py index e034e7cfb..a6c81f48d 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/regras_taticas.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/regras_taticas.py @@ -37,7 +37,7 @@ def verifica_controle_liberado(ignorar_parada=False): _equipamento = ContextoGlobalRedis.get_equipamento() ang_roll_max = _equipamento.get("angulo_roll_max", 15.0) ang_pitch_max = _equipamento.get("angulo_pitch_max", 30.0) - inclinacao_perigosa = abs(_imu.get("pitch", 0.0)) > ang_pitch_max or abs(_imu.get("roll", 0.0)) > ang_roll_max + inclinacao_perigosa = abs(_imu.get("pitch_seg", 0.0)) > ang_pitch_max or abs(_imu.get("roll_seg", 0.0)) > ang_roll_max if not imu_operante: motivos.append(f"IMU mandatório não operante: {_imu_saude.name}") diff --git a/AgroBase/OperationControl/Models/AppShell.cs b/AgroBase/OperationControl/Models/AppShell.cs index 8c082e98c..bf9e38b57 100644 --- a/AgroBase/OperationControl/Models/AppShell.cs +++ b/AgroBase/OperationControl/Models/AppShell.cs @@ -129,6 +129,27 @@ namespace OperationControl.Models Angulo = 0, PercentualVelocidadeSPKmh = 0, TipoMovimento = TipoMovimentoDirecional.RodasDianteiras + }, + Gnss = new AgroBase.Models.Operacoes.OperacaoParametrosDadosGNSSModel() + { + Latitude = -22.17254631649402, + Longitude = -47.395203906184044, + OrientacaoReal = 187 + }, + ModulosSaude = new List() + { + new AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel() + { + modulo = T_Code.Gps, + saude = 0, + status = StatusModulo.Desconectado, + }, + new AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel() + { + modulo = T_Code.Can, + saude = 70, + status = StatusModulo.Alerta, + }, } }; r.Controle = new AgroBase.Models.Operacoes.OperacaoParametrosControleModel() diff --git a/AgroBase/OperationControl/Models/Variaveis.cs b/AgroBase/OperationControl/Models/Variaveis.cs index 07c11c8dc..63fd96d5b 100644 --- a/AgroBase/OperationControl/Models/Variaveis.cs +++ b/AgroBase/OperationControl/Models/Variaveis.cs @@ -366,7 +366,7 @@ namespace OperationControl.Models return Variaveis.UdpChannel.SendBurstAsync(0x01, msg.ToBytes(), count: 6, intervalMs: 25, requestAck: false); } - public static void EnviarComandoReferenciamento(string mod_id = null) + public static void EnviarComandoReferenciamento(string? mod_id = null) { EnviarDadosControle(SelectedRoverId, new OperacaoComandoBaseModel() { diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ControleManualViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ControleManualViewModel.cs index a86372d8a..6dd0a37fd 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ControleManualViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/ControleManualViewModel.cs @@ -54,6 +54,10 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento LimparTeclas(); EnviarNeutros(); } + else + { + VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco.IP, true); + } } } } diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/Diagnostico/DiagnosticoViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/Diagnostico/DiagnosticoViewModel.cs index 7aa07a855..eec839000 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/Diagnostico/DiagnosticoViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/Diagnostico/DiagnosticoViewModel.cs @@ -750,8 +750,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento.Diagnostico AddOrAppendSerie("Jitter", "ms", conexao?.detalhes?["jitter"]?.Value()); AddOrAppendSerie("Timeout", "%", conexao?.detalhes?["tmout_pct"]?.Value()); AddOrAppendSerie("Perda", "%", conexao?.detalhes?["loss_pct"]?.Value()); - AddOrAppendSerie("Banda", "Mbps", conexao?.detalhes?["bw_total_mbps"]?.Value()); - AddOrAppendSerie("Banda %", "%", conexao?.detalhes?["bw_util_pct"]?.Value()); + AddOrAppendSerie("Banda", "%", conexao?.detalhes?["bw_util_pct"]?.Value()); break; case T_Code.Gps: @@ -768,6 +767,14 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento.Diagnostico AddOrAppendSerie("Corrente", "A", bat?.CorrenteInstantanea); AddOrAppendSerie("Temperatura", "°C", bat?.Temperatura); break; + + case T_Code.Imu: + var imu = rover.Imu; + AddOrAppendSerie("Movimento", "", imu?.IndiceMovimento); + AddOrAppendSerie("Estabilidade", "", imu?.IndiceEstabilidade); + AddOrAppendSerie("Rugosidade", "", imu?.IndiceRugosidade); + AddOrAppendSerie("Impacto", "", imu?.IndiceImpacto); + break; } } diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/MonitoramentoViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/MonitoramentoViewModel.cs index bc46abad3..a81ee923c 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/MonitoramentoViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/Monitoramento/MonitoramentoViewModel.cs @@ -104,6 +104,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento public event Action? CameraErvasFrameTipoAlterado; public ICommand RetornoBaseCommand { get; } + public ICommand ReferenciamentoCommand { get; } public MonitoramentoViewModel() { @@ -123,6 +124,7 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento CameraErvasFrameTipoSelecionado = CameraErvasFrameTipos.FirstOrDefault(); RetornoBaseCommand = new RelayCommand(_ => ExecutarRetornoBase(), _ => PodeExecutarRetornoBase()); + ReferenciamentoCommand = new RelayCommand(_ => ExecutarReferenciamento(), _ => PodeExecutarReferenciamento()); } public void AtualizarDadosCameras(CameraWorkerItemModel? snr, CameraWorkerItemModel? cam, StatusCarroMapa? status_carro, double? pct_ervas) @@ -165,6 +167,19 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento return true; } + private void ExecutarReferenciamento() + { + if (System.Windows.MessageBox.Show("Deseja executar o referenciamento do equipamento?", "Referenciamento", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) + { + VariaveisControleOperacao.EnviarComandoReferenciamento(); + } + } + + private bool PodeExecutarReferenciamento() + { + return true; + } + public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs index 8428e1b54..00354a955 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoBottomViewModel.cs @@ -209,15 +209,20 @@ namespace OperationControl.ViewModels.Views.Operacao if (ind.status == StatusModulo.Operante) continue; - alertasEsperados.Add(new AlertaModel + bool mandatorio = ind.em_uso; + + if (mandatorio) { - Rover_ID = rover.RoverId, - Modulo = mod.modulo, - Mod_ID = ind.label, - Severidade = DeParaStatus(ind.status, ind.em_uso), - Mensagem = string.Join(", ", ind.motivos ?? new List()) - // NÃO define Timestamp aqui ainda - }); + alertasEsperados.Add(new AlertaModel + { + Rover_ID = rover.RoverId, + Modulo = mod.modulo, + Mod_ID = ind.label, + Severidade = DeParaStatus(ind.status, mandatorio), + Mensagem = string.Join(", ", ind.motivos ?? new List()) + // NÃO define Timestamp aqui ainda + }); + } } } else @@ -226,17 +231,20 @@ namespace OperationControl.ViewModels.Views.Operacao continue; bool mandatorio = rover.ModulosMandatorios?.Any(x => - x.Dispositivo == mod.modulo && x.Mandatorio) ?? false; + x.Dispositivo == mod.modulo && x.Utilizar) ?? false; - alertasEsperados.Add(new AlertaModel + if (mandatorio) { - Rover_ID = rover.RoverId, - Modulo = mod.modulo, - Mod_ID = null, - Severidade = DeParaStatus(mod.status, mandatorio), - Mensagem = string.Join(", ", mod.motivos ?? new List()) - // NÃO define Timestamp aqui ainda - }); + alertasEsperados.Add(new AlertaModel + { + Rover_ID = rover.RoverId, + Modulo = mod.modulo, + Mod_ID = null, + Severidade = DeParaStatus(mod.status, mandatorio), + Mensagem = string.Join(", ", mod.motivos ?? new List() { "Motivo desconhecido" }) + // NÃO define Timestamp aqui ainda + }); + } } } } diff --git a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs index 91c804ab0..f31ed8734 100644 --- a/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Views/Operacao/OperacaoLeftViewModel.cs @@ -8,6 +8,7 @@ using Brush = System.Windows.Media.Brush; using Brushes = System.Windows.Media.Brushes; using SolidColorBrush = System.Windows.Media.SolidColorBrush; using Color = System.Windows.Media.Color; +using AgroBase.Models.Operacoes; namespace OperationControl.ViewModels.Views.Operacao { @@ -191,6 +192,21 @@ namespace OperationControl.ViewModels.Views.Operacao } } + private string _distanciaBase = "--"; + public string DistanciaBase + { + get => DistanciaOuTracos(_distanciaBase); + set + { + if (_distanciaBase != value) + { + _distanciaBase = value; + OnPropertyChanged(nameof(DistanciaBase)); + } + } + } + + public Brush CorTemperatura => !RoverSelecionado ? Brushes.Gray : _temperaturaNumerica >= 55 ? Brushes.OrangeRed : @@ -217,9 +233,19 @@ namespace OperationControl.ViewModels.Views.Operacao !RoverSelecionado ? Brushes.Gray : CorPorTexto(_saudeGnss); + public Brush CorDistBase => + !RoverSelecionado ? Brushes.Gray : + _distanciaBaseNumerico <= 50 ? Brushes.LimeGreen : + _distanciaBaseNumerico <= 150 ? Brushes.Green : + _distanciaBaseNumerico <= 400 ? Brushes.YellowGreen : + _distanciaBaseNumerico <= 800 ? Brushes.Gold : + _distanciaBaseNumerico <= 1200 ? Brushes.OrangeRed : + Brushes.DarkRed; + private double _temperaturaNumerica; private double _bateriaNumerica; private double _reservatorioNumerico; + private double _distanciaBaseNumerico; public OperacaoLeftViewModel() { @@ -237,6 +263,7 @@ namespace OperationControl.ViewModels.Views.Operacao _temperaturaNumerica = 0; _bateriaNumerica = 0; _reservatorioNumerico = 0; + _distanciaBaseNumerico = 0; Velocidade = "--"; AnguloDirecional = "--"; @@ -246,31 +273,35 @@ namespace OperationControl.ViewModels.Views.Operacao Reservatorio = "--"; SaudeRede = "--"; SaudeGnss = "--"; + DistanciaBase = "--"; OnPropertyChanged(nameof(CorTemperatura)); OnPropertyChanged(nameof(CorBateria)); OnPropertyChanged(nameof(CorReservatorio)); OnPropertyChanged(nameof(CorRede)); OnPropertyChanged(nameof(CorGnss)); + OnPropertyChanged(nameof(CorDistBase)); } - public void AtualizarRover(string nomeRover, double velocidadeKmh, double anguloDirecionalGraus, TipoMovimentoDirecional tipoMovimento, double temperaturaC, double bateriaPct, double reservatorioPct, string saudeRede, string saudeGnss, bool op_iniciada) + public void AtualizarRover(string descricao, OperacaoParametrosDadosControleModel? controle, double temperaturaC, double bateriaPct, double reservatorioPct, string saudeRede, string saudeGnss, double distanciaBase, bool op_iniciada) { RoverSelecionado = true; - NomeRover = nomeRover; + NomeRover = descricao; _temperaturaNumerica = temperaturaC; _bateriaNumerica = bateriaPct; _reservatorioNumerico = reservatorioPct; + _distanciaBaseNumerico = distanciaBase; - Velocidade = velocidadeKmh.ToString("0.0"); - AnguloDirecional = anguloDirecionalGraus.ToString("0.0"); - TipoMovimento = tipoMovimento; + Velocidade = $"{controle?.PercentualVelocidadeSPKmh ?? 0:F2}"; + AnguloDirecional = $"{controle?.Angulo ?? 0:F1}"; + TipoMovimento = controle?.TipoMovimento ?? TipoMovimentoDirecional.RodasDianteiras; Temperatura = temperaturaC.ToString("0.0"); Bateria = bateriaPct.ToString("0"); Reservatorio = reservatorioPct.ToString("0"); SaudeRede = saudeRede; SaudeGnss = saudeGnss; + DistanciaBase = $"{distanciaBase:F2}"; OperacaoEmExecucao = op_iniciada; @@ -279,6 +310,7 @@ namespace OperationControl.ViewModels.Views.Operacao OnPropertyChanged(nameof(CorReservatorio)); OnPropertyChanged(nameof(CorRede)); OnPropertyChanged(nameof(CorGnss)); + OnPropertyChanged(nameof(CorDistBase)); OnPropertyChanged(nameof(OperacaoEmExecucao)); OnPropertyChanged(nameof(TextoBotaoOperacao)); OnPropertyChanged(nameof(CorBotaoOperacao)); @@ -331,6 +363,7 @@ namespace OperationControl.ViewModels.Views.Operacao private string VelocidadeValor(string valor) => RoverSelecionado ? $"{valor} km/h" : "--"; private string TemperaturaValor(string valor) => RoverSelecionado ? $"{valor} °C" : "--"; private string PercentualOuTracos(string valor) => RoverSelecionado ? $"{valor}%" : "--"; + private string DistanciaOuTracos(string valor) => RoverSelecionado ? $"{valor} m" : "--"; private Brush CorPorTexto(string valor) { diff --git a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs index 96c40845e..482ffbb19 100644 --- a/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs +++ b/AgroBase/OperationControl/ViewModels/Windows/DockWindowViewModel.cs @@ -372,7 +372,7 @@ namespace OperationControl.ViewModels var eventos = new List(); double tempoSemResposta = (DateTime.Now - rover.UltimoContato).TotalSeconds; - bool semComunicacao = tempoSemResposta >= VariaveisControleOperacao.TempoRoverVivo; + bool semComunicacao = !AppShell.Mock && tempoSemResposta >= VariaveisControleOperacao.TempoRoverVivo; if (semComunicacao) { @@ -394,7 +394,7 @@ namespace OperationControl.ViewModels eventos.Add(new MotivoTopBarModel { Prioridade = 1100, - Fonte = "Núcleo Central de Processamento", + Fonte = "NCP", Titulo = "NÚCLEO CENTRAL DE PROCESSAMENTO DESCONECTADO", Descricao = "O núcleo central de processamento foi desconectado, tentando reconectar...", StatusVisual = StatusModulo.Falha @@ -475,29 +475,68 @@ namespace OperationControl.ViewModels // 5) Módulos com condições operacionais críticas // ============================ var modulosCriticos = obj.ModulosSaude? - .Where(mod => mod?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false) + .Where(mod => + (mod?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false) || + ( + !new List() { StatusModulo.Operante, StatusModulo.Alerta }.Contains(mod?.status ?? StatusModulo.Desconectado) && + (rover?.ModulosMandatorios?.Any(x => + (x?.Dispositivo ?? T_Code.Vzo) == (mod?.modulo ?? T_Code.Vzo) && + (x?.Utilizar ?? false) && + (x?.Mandatorio ?? false) + ) ?? 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(); + bool temCondicaoSevera = modulo?.condicoes_operacionais?.Any(cond => cond?.severidade > 90) ?? false; + bool moduloMandatorioNaoOperante = + !new List() { StatusModulo.Operante, StatusModulo.Alerta }.Contains(modulo?.status ?? StatusModulo.Desconectado) && + (rover?.ModulosMandatorios?.Any(x => + (x?.Dispositivo ?? T_Code.Vzo) == (modulo?.modulo ?? T_Code.Vzo) && + (x?.Utilizar ?? false) && + (x?.Mandatorio ?? false) + ) ?? false); - foreach (var desc in descricoesCriticas) + // Caso 1: condições operacionais severas + if (temCondicaoSevera) { + 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 + }); + } + } + + // Caso 2: módulo mandatório não operante + if (moduloMandatorioNaoOperante) + { + var statusTexto = (modulo?.status ?? StatusModulo.Desconectado).ToString(); + eventos.Add(new MotivoTopBarModel { - Prioridade = 600, + Prioridade = 610, Fonte = "ModuloCritico", - Titulo = "CONDIÇÕES OPERACIONAIS CRÍTICAS", - Descricao = $"{nomeModulo}: {desc}", - StatusVisual = StatusModulo.Alerta, + Titulo = "MÓDULO MANDATÓRIO NÃO OPERACIONAL", + Descricao = $"{nomeModulo}: módulo mandatório em estado '{statusTexto}'", + StatusVisual = StatusModulo.Falha, Modulo = modulo?.modulo }); } @@ -507,9 +546,11 @@ namespace OperationControl.ViewModels { { T_Code.Ipb, 1 }, { T_Code.Npc, 2 }, - { T_Code.Bat, 3 }, - { T_Code.Atu, 4 }, - { T_Code.Sen, 5 } + { T_Code.Can, 3 }, + { T_Code.Bat, 4 }, + { T_Code.Gps, 5 }, + { T_Code.Atu, 6 }, + { T_Code.Sen, 7 } }; // ============================ @@ -590,7 +631,7 @@ namespace OperationControl.ViewModels if (string.IsNullOrWhiteSpace(linha2)) linha2 = "Sem detalhes adicionais."; - Variaveis.MostrarLog($"LINHA 1: {linha1}"); + Models.Variaveis.MostrarLog($"LINHA 1: {linha1}"); // ============================ // 8) Atualiza UI @@ -619,7 +660,7 @@ namespace OperationControl.ViewModels public void AtualizarDadosGnss(AgroBase.Models.GPSModel dados) { - var bf = Variaveis.GpsService.BaseFix; + var bf = Models.Variaveis.GpsService.BaseFix; if (bf.CorrecaoEmAndamento) { AtualizarProgressoFixacaoBase(bf.Progresso, bf.ProgressoStr); @@ -805,7 +846,7 @@ namespace OperationControl.ViewModels // ESQUERDA var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb); - double c = conexao?.saude ?? 0; + double saude_rede = conexao?.saude ?? 0; double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value() ?? 0.0; double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value() ?? 0.0; double bwTot = conexao?.detalhes?["bw_total_mbps"]?.Value() ?? 0.0; @@ -814,14 +855,13 @@ namespace OperationControl.ViewModels _viewOperacaoLeft?._vm?.AtualizarRover( rover?.Descricao ?? "", - obj.Controle?.PercentualVelocidadeSPKmh ?? 0, - obj.Controle?.Angulo ?? 0, - obj.Controle?.TipoMovimento ?? TipoMovimentoDirecional.Diagnostico, + obj.Controle, obj.Refrigeracao?.Temperatura ?? 0, obj.Bateria?.PercentualBateria ?? 0, obj.Atuador?.PercentualReservatorio ?? 0, - $"{c}%", + $"{saude_rede}%", $"{obj.Gnss?.QualidadeFix ?? TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm", + AgroBase.Models.GPSUtils.DistanciaEntrePontos(new AgroBase.Models.GPSModel() { Latitude = obj.Gnss?.Latitude ?? 0, Longitude = obj.Gnss?.Longitude ?? 0 }, Models.Variaveis.GpsService.UltimaLeitura), obj.Operacao?.Iniciada ?? false );