using Speckle.InterfaceGenerator; using Speckle.Sdk.Transports; namespace Speckle.Connectors.Common.Operations; [GenerateAutoInterface] public class ProgressDisplayManager(IStopwatchManager stopwatch) : IProgressDisplayManager { private long _lastMs; private const int UPDATE_INTERVAL = 200; public void Begin() => stopwatch.Start(); private long _lastCount; public long LastCount { get => _lastCount; set => _lastCount = value; } public bool ShouldUpdate() { if (stopwatch.ElapsedMilliseconds < _lastMs + UPDATE_INTERVAL) { return false; } _lastMs = stopwatch.ElapsedMilliseconds; return true; } public double? CalculatePercentage(ProgressArgs args) { double? percentage = null; if (args.Total is not null) { percentage = (double)args.Count / args.Total.Value; } return percentage; } public string CalculateSpeed(ProgressArgs args) { if (args.Count == 0) { return string.Empty; } var countPerSecond = (args.Count - _lastCount) / stopwatch.ElapsedSeconds; Interlocked.Exchange(ref _lastCount, args.Count); switch (args.ProgressEvent) { case ProgressEvent.DownloadBytes: case ProgressEvent.UploadBytes: return $"{ToFileSize(countPerSecond)} / sec"; case ProgressEvent.DeserializeObject: case ProgressEvent.SerializeObject: return $"{ThreeNonZeroDigits(countPerSecond)} objects / sec"; default: return string.Empty; } } private static readonly string[] s_suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; private static string ToFileSize(double value) { if (value < 0 || double.IsPositiveInfinity(value)) { return ThreeNonZeroDigits(value) + " " + s_suffixes[0]; } for (int i = 0; i < s_suffixes.Length; i++) { if (value <= (Math.Pow(1024, i + 1))) { return ThreeNonZeroDigits(value / Math.Pow(1024, i)) + " " + s_suffixes[i]; } } return ThreeNonZeroDigits(value / Math.Pow(1024, s_suffixes.Length - 1)) + " " + s_suffixes[^1]; } private static string ThreeNonZeroDigits(double value) { if (value < 0 || double.IsPositiveInfinity(value)) { return "0"; } if (value >= 100) { // No digits after the decimal. return value.ToString("0,0"); } if (value >= 10) { // One digit after the decimal. return value.ToString("0.0"); } // Two digits after the decimal. return value.ToString("0.00"); } }