46 lines
1.6 KiB
C#
46 lines
1.6 KiB
C#
using System.Diagnostics;
|
|
|
|
namespace RobotNet.ScriptManager.Services;
|
|
|
|
public abstract class LoopService(int interval) : IHostedService
|
|
{
|
|
private readonly TimeSpan Interval = TimeSpan.FromMilliseconds(interval);
|
|
private readonly CancellationTokenSource cancellationTokenSource = new();
|
|
private readonly Stopwatch stopwatch = new();
|
|
private Timer? timer;
|
|
|
|
public async Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
await BeforExecuteAsync(cancellationToken);
|
|
stopwatch.Start();
|
|
timer = new Timer(Callback, cancellationTokenSource, 0, Timeout.Infinite);
|
|
}
|
|
|
|
public async Task StopAsync(CancellationToken cancellationToken)
|
|
{
|
|
cancellationTokenSource.Cancel();
|
|
timer?.Dispose();
|
|
await AfterExecuteAsync(cancellationToken);
|
|
}
|
|
protected virtual Task BeforExecuteAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
protected abstract void Execute(CancellationToken stoppingToken);
|
|
protected virtual Task AfterExecuteAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
private void Callback(object? state)
|
|
{
|
|
if (state is not CancellationTokenSource cts || cts.IsCancellationRequested) return;
|
|
|
|
Execute(cts.Token);
|
|
|
|
if (!cts.IsCancellationRequested)
|
|
{
|
|
long nextTrigger = Interval.Ticks - (stopwatch.ElapsedTicks % Interval.Ticks);
|
|
if (nextTrigger <= 0) nextTrigger = Interval.Ticks;
|
|
|
|
int nextIntervalMs = (int)(nextTrigger / TimeSpan.TicksPerMillisecond);
|
|
|
|
timer?.Change(nextIntervalMs, Timeout.Infinite);
|
|
}
|
|
}
|
|
}
|