using System.Collections.Concurrent; using System.Diagnostics; using MaddoShared; using Microsoft.Extensions.Logging; namespace CatalogLite; public readonly record struct ImageProcessedUpdate(string Status, int Total, int Processed); public sealed class ImageProcessingRunResult { public required string FinalSpeedCounter { get; init; } } public sealed class ImageProcessingCoordinator { private readonly ImageCreationService _imageCreationService; private readonly ILogger _logger; public ImageProcessingCoordinator(ImageCreationService imageCreationService, ILogger logger) { _imageCreationService = imageCreationService; _logger = logger; } public async Task RunAsync( ImageCreationService.Options options, CancellationToken token, Action onImageProcessed, Action onSpeedUpdated) { var results = new ConcurrentBag(); var recentDiffs = new Queue(); const int recentWindowSize = 5; var currentAmount = 0; var previousAmount = 0; var processedAtomic = 0; var speedWatch = Stopwatch.StartNew(); using var speedTimer = new System.Threading.Timer(_ => { try { previousAmount = currentAmount; currentAmount = Volatile.Read(ref processedAtomic); var diff = Math.Max(0, currentAmount - previousAmount); lock (recentDiffs) { recentDiffs.Enqueue(diff); if (recentDiffs.Count > recentWindowSize) { recentDiffs.Dequeue(); } } double recentAverage; lock (recentDiffs) { recentAverage = recentDiffs.Count == 0 ? 0.0 : recentDiffs.Average(); } var total = Volatile.Read(ref processedAtomic); var overall = speedWatch.Elapsed.TotalSeconds > 0 ? total / speedWatch.Elapsed.TotalSeconds : 0.0; var elapsed = speedWatch.Elapsed; var elapsedText = $"{(int)elapsed.TotalHours}h {elapsed.Minutes}m {elapsed.Seconds}s"; onSpeedUpdated($"{recentAverage:0.00} f/s (media: {overall:0.00} f/s) - {elapsedText}{Environment.NewLine}media: {recentAverage * 60.0:0.00} f/m"); } catch (Exception ex) { _logger.LogDebug(ex, "Errore durante l'aggiornamento della velocità"); } }, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); EventHandler> onImageProcessedInternal = (_, args) => { var processed = Interlocked.Increment(ref processedAtomic); onImageProcessed(new ImageProcessedUpdate(args.Item1, args.Item2, processed)); }; await _imageCreationService.CreaCatalogoParallel(options, results, onImageProcessedInternal, token).ConfigureAwait(false); speedWatch.Stop(); var finalProcessed = Volatile.Read(ref processedAtomic); var finalAverage = speedWatch.Elapsed.TotalSeconds > 0 ? finalProcessed / speedWatch.Elapsed.TotalSeconds : 0.0; var finalElapsed = speedWatch.Elapsed; return new ImageProcessingRunResult { FinalSpeedCounter = $"{(int)finalElapsed.TotalHours}h {finalElapsed.Minutes}m {finalElapsed.Seconds}s{Environment.NewLine}media: {finalAverage:0.00} f/s{Environment.NewLine}media: {finalAverage * 60.0:0.00} f/m" }; } }