Files
FlaxEngine/Source/Tools/Flax.Build/Deps/ProgressDisplay.cs
2024-02-26 19:00:48 +01:00

177 lines
5.6 KiB
C#

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Flax.Deps
{
/// <summary>
/// Class to display download progress
/// </summary>
public class ProgressDisplay : IDisposable
{
/// <summary>
/// Current progress
/// </summary>
private long _progress;
/// <summary>
/// Current display thread
/// </summary>
private readonly Thread _asyncDisplayThread;
/// <summary>
/// Progress display console position
/// </summary>
private readonly int _cursorPos;
/// <summary>
/// Queue of diffs from progress
/// </summary>
/// <remarks>
/// Queue last second of data
/// </remarks>
private readonly Queue<long> _progressQueue = new Queue<long>();
/// <summary>
/// Last progress to calculate progress diff
/// </summary>
private long _lastProgress;
/// <summary>
/// Should thread be alive
/// </summary>
private bool _isAlive = true;
/// <summary>
/// Total file size in bytes
/// </summary>
public long TotalFileSizeBytes { get; private set; }
/// <summary>
/// Current progress of task
/// </summary>
public long Progress
{
get => _progress;
set
{
if (value < _progress)
{
throw new Exception("Value cannot be smaller then current progress.");
}
_progress = value;
}
}
/// <summary>
/// Gets a value indicating whether can use system console for progress reporting.
/// </summary>
public static bool CanUseConsole
{
get
{
return false;
/*
try
{
// App crashes on Azure DevOps with exception:
// The handle is invalid.
// at
// at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
// at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean & succeeded)
// at Flax.Deps.ProgressDisplay..ctor(Int64 totalFileSizeBytes)
int a = Console.CursorTop;
Console.SetCursorPosition(0, a + 1);
}
catch (Exception ex)
{
Console.WriteLine("No ProgressDisplay support.");
Console.WriteLine(ex.Message);
Console.WriteLine();
Console.WriteLine(ex.StackTrace);
Console.WriteLine();
return false;
}
return true;
*/
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ProgressDisplay"/> class.
/// </summary>
/// <param name="totalFileSizeBytes">The total file size bytes.</param>
public ProgressDisplay(long totalFileSizeBytes)
{
TotalFileSizeBytes = totalFileSizeBytes;
_cursorPos = Console.CursorTop;
Console.SetCursorPosition(0, _cursorPos + 1);
_asyncDisplayThread = new Thread(Display);
_asyncDisplayThread.Start();
}
/// <summary>
/// Updates the progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="totalLength">The total length.</param>
public void Update(long progress, long totalLength)
{
TotalFileSizeBytes = totalLength <= 0 ? progress : totalLength;
_progress = progress;
}
/// <summary>
/// Updates console text with current progress.
/// </summary>
private void Display()
{
lock (_asyncDisplayThread)
{
while (_isAlive)
{
// Set queue progress
_progressQueue.Enqueue(Progress - _lastProgress);
// Remember last cursor position
var cursorPosTop = Console.CursorTop;
var cursorPosLeft = Console.CursorLeft;
// Set cursor position at progress display location
Console.SetCursorPosition(0, _cursorPos);
var display = $"Downloading: {(Progress / (1024 * 1024f)):0.00} MB / {(TotalFileSizeBytes / (1024 * 1024f)):0.00} - {_progressQueue.Sum() / (1024 * 1024f):0.00} MB/s";
// TODO add blank space fillings at the end of line if previous line was longer
Console.WriteLine(display);
// Set back previous position
Console.SetCursorPosition(cursorPosLeft, cursorPosTop);
// Remove old entries from queue
if (_progressQueue.Count > 10)
{
_progressQueue.Dequeue();
}
_lastProgress = Progress;
// Wait 1/10 of second
Thread.Sleep(100);
}
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_isAlive = false;
}
}
}