diff --git a/imagecatalog/DataModel.cs b/imagecatalog/DataModel.cs index 0c7d826..080ef24 100644 --- a/imagecatalog/DataModel.cs +++ b/imagecatalog/DataModel.cs @@ -849,6 +849,11 @@ namespace ImageCatalog_2 // Atomic counter for processed images — avoids expensive ConcurrentBag.Count enumerations private int _processedAtomic = 0; private System.Threading.Timer? _speedTimer; + // Stopwatch used to compute run-wide averages + private Stopwatch? _speedWatch; + // Recent diffs queue to smooth short-term fluctuations + private readonly Queue _recentDiffs = new(); + private int _recentWindowSize = 5; // average over last 5 samples (~5s) private void Test(object parameter) { @@ -907,6 +912,8 @@ namespace ImageCatalog_2 _processedAtomic = 0; // Start speed timer (sample every second using lightweight atomic reads) + _speedWatch = Stopwatch.StartNew(); + _recentDiffs.Clear(); _speedTimer = new System.Threading.Timer(UpdateSpeedCounter, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); var time = await _imageCreationService.CreaCatalogoParallel( @@ -915,9 +922,28 @@ namespace ImageCatalog_2 OnImageProcessed, token); - SpeedCounter = time; + // Compute final averages and show only averages (do not show raw seconds) + var finalProcessed = System.Threading.Volatile.Read(ref _processedAtomic); + double overallAvg = 0.0; + double overallPerMin = 0.0; + if (_speedWatch is not null && _speedWatch.Elapsed.TotalSeconds > 0.0) + { + overallAvg = finalProcessed / _speedWatch.Elapsed.TotalSeconds; + overallPerMin = overallAvg * 60.0; + } + + // Compute elapsed time as h m s and show final averages (no raw seconds parentheses) + var finalElapsed = _speedWatch?.Elapsed ?? TimeSpan.Zero; + int fh = (int)finalElapsed.TotalHours; + int fm = finalElapsed.Minutes; + int fs = finalElapsed.Seconds; + + SpeedCounter = $"{fh}h {fm}m {fs}s{Environment.NewLine}media: {overallAvg:0.00} f/s{Environment.NewLine}media: {overallPerMin:0.00} f/m"; + _speedTimer?.Dispose(); _speedTimer = null; + _speedWatch?.Stop(); + _speedWatch = null; } catch (OperationCanceledException) { @@ -942,12 +968,57 @@ namespace ImageCatalog_2 private void UpdateSpeedCounter(object? state) { - _previousAmount = _currentAmount; - // Read the atomic counter without enumerating the ConcurrentBag - _currentAmount = System.Threading.Volatile.Read(ref _processedAtomic); - int diff = _currentAmount - _previousAmount; - // Report files per second (timer runs every 1s) - SpeedCounter = $"{diff} f/s"; + try + { + _previousAmount = _currentAmount; + // Read the atomic counter without enumerating the ConcurrentBag + _currentAmount = System.Threading.Volatile.Read(ref _processedAtomic); + int diff = _currentAmount - _previousAmount; + + // Protect against negative or spurious diffs + if (diff < 0) diff = 0; + + // Maintain a small sliding window of recent diffs to smooth the display + lock (_recentDiffs) + { + _recentDiffs.Enqueue(diff); + if (_recentDiffs.Count > _recentWindowSize) + _recentDiffs.Dequeue(); + } + + double avgRecent; + lock (_recentDiffs) + { + avgRecent = _recentDiffs.Count == 0 ? 0.0 : _recentDiffs.Average(); + } + + // Compute overall average (since start) if we have a stopwatch + double overall = 0.0; + if (_speedWatch is not null && _speedWatch.Elapsed.TotalSeconds >= 1) + { + var elapsedSeconds = _speedWatch.Elapsed.TotalSeconds; + var total = System.Threading.Volatile.Read(ref _processedAtomic); + overall = elapsedSeconds > 0 ? total / elapsedSeconds : 0.0; + } + + // Recent per-minute estimate + var recentPerMin = avgRecent * 60.0; + var overallPerMin = overall * 60.0; + + // Build a two-line display plus elapsed time: first line shows f/s with overall media and elapsed time, + // second line shows recent photos per minute (media) + var elapsed = _speedWatch?.Elapsed ?? TimeSpan.Zero; + int hours = (int)elapsed.TotalHours; + int minutes = elapsed.Minutes; + int seconds = elapsed.Seconds; + var elapsedStr = $"{hours}h {minutes}m {seconds}s"; + + SpeedCounter = $"{avgRecent:0.00} f/s (media: {overall:0.00} f/s) - {elapsedStr}{Environment.NewLine}media: {recentPerMin:0.00} f/m"; + } + catch + { + // Swallow unlikely errors from timing/queue operations but keep UI responsive + } } private void OnImageProcessed(object? sender, Tuple args) diff --git a/imagecatalog/MainForm.Designer.cs b/imagecatalog/MainForm.Designer.cs index 6b1c48c..0ee4e01 100644 --- a/imagecatalog/MainForm.Designer.cs +++ b/imagecatalog/MainForm.Designer.cs @@ -56,6 +56,8 @@ namespace ImageCatalog Label8 = new Label(); Label7 = new Label(); GroupBox3 = new GroupBox(); + btnOpenDestFolder = new Button(); + btnOpenSourceFolder = new Button(); chkAggiornaSottodirectory = new CheckBox(); _Button3 = new Button(); _Button2 = new Button(); @@ -181,8 +183,6 @@ namespace ImageCatalog _btnCreaCatalogoAsync = new Button(); timer1 = new System.Windows.Forms.Timer(components); dataModelBindingSource1 = new BindingSource(components); - btnOpenSourceFolder = new Button(); - btnOpenDestFolder = new Button(); ((System.ComponentModel.ISupportInitialize)bindingSource1).BeginInit(); ((System.ComponentModel.ISupportInitialize)dataModelBindingSource).BeginInit(); TabControl1.SuspendLayout(); @@ -245,7 +245,7 @@ namespace ImageCatalog // Label43.AutoSize = true; Label43.DataBindings.Add(new Binding("Text", bindingSource1, "SpeedCounter", true)); - Label43.Location = new Point(1073, 789); + Label43.Location = new Point(1074, 725); Label43.Margin = new Padding(6, 0, 6, 0); Label43.Name = "Label43"; Label43.Size = new Size(46, 30); @@ -418,6 +418,24 @@ namespace ImageCatalog GroupBox3.TabStop = false; GroupBox3.Text = "Directory"; // + // btnOpenDestFolder + // + btnOpenDestFolder.Location = new Point(939, 97); + btnOpenDestFolder.Margin = new Padding(6, 8, 6, 8); + btnOpenDestFolder.Name = "btnOpenDestFolder"; + btnOpenDestFolder.Size = new Size(48, 35); + btnOpenDestFolder.TabIndex = 27; + btnOpenDestFolder.Text = "Apri"; + // + // btnOpenSourceFolder + // + btnOpenSourceFolder.Location = new Point(939, 38); + btnOpenSourceFolder.Margin = new Padding(6, 8, 6, 8); + btnOpenSourceFolder.Name = "btnOpenSourceFolder"; + btnOpenSourceFolder.Size = new Size(48, 35); + btnOpenSourceFolder.TabIndex = 26; + btnOpenSourceFolder.Text = "Apri"; + // // chkAggiornaSottodirectory // chkAggiornaSottodirectory.DataBindings.Add(new Binding("Checked", bindingSource1, "UpdateSubdirectories", true, DataSourceUpdateMode.OnPropertyChanged)); @@ -1844,24 +1862,6 @@ namespace ImageCatalog // dataModelBindingSource1.DataSource = typeof(ImageCatalog_2.DataModel); // - // btnOpenSourceFolder - // - btnOpenSourceFolder.Location = new Point(939, 38); - btnOpenSourceFolder.Margin = new Padding(6, 8, 6, 8); - btnOpenSourceFolder.Name = "btnOpenSourceFolder"; - btnOpenSourceFolder.Size = new Size(48, 35); - btnOpenSourceFolder.TabIndex = 26; - btnOpenSourceFolder.Text = "Apri"; - // - // btnOpenDestFolder - // - btnOpenDestFolder.Location = new Point(939, 97); - btnOpenDestFolder.Margin = new Padding(6, 8, 6, 8); - btnOpenDestFolder.Name = "btnOpenDestFolder"; - btnOpenDestFolder.Size = new Size(48, 35); - btnOpenDestFolder.TabIndex = 27; - btnOpenDestFolder.Text = "Apri"; - // // MainForm // AutoScaleDimensions = new SizeF(12F, 30F); diff --git a/imagecatalog/Models/SettingsDto.cs b/imagecatalog/Models/SettingsDto.cs index d5a01b2..70896e0 100644 --- a/imagecatalog/Models/SettingsDto.cs +++ b/imagecatalog/Models/SettingsDto.cs @@ -177,6 +177,10 @@ namespace ImageCatalog_2.Models [XmlElement("GeneraleRotazioneAutomatica")] public bool AutomaticRotation { get; set; } + [JsonPropertyName("UpdateSubdirectories")] + [XmlElement("DirSottoDirectory")] + public bool UpdateSubdirectories { get; set; } + [JsonPropertyName("AddRaceTime")] [XmlElement("TempoGara")] public bool AddRaceTime { get; set; }