agrobot_base/AgroBase/AgroBase/Models/AsyncTaskTimerModel.cs

226 lines
7.3 KiB
C#

using OpenTK.Platform.Windows;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AgroBase.Models
{
public class AsyncTaskTimerModel : IDisposable
{
public static List<AsyncTaskTimerModel> Timers { get; set; } = new List<AsyncTaskTimerModel>();
private CancellationTokenSource _cancellationTokenSource;
private Func<Task> _taskToExecute;
private int _interval; // Intervalo em milissegundos
public int Interval { get { return _interval; } }
private string _stackTrace;
public string StackTrace { get { return _stackTrace; } }
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;
/// <summary>
/// Inicializa uma nova instância do AsyncTaskTimerService.
/// </summary>
/// <param name="taskToExecute">A tarefa assíncrona que será executada periodicamente.</param>
/// <param name="interval">Intervalo em milissegundos entre as execuções.</param>
/// <param name="uiControl">Controle para executar tarefas relacionadas à UI. Opcional.</param>
public AsyncTaskTimerModel(string id, Func<Task> 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);
}
/// <summary>
/// Inicia o timer assíncrono.
/// </summary>
public void Start()
{
if (_cancellationTokenSource != null) return;
_cancellationTokenSource = new CancellationTokenSource();
ExibirConsole($"START");
Task.Run(() => TimerLoopAsync(_cancellationTokenSource.Token));
}
/// <summary>
/// Para o timer assíncrono.
/// </summary>
public void Stop()
{
if (_cancellationTokenSource == null) return;
_cancellationTokenSource.Cancel();
_cancellationTokenSource = null;
ExibirConsole($"STOP");
}
/// <summary>
/// Reinicia o timer assíncrono.
/// </summary>
public void Restart()
{
Stop();
Start();
ExibirConsole($"RESTART");
}
/// <summary>
/// Alterna o status do timer.
/// </summary>
public void Toogle()
{
if (IsRunning)
{
Stop();
}
else
{
Start();
}
ExibirConsole($"TOOGLE");
}
/// <summary>
/// Define o intervalo entre as execuções.
/// </summary>
/// <param name="interval">Novo intervalo em milissegundos.</param>
public void SetInterval(int interval)
{
if (interval <= 0) throw new ArgumentException("Interval must be greater than zero.", nameof(interval));
_interval = interval;
}
/// <summary>
/// Verifica se o timer está em execução.
/// </summary>
public bool IsRunning
{
get
{
return _cancellationTokenSource != null && !_cancellationTokenSource.Token.IsCancellationRequested && lastTick.AddMilliseconds(_timeout) >= DateTime.Now;
}
}
/// <summary>
/// Loop interno do timer com timeout na execução da tarefa.
/// </summary>
private async Task TimerLoopAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
lastTick = DateTime.Now;
try
{
// 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)
{
// A tarefa foi concluída dentro do limite
await taskToRun;
}
else
{
// Timeout atingido
ExibirConsole($"TIMEOUT - tarefa excedeu {_timeout}ms");
}
// Aguarda o intervalo antes da próxima execução
await Task.Delay(_interval, cancellationToken);
}
catch (OperationCanceledException)
{
// Cancelamento solicitado
ExibirConsole($"CANCEL");
break;
}
catch (Exception ex)
{
// Tratar erros inesperados
ExibirConsole($"ERROR");
Console.WriteLine($"Erro no timer: {ex.Message}");
}
}
ExibirConsole($"ENCERRADO");
}
private bool _disposed = false;
public void Dispose()
{
if (_disposed) return;
// Cancela o token de cancelamento
Stop();
// Remove o timer da lista estática
Timers.Remove(this);
// Libera o CancellationTokenSource
_cancellationTokenSource?.Dispose();
_disposed = true;
ExibirConsole($"DISPOSED");
}
private string ObterIdDoChamador()
{
var stackTrace = new System.Diagnostics.StackTrace();
var frame = stackTrace.GetFrame(2); // Obter o chamador que instanciou este objeto
var method = frame.GetMethod();
return $"{method.DeclaringType?.Name}.{method.Name}";
}
private void ExibirConsole(string msg)
{
if (DebugMessages)
{
Console.WriteLine($"AsyncTaskTimer id = {_stackTrace} - {msg}");
}
}
}
}