From 64fce863d58a2bf96243acb499e1bfe0219017ad Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Wed, 1 Jul 2026 09:00:39 -0300 Subject: [PATCH] melhorado saude do atuador, incluido cavitacao na propria bomba --- .../AgroBase/Models/Modules/AtuadorModel.cs | 47 ++++- .../AgroBase/Models/Modules/PinoutModel.cs | 1 + .../Models/Operacoes/OperacaoModel.cs | 29 ++- AgroBase/AgroBase/Services/OIDCanService.cs | 1 + .../workers/health_worker/modulos/atuador.py | 141 +++++++++---- .../Modulos/BombaPressurizadoraModel_v2.h | 188 +++++++++++++++++- 6 files changed, 358 insertions(+), 49 deletions(-) diff --git a/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs b/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs index fb418f5cb..c1f5591e8 100644 --- a/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs +++ b/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs @@ -719,7 +719,7 @@ namespace AgroBase.Models.Modules analise_health = true, maximo = 120, minimo = 0, - nominal = 85, + nominal = 20, unidade_medida = "PSI" } } @@ -764,7 +764,7 @@ namespace AgroBase.Models.Modules posicao = CanMessagePosicaoDados.Dados1, indice = 2, analise_health = true, - maximo = 20, + maximo = 100, minimo = 0, nominal = 0, unidade_medida = "mL/s" @@ -1080,7 +1080,7 @@ namespace AgroBase.Models.Modules }, new SensorValoresLeituraModel() { - funcao = FuncoesPinout.PressaoSPExtra, + funcao = FuncoesPinout.BitmapDiagnostico, posicao = CanMessagePosicaoDados.Dados2, indice = 5, }, @@ -2329,7 +2329,42 @@ namespace AgroBase.Models.Modules public double _PotenciaFinal => ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados2 && x.funcao == FuncoesPinout.Potencia)?.atual?.valor ?? 0; public double _PressaoSensor => ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados2 && x.funcao == FuncoesPinout.Pressao)?.atual?.valor ?? 0; public double _PressaoSPControle => ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados2 && x.funcao == FuncoesPinout.PressaoSP)?.atual?.valor ?? 0; - public bool _PressaoSPForaDoMapa => ValoresLeituras?.FirstOrDefault(x => x.posicao == CanMessagePosicaoDados.Dados2 && x.funcao == FuncoesPinout.PressaoSPExtra)?.atual?.valor ?? false; + + private int _BitmapDiagnostico + { + get + { + try + { + var valor = ValoresLeituras? + .FirstOrDefault(x => + x.posicao == CanMessagePosicaoDados.Dados2 && + x.funcao == FuncoesPinout.BitmapDiagnostico) + ?.atual?.valor; + + if (valor == null) + return 0; + + return Convert.ToInt32(valor); + } + catch + { + return 0; + } + } + } + private bool TemFlagDiagnostico(int bit) + { + return (_BitmapDiagnostico & (1 << bit)) != 0; + } + public bool _PressaoSPForaDoMapa => TemFlagDiagnostico(0); + public bool _FluxoInsuficiente => TemFlagDiagnostico(1); + public bool _RecuperacaoFluxoAtiva => TemFlagDiagnostico(2); + public bool _CavitacaoSuspeita => TemFlagDiagnostico(3); + public bool _Cavitada => TemFlagDiagnostico(4); + public bool _CorteSegurancaAtivo => TemFlagDiagnostico(5); + public bool _PressaoSPLimitadoPorCapacidade => TemFlagDiagnostico(6); + public int Atuacoes { get; set; } @@ -2912,7 +2947,7 @@ namespace AgroBase.Models.Modules int potenciaFinal = data.Length > 4 ? data[4] : 0; int pressaoSensor = data.Length > 5 ? data[5] : 0; int pressaoSpControle = data.Length > 6 ? data[6] : 0; - bool pressaoSpForaDoMapa = data.Length > 7 && data[7] == 1; + byte flags = data.Length > 7 ? data[7] : (byte)0; AtualizarDados( ID_Num, posicao, @@ -2924,7 +2959,7 @@ namespace AgroBase.Models.Modules potenciaFinal, pressaoSensor, pressaoSpControle, - pressaoSpForaDoMapa + flags } ); diff --git a/AgroBase/AgroBase/Models/Modules/PinoutModel.cs b/AgroBase/AgroBase/Models/Modules/PinoutModel.cs index 19249c3b1..82ca75243 100644 --- a/AgroBase/AgroBase/Models/Modules/PinoutModel.cs +++ b/AgroBase/AgroBase/Models/Modules/PinoutModel.cs @@ -442,6 +442,7 @@ namespace AgroBase.Models.Modules Botao, MalhaFechada, PressaoSPExtra, + BitmapDiagnostico } public static class PinoutUtils diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 8f514ce65..78f515bda 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -3551,17 +3551,19 @@ namespace AgroBase.Models PotenciaFinal = bombaLinha?._PotenciaFinal ?? 0, PressaoSensor = bombaLinha?._PressaoSensor ?? 0, PressaoSPControle = bombaLinha?._PressaoSPControle ?? 0, + PressaoSPForaDoMapa = bombaLinha?._PressaoSPForaDoMapa ?? false, + FluxoInsuficiente = bombaLinha?._FluxoInsuficiente ?? false, + RecuperacaoFluxoAtiva = bombaLinha?._RecuperacaoFluxoAtiva ?? false, + CavitacaoSuspeita = bombaLinha?._CavitacaoSuspeita ?? false, + Cavitada = bombaLinha?._Cavitada ?? false, + CorteSegurancaAtivo = bombaLinha?._CorteSegurancaAtivo ?? false, + PressaoSPLimitadoPorCapacidade = bombaLinha?._PressaoSPLimitadoPorCapacidade ?? false, TempoPulverizadorAtuado = dadosAtu.TempoAtuado, TempoBombaAtuada = dadosAtu.TempoAtuado, - BombaCavitada = - dadosAtu.Conectado && - dadosAtu.PressaoLinha < (pControle?.AtuPressaoLinha ?? 0) && - dadosAtu.PressaoLinha < VariaveisEquipamento.PressaoMinimaCavitacao && - bombaLinha?._EstadoLeitura == Estado.Ligado && - (bombaLinha?._PotenciaFinal ?? bombaLinha?._Potencia ?? 0) > 20, + BombaCavitada = dadosAtu.Conectado && (bombaLinha?._Cavitada ?? false), ModoControleAgitador = pControle?.AtuAgitadorModo ?? ModoAgitadorCalda.SemAgitacao, PotenciaAgitador = dadosAtu.AgitadorDeCalda?.PotenciaMediaAtiva ?? 0, @@ -4978,7 +4980,15 @@ namespace AgroBase.Models public double PotenciaFinal { get; set; } public double PressaoSensor { get; set; } public double PressaoSPControle { get; set; } + + // Flags de cavitaao public bool PressaoSPForaDoMapa { get; set; } + public bool FluxoInsuficiente { get; set; } + public bool RecuperacaoFluxoAtiva { get; set; } + public bool CavitacaoSuspeita { get; set; } + public bool Cavitada { get; set; } + public bool CorteSegurancaAtivo { get; set; } + public bool PressaoSPLimitadoPorCapacidade { get; set; } public double TempoPulverizadorAtuado { get; set; } public double TempoBombaAtuada { get; set; } @@ -5055,7 +5065,14 @@ namespace AgroBase.Models PotenciaFinal = PotenciaFinal, PressaoSensor = PressaoSensor, PressaoSPControle = PressaoSPControle, + PressaoSPForaDoMapa = PressaoSPForaDoMapa, + FluxoInsuficiente = FluxoInsuficiente, + RecuperacaoFluxoAtiva = RecuperacaoFluxoAtiva, + CavitacaoSuspeita = CavitacaoSuspeita, + Cavitada = Cavitada, + CorteSegurancaAtivo = CorteSegurancaAtivo, + PressaoSPLimitadoPorCapacidade = PressaoSPLimitadoPorCapacidade, TempoPulverizadorAtuado = TempoPulverizadorAtuado, TempoBombaAtuada = TempoBombaAtuada, diff --git a/AgroBase/AgroBase/Services/OIDCanService.cs b/AgroBase/AgroBase/Services/OIDCanService.cs index a5b1b3a39..376fd22f3 100644 --- a/AgroBase/AgroBase/Services/OIDCanService.cs +++ b/AgroBase/AgroBase/Services/OIDCanService.cs @@ -432,6 +432,7 @@ namespace AgroBase.Services } ); + op = Variaveis.OperacaoEmAndamento; op.Sensoriamento.Operacao.ModsCalibragem.TryGetValue(T_Code.Mov, out var m); if (m == null) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py index a89ac7b6c..c31f066c1 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/atuador.py @@ -23,9 +23,10 @@ class ModuloAtuador(ModuloDiagnosticoBase): RESPOSTA_RECENTE_MS = 8000.0 RESPOSTA_LENTA_MS = 4500.0 - COMANDO_GRACE_MS = 900.0 - COMANDO_ALERTA_MS = 2500.0 - COMANDO_FALHA_MS = 6000.0 + COMANDO_GRACE_MS = 1500.0 + COMANDO_ALERTA_MS = 4000.0 + COMANDO_FALHA_MS = 8000.0 + PROCESSO_GRACE_MS = 2500.0 FREQ_MIN_DEFAULT_FACTOR = 0.35 PENALIDADE_FREQ_MAX_PADRAO = 4 @@ -467,8 +468,8 @@ class ModuloAtuador(ModuloDiagnosticoBase): score = min(score, 90) if label_sensor == "PRSLN": - bomba_on = self._bomba_principal_ligada(ctx) - if bomba_on and not ctx["pressao_linha_regular"]: + bomba_on = self._bomba_principal_ligada_fisica(ctx) + if bomba_on and not self._pulverizacao_em_transicao(ctx) and not ctx["pressao_linha_regular"]: pressao = ctx["pressao_linha"] motivos.append(f"Pressão baixa com bomba ligada ({pressao:.1f} psi)") condicoes.append({ @@ -485,11 +486,11 @@ class ModuloAtuador(ModuloDiagnosticoBase): score = min(score, 70) if label_sensor == "FLXLN": - bicos_on = len(ctx["bicos_ligados"]) > 0 or len(ctx["bicos_comandados"]) > 0 - bomba_on = self._bomba_principal_ligada(ctx) + bicos_on = len(ctx["bicos_ligados"]) > 0 + bomba_on = self._bomba_principal_ligada_fisica(ctx) vazao = ctx["vazao_linha_mls"] - if bicos_on and bomba_on and vazao <= self.FLUXO_MIN_COM_BICOS_MLS: + if bicos_on and bomba_on and not self._pulverizacao_em_transicao(ctx) and vazao <= self.FLUXO_MIN_COM_BICOS_MLS: motivos.append(f"Fluxo baixo com bicos e bomba ativos ({vazao:.1f} mL/s)") condicoes.append({ "label": "Fluxo de linha", @@ -597,24 +598,27 @@ class ModuloAtuador(ModuloDiagnosticoBase): } if comando and not estado_on: - severidade = 92 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 75 - motivos.append(f"Bico comandado ligado, mas leitura indica desligado ({label})") + severidade = 85 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 70 + + motivos.append(f"Bico comandado ligado, mas leitura ainda indica desligado ({label})") condicoes.append({ "label": "Bico", "valor": 0, "severidade": severidade, - "classe": "falha_hardware" if severidade > 90 else "processo", - "descricao": f"Bico {label} comandado ligado, mas estado lido está desligado", + "classe": "processo", + "descricao": f"Bico {label} comandado ligado, mas estado lido ainda está desligado", "acoes": [ - "Verificar solenoide do bico.", - "Verificar comando físico no ATU.", + "Aguardar próxima atualização CAN.", + "Verificar solenoide do bico se a divergência persistir.", "Conferir alimentação e retorno do bico.", ], }) - score = min(score, 35 if severidade > 90 else 70) + + score = min(score, 55 if severidade >= 85 else 75) if not comando and estado_on: - severidade = 95 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_ALERTA_MS else 85 + severidade = 95 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 82 + motivos.append(f"Bico ligado sem comando ({label})") condicoes.append({ "label": "Bico", @@ -627,7 +631,8 @@ class ModuloAtuador(ModuloDiagnosticoBase): "Verificar se o comando de desligamento chegou ao ATU.", ], }) - score = min(score, 25 if severidade > 90 else 60) + + score = min(score, 25 if severidade > 90 else 65) erro_ang = abs(angulo - angulo_desejado) @@ -747,24 +752,27 @@ class ModuloAtuador(ModuloDiagnosticoBase): } if comando and not bomba_ligada: - severidade = 92 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 78 - motivos.append(f"Bomba {label} comandada ligada, mas leitura não indica acionamento") + severidade = 85 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 70 + + motivos.append(f"Bomba {label} comandada ligada, mas leitura ainda não indica acionamento") condicoes.append({ "label": "Bomba", "valor": potencia, "severidade": severidade, - "classe": "falha_hardware" if severidade > 90 else "processo", - "descricao": f"Bomba {label} comandada ligada, mas estado/potência indicam desligado", + "classe": "processo", + "descricao": f"Bomba {label} comandada ligada, mas estado/potência ainda indicam desligado", "acoes": [ - "Verificar relé/driver da bomba.", - "Verificar alimentação da bomba.", - "Conferir comando físico Command1 do ATU.", + "Aguardar próxima atualização CAN.", + "Verificar relé/driver da bomba se a divergência persistir.", + "Conferir alimentação e retorno da bomba.", ], }) - score = min(score, 35 if severidade > 90 else 68) + + score = min(score, 55 if severidade >= 85 else 75) if not comando and bomba_ligada: - severidade = 95 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_ALERTA_MS else 85 + severidade = 95 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 82 + motivos.append(f"Bomba {label} ligada sem comando") condicoes.append({ "label": "Bomba", @@ -777,7 +785,8 @@ class ModuloAtuador(ModuloDiagnosticoBase): "Verificar se comando de desligamento chegou ao ATU.", ], }) - score = min(score, 25 if severidade > 90 else 60) + + score = min(score, 25 if severidade > 90 else 65) if eh_principal and comando and bomba_ligada: pressao_ref = pressao if pressao > 0 else ctx["pressao_linha"] @@ -799,7 +808,13 @@ class ModuloAtuador(ModuloDiagnosticoBase): }) score = min(score, 25) - elif pressao_alvo > 0 and pressao_ref < max(0, pressao_alvo - self.PRESSAO_TOL_PSI): + elif ( + pressao_alvo > 0 and + pressao_ref < max(0, pressao_alvo - self.PRESSAO_TOL_PSI) and + not ctx["calibrando"] and + not ctx["finalizando"] and + not self._pulverizacao_em_transicao(ctx) + ): severidade = 88 if len(ctx["bicos_ligados"]) > 0 else 75 motivos.append(f"Pressão abaixo do SP ({pressao_ref:.1f}/{pressao_alvo:.1f} psi)") condicoes.append({ @@ -855,7 +870,9 @@ class ModuloAtuador(ModuloDiagnosticoBase): calibrando = ctx["calibrando"] bomba_principal = ctx["bomba_principal"] - bomba_principal_on = self._bomba_principal_ligada(ctx) + em_transicao = self._pulverizacao_em_transicao(ctx) + bomba_principal_on = self._bomba_principal_ligada_fisica(ctx) + bomba_principal_cmd = self._bomba_principal_comandada(ctx) bicos_cmd = len(ctx["bicos_comandados"]) > 0 bicos_on = len(ctx["bicos_ligados"]) > 0 @@ -933,7 +950,15 @@ class ModuloAtuador(ModuloDiagnosticoBase): }) score = min(score, 80) - if (bicos_cmd or bicos_on) and auto and not bomba_principal_on and not calibrando: + if ( + (bicos_cmd or bicos_on) and + auto and + not bomba_principal_on and + not bomba_principal_cmd and + not calibrando and + not ctx["finalizando"] and + not em_transicao + ): pulverizacao_incoerente = True motivos.append("Bicos ativos ou comandados com bomba principal desligada") condicoes.append({ @@ -950,7 +975,14 @@ class ModuloAtuador(ModuloDiagnosticoBase): }) score = min(score, 35) - if bomba_principal_on and bicos_on and ctx["vazao_linha_mls"] <= self.FLUXO_MIN_COM_BICOS_MLS: + if ( + bomba_principal_on and + bicos_on and + not calibrando and + not ctx["finalizando"] and + not em_transicao and + ctx["vazao_linha_mls"] <= self.FLUXO_MIN_COM_BICOS_MLS + ): motivos.append("Fluxo ausente com bomba e bicos ligados") condicoes.append({ "label": "Fluxo de pulverização", @@ -964,7 +996,13 @@ class ModuloAtuador(ModuloDiagnosticoBase): }) score = min(score, 62) - if bomba_principal_on and not ctx["pressao_linha_regular"]: + if ( + bomba_principal_on and + not calibrando and + not ctx["finalizando"] and + not em_transicao and + not ctx["pressao_linha_regular"] + ): motivos.append("Pressão de linha irregular com bomba ligada") condicoes.append({ "label": "Pressão de pulverização", @@ -1251,7 +1289,15 @@ class ModuloAtuador(ModuloDiagnosticoBase): return item return None - def _bomba_principal_ligada(self, ctx): + def _bomba_principal_comandada(self, ctx): + bomba = ctx.get("bomba_principal") + if not bomba: + return False + + dados = bomba.get("dados", {}) + return self._estado_desejado_ligado(dados) + + def _bomba_principal_ligada_fisica(self, ctx): bomba = ctx.get("bomba_principal") if not bomba: return False @@ -1259,9 +1305,29 @@ class ModuloAtuador(ModuloDiagnosticoBase): dados = bomba.get("dados", {}) estado = self._get(dados, "estado_leitura", self._campo_atual(dados, "EstadoLeitura", 0)) potencia = self._float(self._get(dados, "potencia", self._campo_atual(dados, "Potencia", 0)), 0) - comando = self._bool(dados.get("comando_atuar", False)) - return self._estado_ligado(estado) or potencia > self.POTENCIA_MIN_BOMBA_LIGADA or comando + return self._estado_ligado(estado) or potencia > self.POTENCIA_MIN_BOMBA_LIGADA + + def _comando_em_transicao(self, dados, janela_ms=None): + janela_ms = janela_ms if janela_ms is not None else self.PROCESSO_GRACE_MS + ultimo_comando_ms = self._float(dados.get("ultimo_comando_ms", 999999), 999999) + return ultimo_comando_ms < 900000 and ultimo_comando_ms < janela_ms + + def _pulverizacao_em_transicao(self, ctx): + bomba = ctx.get("bomba_principal") + + if bomba and self._comando_em_transicao(bomba.get("dados", {})): + return True + + for b in ctx.get("bicos_comandados", []) or []: + if self._comando_em_transicao(b.get("dados", {})): + return True + + for b in ctx.get("bicos_ligados", []) or []: + if self._comando_em_transicao(b.get("dados", {})): + return True + + return False def _obter_ultima_resposta_ms(self, dados): if not isinstance(dados, dict): @@ -1332,6 +1398,9 @@ class ModuloAtuador(ModuloDiagnosticoBase): if not isinstance(dados, dict): return False + if "comando_atuar" in dados: + return self._bool(dados.get("comando_atuar", False)) + for key in [ "estado_desejado", "status_desejado", @@ -1341,7 +1410,7 @@ class ModuloAtuador(ModuloDiagnosticoBase): if key in dados: return self._estado_ligado(dados.get(key)) - return self._bool(dados.get("comando_atuar", False)) + return False @staticmethod diff --git a/Firmware/Modulos/BombaPressurizadoraModel_v2.h b/Firmware/Modulos/BombaPressurizadoraModel_v2.h index 12834c545..b724965d2 100644 --- a/Firmware/Modulos/BombaPressurizadoraModel_v2.h +++ b/Firmware/Modulos/BombaPressurizadoraModel_v2.h @@ -310,6 +310,27 @@ class BombaPressurizadora_v2 : public ComponenteCAN { bool LinhaArmadaPorFluxo = false; bool FalhaArmamentoLinhaAtual = false; + /* + Detector de cavitacao consolidado na firmware. + Nao corta a bomba neste primeiro momento. + Apenas informa estado diagnostico para o C# via CAN. + */ + bool HabilitarDetectorCavitacao = true; + + double PressaoMinimaCavitacaoPsi = 8.0; + double MargemPressaoSPCavitacaoPsi = 6.0; + double PotenciaMinimaParaDetectarCavitacaoPct = 25.0; + double VazaoMinimaCavitacaoMLs = 2.0; + + unsigned long TempoIgnorarAposLigarBombaMs = 1500; + unsigned long TempoIgnorarAposMudancaBicosMs = 900; + unsigned long TempoParaSuspeitarCavitacaoMs = 900; + unsigned long TempoParaConfirmarCavitacaoMs = 2500; + unsigned long TempoParaLimparCavitacaoMs = 1800; + + bool CavitacaoSuspeitaAtual = false; + bool CavitadaAtual = false; + void ConfigurarBicos(BicoPulverizador_v2** bicos, size_t qtdBicos) { _bicos = bicos; _qtdBicos = qtdBicos; @@ -615,6 +636,8 @@ class BombaPressurizadora_v2 : public ComponenteCAN { PercentualPotenciaLeitura = constrain(PotenciaFinalAtual, 0.0, 100.0); PotenciaLeitura = (int)round(PercentualPotenciaLeitura); EstadoLeitura = (PercentualPotenciaLeitura > 2.0) ? Estado::Ligado : Estado::Desligado; + + AtualizarDetectorCavitacao(); } void RequisitarDados() { @@ -694,7 +717,7 @@ class BombaPressurizadora_v2 : public ComponenteCAN { data.push_back(LimitarByte(PotenciaFinalAtual)); // data[4] data.push_back(LimitarByte(PressaoSensorAtual)); // data[5] data.push_back(LimitarByte(PressaoSPControleAtual)); // data[6] - data.push_back(PressaoSPForaDoMapaAtual ? 1 : 0); // data[7] + data.push_back(MontarFlagsDiagnosticoBomba()); // data[7] break; } @@ -732,9 +755,18 @@ class BombaPressurizadora_v2 : public ComponenteCAN { unsigned long _inicioSemFluxoMs = 0; unsigned long _inicioComFluxoMs = 0; + unsigned long _ultimoComandoLigadoMs = 0; + unsigned long _ultimaMudancaBicosCavitacaoMs = 0; + unsigned long _inicioCondicaoCavitacaoMs = 0; + unsigned long _inicioCondicaoNormalCavitacaoMs = 0; + + uint8_t _ultimaQtdBicosCavitacao = 0; + Estado _ultimoEstadoControleCavitacao = Estado::Desligado; + void ResetarControle() { ResetarIntegralPressao(); ResetarSupervisorFluxo(); + ResetarDetectorCavitacao(); ResetarKick(); _ultimoControleMs = 0; _ultimoTempoDtMs = 0; @@ -760,6 +792,7 @@ class BombaPressurizadora_v2 : public ComponenteCAN { void ResetarEstadosDeBicosFechados() { ResetarIntegralPressao(); ResetarSupervisorFluxo(); + ResetarDetectorCavitacao(); ResetarKick(); PotenciaFeedForwardAtual = 0.0; CorrecaoPIAtual = 0.0; @@ -1421,6 +1454,159 @@ class BombaPressurizadora_v2 : public ComponenteCAN { return (a > b) ? a : b; } + void ResetarDetectorCavitacao() { + CavitacaoSuspeitaAtual = false; + CavitadaAtual = false; + + _inicioCondicaoCavitacaoMs = 0; + _inicioCondicaoNormalCavitacaoMs = 0; + _ultimaMudancaBicosCavitacaoMs = 0; + _ultimoComandoLigadoMs = 0; + + _ultimaQtdBicosCavitacao = 0; + _ultimoEstadoControleCavitacao = _EstadoControle; + } + + void AtualizarDetectorCavitacao() { + unsigned long agora = millis(); + + if (!HabilitarDetectorCavitacao) { + ResetarDetectorCavitacao(); + return; + } + + if (_EstadoControle != _ultimoEstadoControleCavitacao) { + _ultimoEstadoControleCavitacao = _EstadoControle; + + if (_EstadoControle == Estado::Ligado) { + _ultimoComandoLigadoMs = agora; + } else { + ResetarDetectorCavitacao(); + return; + } + } + + if (QtdBicosLigados != _ultimaQtdBicosCavitacao) { + _ultimaQtdBicosCavitacao = QtdBicosLigados; + _ultimaMudancaBicosCavitacaoMs = agora; + + _inicioCondicaoCavitacaoMs = 0; + CavitacaoSuspeitaAtual = false; + } + + if (_EstadoControle != Estado::Ligado) { + ResetarDetectorCavitacao(); + return; + } + + if (QtdBicosLigados == 0) { + ResetarDetectorCavitacao(); + return; + } + + if (!SensorFluxoDisponivel() || sensorPressao == nullptr) { + CavitacaoSuspeitaAtual = false; + CavitadaAtual = false; + _inicioCondicaoCavitacaoMs = 0; + _inicioCondicaoNormalCavitacaoMs = 0; + return; + } + + bool emJanelaIgnorada = false; + + if (_ultimoComandoLigadoMs > 0 && (agora - _ultimoComandoLigadoMs) < TempoIgnorarAposLigarBombaMs) { + emJanelaIgnorada = true; + } + + if (_ultimaMudancaBicosCavitacaoMs > 0 && (agora - _ultimaMudancaBicosCavitacaoMs) < TempoIgnorarAposMudancaBicosMs) { + emJanelaIgnorada = true; + } + + if (emJanelaIgnorada) { + _inicioCondicaoCavitacaoMs = 0; + CavitacaoSuspeitaAtual = false; + return; + } + + bool bombaRealmenteAtiva = + EstadoLeitura == Estado::Ligado && + PotenciaFinalAtual >= PotenciaMinimaParaDetectarCavitacaoPct; + + bool pressaoMuitoBaixa = + PressaoFiltrada <= PressaoMinimaCavitacaoPsi; + + bool pressaoAbaixoDoSP = + _PressaoSP > 0.0 && + PressaoFiltrada <= (_PressaoSP - MargemPressaoSPCavitacaoPsi); + + bool semFluxo = + !FluxoPresenteAtual && + VazaoFluxoCorrigidaAtualMLs <= VazaoMinimaCavitacaoMLs; + + bool condicaoCavitacao = + bombaRealmenteAtiva && + pressaoMuitoBaixa && + pressaoAbaixoDoSP && + semFluxo; + + if (condicaoCavitacao) { + if (_inicioCondicaoCavitacaoMs == 0) { + _inicioCondicaoCavitacaoMs = agora; + } + + _inicioCondicaoNormalCavitacaoMs = 0; + + unsigned long tempoCondicao = agora - _inicioCondicaoCavitacaoMs; + + CavitacaoSuspeitaAtual = + tempoCondicao >= TempoParaSuspeitarCavitacaoMs; + + if (tempoCondicao >= TempoParaConfirmarCavitacaoMs) { + CavitadaAtual = true; + } + + return; + } + + _inicioCondicaoCavitacaoMs = 0; + CavitacaoSuspeitaAtual = false; + + bool condicaoNormal = + _EstadoControle != Estado::Ligado || + QtdBicosLigados == 0 || + FluxoPresenteAtual || + VazaoFluxoCorrigidaAtualMLs > VazaoMinimaCavitacaoMLs || + PressaoFiltrada >= (PressaoMinimaCavitacaoPsi + 3.0) || + PotenciaFinalAtual < PotenciaMinimaParaDetectarCavitacaoPct; + + if (CavitadaAtual && condicaoNormal) { + if (_inicioCondicaoNormalCavitacaoMs == 0) { + _inicioCondicaoNormalCavitacaoMs = agora; + } + + if ((agora - _inicioCondicaoNormalCavitacaoMs) >= TempoParaLimparCavitacaoMs) { + CavitadaAtual = false; + _inicioCondicaoNormalCavitacaoMs = 0; + } + } else if (!condicaoNormal) { + _inicioCondicaoNormalCavitacaoMs = 0; + } + } + + uint8_t MontarFlagsDiagnosticoBomba() { + uint8_t flags = 0; + + if (PressaoSPForaDoMapaAtual) flags |= (1 << 0); + if (FluxoInsuficienteAtual) flags |= (1 << 1); + if (RecuperacaoFluxoAtiva) flags |= (1 << 2); + if (CavitacaoSuspeitaAtual) flags |= (1 << 3); + if (CavitadaAtual) flags |= (1 << 4); + if (CorteSegurancaAtivo) flags |= (1 << 5); + if (PressaoSPLimitadoPorCapacidade) flags |= (1 << 6); + + return flags; + } + void MostrarLog(String mensagem) { if (DebugMode) { PrintTela("[BMB " + _ID + "] " + mensagem);