From fce03f5044ff7e8754f5ce25c533e623da88c8bc Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Thu, 13 Aug 2026 21:11:55 -0300 Subject: [PATCH] bico pode ser ligado e desligado com o tempo parametrizado --- .../IHM/Operacao/ucOperacaoPulverizador.cs | 112 +++++++++++++++++- .../Models/Operacoes/OperacaoModel.cs | 111 +++++++++++++++-- 2 files changed, 210 insertions(+), 13 deletions(-) diff --git a/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs b/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs index dbe951b31..7221a75ed 100644 --- a/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs +++ b/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs @@ -5,9 +5,11 @@ using AgroBase.Models.Operadores; using AgroBase.Services; using AgroBase.Services.Operadores; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static AgroBase.Models.Enums; @@ -388,7 +390,9 @@ namespace AgroBase.Forms.IHM.Operacao if (!(op?.Parametros?.Controle?.ControleManualAcionado ?? false)) return; - var bico = op?.DispAtu?.Dados?.BicosPulverizadores?.FirstOrDefault(x => x.ID == card.Codigo); + string bicoId = card.Codigo; + + var bico = op?.DispAtu?.Dados?.BicosPulverizadores?.FirstOrDefault(x => x.ID == bicoId); if (bico == null) return; @@ -400,12 +404,112 @@ namespace AgroBase.Forms.IHM.Operacao setarControle: true, status: card.CommandOn ); + + + + bool ligar = !card.CommandOn; + int tempoAtuacaoMs = Math.Max(0, op?.Parametros?.Controle?.AtuDuracaoAtuacao ?? 0); + + // Qualquer comando novo invalida o timer anterior deste bico. + CancelarTemporizadorBico(bicoId); + + op?.DispAtu?.Dados?.EnviarComandoFisicoBico( + bico, + CanMessagePosicaoDados.Command1, + setarControle: true, + status: ligar + ); + + if (ligar && tempoAtuacaoMs > 0) + { + _ = ProgramarDesligamentoBicoAsync( + bicoId, + tempoAtuacaoMs, + () => + { + op?.DispAtu?.Dados?.EnviarComandoFisicoBico( + bico, + CanMessagePosicaoDados.Command1, + setarControle: true, + status: false + ); + } + ); + } } - - + private readonly ConcurrentDictionary _temporizadoresBicos = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private void CancelarTemporizadorBico(string bicoId) + { + if (_temporizadoresBicos.TryRemove(bicoId, out var temporizador)) + { + try + { + temporizador.Cancel(); + } + finally + { + temporizador.Dispose(); + } + } + } + private async Task ProgramarDesligamentoBicoAsync(string bicoId, int tempoAtuacaoMs, Action desligar) + { + CancelarTemporizadorBico(bicoId); + + var cts = new CancellationTokenSource(); + + if (!_temporizadoresBicos.TryAdd(bicoId, cts)) + { + cts.Dispose(); + return; + } + + try + { + await Task.Delay( + TimeSpan.FromMilliseconds(tempoAtuacaoMs), + cts.Token + ); + + // Garante que este ainda é o temporizador mais recente do bico. + if (_temporizadoresBicos.TryGetValue( + bicoId, + out var atual) && + ReferenceEquals(atual, cts)) + { + desligar(); + } + } + catch (OperationCanceledException) + { + // Outro comando substituiu ou cancelou esta atuação. + } + catch (Exception ex) + { + Models.Variaveis.MostrarLog( + $"Erro no desligamento temporizado do bico {bicoId}: {ex.Message}" + ); + } + finally + { + if (_temporizadoresBicos.TryGetValue( + bicoId, + out var atual) && + ReferenceEquals(atual, cts)) + { + _temporizadoresBicos.TryRemove(bicoId, out _); + } + + cts.Dispose(); + } + } + + + + + - } } diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 8adb16df5..ad7f43a47 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -10,6 +10,7 @@ using AgroMonitor; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing; using System.Globalization; @@ -554,7 +555,7 @@ namespace AgroBase.Models DistanciaManobra = 3.0, AtuAlturaAreaPulverizacao = 10, - AtuDuracaoAtuacao = 200, + AtuDuracaoAtuacao = 2000, AtuPercentualErvasBicoOff = 2, AtuPercentualErvasBicoOn = 1, AtuPercentualInicioPulverizacao = 85, @@ -668,7 +669,7 @@ namespace AgroBase.Models DistanciaManobra = 3.0, AtuAlturaAreaPulverizacao = 10, - AtuDuracaoAtuacao = 200, + AtuDuracaoAtuacao = 0, AtuPercentualErvasBicoOff = 2, AtuPercentualErvasBicoOn = 1, AtuPercentualInicioPulverizacao = 85, @@ -812,7 +813,7 @@ namespace AgroBase.Models DistanciaManobra = 3.0, AtuAlturaAreaPulverizacao = 10, - AtuDuracaoAtuacao = 200, + AtuDuracaoAtuacao = 0, AtuPercentualErvasBicoOff = 2, AtuPercentualErvasBicoOn = 1, AtuPercentualInicioPulverizacao = 85, @@ -954,7 +955,7 @@ namespace AgroBase.Models DistanciaManobra = 3.0, AtuAlturaAreaPulverizacao = 10, - AtuDuracaoAtuacao = 200, + AtuDuracaoAtuacao = 0, AtuPercentualErvasBicoOff = 2, AtuPercentualErvasBicoOn = 1, AtuPercentualInicioPulverizacao = 85, @@ -1056,7 +1057,7 @@ namespace AgroBase.Models DistanciaManobra = 3.0, AtuAlturaAreaPulverizacao = 10, - AtuDuracaoAtuacao = 200, + AtuDuracaoAtuacao = 0, AtuPercentualErvasBicoOff = 2, AtuPercentualErvasBicoOn = 1, AtuPercentualInicioPulverizacao = 85, @@ -2671,6 +2672,72 @@ namespace AgroBase.Models ); } + private readonly ConcurrentDictionary _temporizadoresBicos = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private void CancelarTemporizadorBico(string bicoId) + { + if (_temporizadoresBicos.TryRemove(bicoId, out var temporizador)) + { + try + { + temporizador.Cancel(); + } + finally + { + temporizador.Dispose(); + } + } + } + private async Task ProgramarDesligamentoBicoAsync(string bicoId, int tempoAtuacaoMs, Action desligar) + { + CancelarTemporizadorBico(bicoId); + + var cts = new CancellationTokenSource(); + + if (!_temporizadoresBicos.TryAdd(bicoId, cts)) + { + cts.Dispose(); + return; + } + + try + { + await Task.Delay( + TimeSpan.FromMilliseconds(tempoAtuacaoMs), + cts.Token + ); + + // Garante que este ainda é o temporizador mais recente do bico. + if (_temporizadoresBicos.TryGetValue( + bicoId, + out var atual) && + ReferenceEquals(atual, cts)) + { + desligar(); + } + } + catch (OperationCanceledException) + { + // Outro comando substituiu ou cancelou esta atuação. + } + catch (Exception ex) + { + Models.Variaveis.MostrarLog( + $"Erro no desligamento temporizado do bico {bicoId}: {ex.Message}" + ); + } + finally + { + if (_temporizadoresBicos.TryGetValue( + bicoId, + out var atual) && + ReferenceEquals(atual, cts)) + { + _temporizadoresBicos.TryRemove(bicoId, out _); + } + + cts.Dispose(); + } + } private void ExecutarComandoAtuador(OperacaoComandoBaseModel controleBase, OperacaoModel op) { if (controleBase.Controle == null) @@ -2701,7 +2768,8 @@ namespace AgroBase.Models return; case S_Code.sBIC: - var bico = dadosAtu.BicosPulverizadores?.FirstOrDefault(x => x.ID == controleBase._comp_id); + var bicoId = controleBase._comp_id; + var bico = dadosAtu.BicosPulverizadores?.FirstOrDefault(x => x.ID == bicoId); if (controleBase._comp_value != null) { @@ -2714,13 +2782,38 @@ namespace AgroBase.Models } } - dadosAtu?.EnviarComandoFisicoBico( + bool ligar = controleBase.Controle.Estado == true; + int tempoAtuacaoMs = Math.Max(0, op?.Parametros?.Controle?.AtuDuracaoAtuacao ?? 0); + double anguloComando = controleBase?.Controle?.AnguloSP ?? 0; + + // Qualquer comando novo invalida o timer anterior deste bico. + CancelarTemporizadorBico(bicoId); + + dadosAtu.EnviarComandoFisicoBico( bico, CanMessagePosicaoDados.Command1, setarControle: true, - status: controleBase.Controle.Estado == true, - angulo: controleBase.Controle.AnguloSP + status: ligar, + angulo: anguloComando ); + + if (ligar && tempoAtuacaoMs > 0) + { + _ = ProgramarDesligamentoBicoAsync( + bicoId, + tempoAtuacaoMs, + () => + { + dadosAtu.EnviarComandoFisicoBico( + bico, + CanMessagePosicaoDados.Command1, + setarControle: true, + status: false, + angulo: anguloComando + ); + } + ); + } return; case S_Code.sBMB: