diff --git a/AgroBase/AgroBase/Models/AsyncTaskTimerModel.cs b/AgroBase/AgroBase/Models/AsyncTaskTimerModel.cs index a8e38784d..a33b34f0f 100644 --- a/AgroBase/AgroBase/Models/AsyncTaskTimerModel.cs +++ b/AgroBase/AgroBase/Models/AsyncTaskTimerModel.cs @@ -20,6 +20,9 @@ namespace AgroBase.Models private string _id; public string Id { get { return _id; } } private Control _uiControl; + private int _timeout = 60000; + + private DateTime lastTick = DateTime.MinValue; private bool DebugMessages = true; @@ -29,16 +32,18 @@ namespace AgroBase.Models /// A tarefa assíncrona que será executada periodicamente. /// Intervalo em milissegundos entre as execuções. /// Controle para executar tarefas relacionadas à UI. Opcional. - public AsyncTaskTimerModel(string id, Func taskToExecute, int interval, Control uiControl = null) + public AsyncTaskTimerModel(string id, Func taskToExecute, int interval, Control uiControl = null, int timeout = 60000) { if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id)); if (taskToExecute == null) throw new ArgumentNullException(nameof(taskToExecute)); if (interval <= 0) throw new ArgumentException("Interval must be greater than zero.", nameof(interval)); + if (timeout <= 0) throw new ArgumentException("Timeout must be greater than zero.", nameof(timeout)); _id = id; _stackTrace = ObterIdDoChamador(); _taskToExecute = taskToExecute; _interval = interval; + _timeout = timeout; _uiControl = uiControl; Timers.Add(this); @@ -110,28 +115,50 @@ namespace AgroBase.Models /// /// Verifica se o timer está em execução. /// - public bool IsRunning => _cancellationTokenSource != null; + public bool IsRunning + { + get + { + return _cancellationTokenSource != null && !_cancellationTokenSource.Token.IsCancellationRequested && lastTick.AddMilliseconds(_timeout) >= DateTime.Now; + } + } /// - /// Loop interno do timer. + /// Loop interno do timer com timeout na execução da tarefa. /// private async Task TimerLoopAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { - //ExibirConsole($"TICK"); + lastTick = DateTime.Now; try { - if (_uiControl != null) + // Cria um token de cancelamento específico para o timeout + var timeoutCts = new CancellationTokenSource(); + var timeoutToken = timeoutCts.Token; + + // Define o tempo limite para a execução da tarefa + timeoutCts.CancelAfter(_timeout); + + var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken); + + // Executa a tarefa com timeout + var taskToRun = _uiControl != null + ? FuncoesGlobais.ExecutarMetodoComVerificacaoCrossThreadAsync(_uiControl, _taskToExecute) + : _taskToExecute(); + + var completedTask = await Task.WhenAny(taskToRun, Task.Delay(_timeout, linkedTokenSource.Token)); + + if (completedTask == taskToRun) { - // Se um controle de UI foi especificado, use-o para executar tarefas relacionadas à UI - await FuncoesGlobais.ExecutarMetodoComVerificacaoCrossThreadAsync(_uiControl, _taskToExecute); + // A tarefa foi concluída dentro do limite + await taskToRun; } else { - // Caso contrário, execute a tarefa diretamente - await _taskToExecute(); + // Timeout atingido + ExibirConsole($"TIMEOUT - tarefa excedeu {_timeout}ms"); } // Aguarda o intervalo antes da próxima execução @@ -154,6 +181,7 @@ namespace AgroBase.Models ExibirConsole($"ENCERRADO"); } + private bool _disposed = false; public void Dispose()