226 lines
7.3 KiB
C#
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}");
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|