using DotBased.Logging; using Manager.App.Services.System; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Manager.App.Services; public abstract class ExtendedBackgroundService : BackgroundService { private TaskCompletionSource _resumeSignal = new(TaskCreationOptions.RunContinuationsAsynchronously); private readonly ILogger _logger; public ServiceState State { get; private set; } = ServiceState.Stopped; public CircularBuffer ProgressEvents { get; } = new(500); public string Name { get; } public string Description { get; set; } public TimeSpan ExecuteInterval { get; set; } public ExtendedBackgroundService(string name, string description, ILogger logger, BackgroundServiceManager manager, TimeSpan? executeInterval = null) { Name = name; Description = description; _logger = logger; manager.RegisterService(this); ExecuteInterval = executeInterval ?? TimeSpan.FromMinutes(1); } protected sealed override async Task ExecuteAsync(CancellationToken stoppingToken) { State = ServiceState.Running; _logger.LogInformation("Initializing background service: {ServiceName}", Name); await InitializeAsync(stoppingToken); try { _logger.LogInformation("Running background service: {ServiceName}", Name); while (!stoppingToken.IsCancellationRequested) { if (State == ServiceState.Paused) { _resumeSignal = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); await _resumeSignal.Task.WaitAsync(stoppingToken); } await Task.Delay(ExecuteInterval, stoppingToken); await ExecuteServiceAsync(stoppingToken); } } catch (Exception e) { if (e is not OperationCanceledException) { State = ServiceState.Faulted; _logger.LogError(e,"Background service {ServiceName} faulted!", Name); throw; } _logger.LogInformation(e,"Service {ServiceName} received cancellation", Name); } finally { State = ServiceState.Stopped; } } protected void LogEvent(string message, LogSeverity severity = LogSeverity.Info) => ProgressEvents.Add(new ServiceEvent(Name, message, DateTime.UtcNow, severity)); public void Pause() { if (State == ServiceState.Running) { State = ServiceState.Paused; _logger.LogInformation("Pauses service: {ServiceName}", Name); } } public void Resume() { if (State == ServiceState.Paused) { State = ServiceState.Running; _resumeSignal.TrySetResult(); _logger.LogInformation("Resumed service: {ServiceName}", Name); } } protected abstract Task InitializeAsync(CancellationToken stoppingToken); protected abstract Task ExecuteServiceAsync(CancellationToken stoppingToken); public override bool Equals(object? obj) { return obj is ExtendedBackgroundService bgService && bgService.Name.Equals(Name, StringComparison.OrdinalIgnoreCase); } public override int GetHashCode() { return Name.GetHashCode(); } } public enum ServiceState { Stopped, Faulted, Running, Paused } public record ServiceEvent(string Source, string Message, DateTime Date, LogSeverity Severity);