Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions ConsoleProgressBar/AaronLuna.ConsoleProgressBar.csproj
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>ConsoleProgressBar</AssemblyName>
<PackageId>AaronLuna.ConsoleProgressBar</PackageId>
<Description>Console progress bar</Description>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>First release</PackageReleaseNotes>
<Copyright>Copyright 2019 (c)Aaron Luna. All rights reserved.</Copyright>
<PackageTags>Console Progress netcore csharp file-transfer cross-platform</PackageTags>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<RootNamespace>AaronLuna.ConsoleProgressBar</RootNamespace>
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Class1.cs" />
</ItemGroup>

<ItemGroup>
<None Remove="ConsoleProgressBar.dt" />
</ItemGroup>

<ItemGroup>
<Reference Include="AaronLuna.Common">
<HintPath>..\ExternalReferences\AaronLuna.Common.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
21 changes: 12 additions & 9 deletions ConsoleProgressBar/ConsoleProgressBar.cs
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
namespace AaronLuna.ConsoleProgressBar
{
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System;
using System.Linq;
using System.Text;
using System.Threading;

namespace AaronLuna.ConsoleProgressBar
{
public class ConsoleProgressBar : IDisposable, IProgress<double>
{
private readonly TimeSpan _animationInterval = TimeSpan.FromSeconds(1.0 / 8);
Expand Down Expand Up @@ -88,9 +88,11 @@ private string GetProgressBarText(double currentProgress)
string.Empty,
(current, _) => current + IncompleteBlock);

var progressBar = $"{StartBracket}{completedBlocks}{incompleteBlocks}{EndBracket}";
var progressBar =
$"{StartBracket}{completedBlocks}{incompleteBlocks}{EndBracket}";
var percent = $"{currentProgress:P0}".PadLeft(4, '\u00a0');
var animationFrame = AnimationSequence[AnimationIndex++ % AnimationSequence.Length];
var animationFrame =
AnimationSequence[AnimationIndex++ % AnimationSequence.Length];
var animation = $"{animationFrame}";

progressBar = DisplayBar
Expand All @@ -114,7 +116,8 @@ internal void UpdateText(string text)
// Get length of common portion
var commonPrefixLength = 0;
var commonLength = Math.Min(_currentText.Length, text.Length);
while (commonPrefixLength < commonLength && text[commonPrefixLength] == _currentText[commonPrefixLength])
while (commonPrefixLength < commonLength && text[commonPrefixLength] ==
_currentText[commonPrefixLength])
commonPrefixLength++;

// Backtrack to the first differing character
Expand Down
248 changes: 131 additions & 117 deletions ConsoleProgressBar/FileTransferProgressBar.cs
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,118 +1,132 @@
namespace AaronLuna.ConsoleProgressBar
using System;
using System.Linq;
using System.Threading;

namespace AaronLuna.ConsoleProgressBar
{
using System;
using System.Linq;
using System.Threading;
using Common.IO;

public class FileTransferProgressBar : ConsoleProgressBar
{
long _lastReportTicks;

public FileTransferProgressBar(long fileSizeInBytes, TimeSpan timeout)
{
_lastReportTicks = DateTime.Now.Ticks;

FileSizeInBytes = fileSizeInBytes;
BytesReceived = 0;
TimeSpanFileStalled = timeout;
DisplayBytes = true;

Timer = new Timer(TimerHandler);

// A progress bar is only for temporary display in a console window.
// If the console output is redirected to a file, draw nothing.
// Otherwise, we'll end up with a lot of garbage in the target file.
if (!Console.IsOutputRedirected)
{
ResetTimer();
}
}

public long FileSizeInBytes { get; set; }
public long BytesReceived { get; set; }
public TimeSpan TimeSpanFileStalled { get; set; }
public bool DisplayBytes { get; set; }

public event EventHandler<ProgressEventArgs> FileTransferStalled;

public new void Report(double value)
{
var ticks = DateTime.Now.Ticks;
Interlocked.Exchange(ref _lastReportTicks, ticks);

// Make sure value is in [0..1] range
value = Math.Max(0, Math.Min(1, value));
Interlocked.Exchange(ref CurrentProgress, value);
}

void TimerHandler(object state)
{
lock (Timer)
{
if (Disposed) return;
var elapsedTicks = DateTime.Now.Ticks - _lastReportTicks;
var elapsed = TimeSpan.FromTicks(elapsedTicks);

UpdateText(GetProgressBarText(CurrentProgress));
ResetTimer();

if (elapsed < TimeSpanFileStalled) return;

FileTransferStalled?.Invoke(this,
new ProgressEventArgs
{
LastDataReceived = new DateTime(_lastReportTicks),
TimeOutTriggered = DateTime.Now
});
}
}

string GetProgressBarText(double currentProgress)
{
const string singleSpace = " ";

var numBlocksCompleted = (int)(currentProgress * NumberOfBlocks);

var completedBlocks =
Enumerable.Range(0, numBlocksCompleted).Aggregate(
string.Empty,
(current, _) => current + CompletedBlock);

var incompleteBlocks =
Enumerable.Range(0, NumberOfBlocks - numBlocksCompleted).Aggregate(
string.Empty,
(current, _) => current + IncompleteBlock);

var progressBar = $"{StartBracket}{completedBlocks}{incompleteBlocks}{EndBracket}";
var percent = $"{currentProgress:P0}".PadLeft(4, '\u00a0');

var fileSizeInBytes = FileHelper.FileSizeToString(FileSizeInBytes);
var padLength = fileSizeInBytes.Length;
var bytesReceived = FileHelper.FileSizeToString(BytesReceived).PadLeft(padLength, '\u00a0');
var bytes = $"{bytesReceived} of {fileSizeInBytes}";

var animationFrame = AnimationSequence[AnimationIndex++ % AnimationSequence.Length];
var animation = $"{animationFrame}";

progressBar = DisplayBar
? progressBar + singleSpace
: string.Empty;

percent = DisplayPercentComplete
? percent + singleSpace
: string.Empty;

bytes = DisplayBytes
? bytes + singleSpace
: string.Empty;

if (!DisplayAnimation || currentProgress is 1)
{
animation = string.Empty;
}

return progressBar + bytes + percent + animation;
}
}
}
public class FileTransferProgressBar : ConsoleProgressBar
{
private long _lastReportTicks;

public FileTransferProgressBar(long fileSizeInBytes, TimeSpan timeout)
{
_lastReportTicks = DateTime.Now.Ticks;

FileSizeInBytes = fileSizeInBytes;
BytesReceived = 0;
TimeSpanFileStalled = timeout;
DisplayBytes = true;

Timer = new Timer(TimerHandler);

// A progress bar is only for temporary display in a console window.
// If the console output is redirected to a file, draw nothing.
// Otherwise, we'll end up with a lot of garbage in the target file.
if (!Console.IsOutputRedirected)
{
ResetTimer();
}
}

public long FileSizeInBytes { get; set; }
public long BytesReceived { get; set; }
public TimeSpan TimeSpanFileStalled { get; set; }
public bool DisplayBytes { get; set; }

public event EventHandler<ProgressEventArgs> FileTransferStalled;

public new void Report(double value)
{
var ticks = DateTime.Now.Ticks;
Interlocked.Exchange(ref _lastReportTicks, ticks);

// Make sure value is in [0..1] range
value = Math.Max(0, Math.Min(1, value));
Interlocked.Exchange(ref CurrentProgress, value);
}

private void TimerHandler(object state)
{
lock (Timer)
{
if (Disposed) return;
var elapsedTicks = DateTime.Now.Ticks - _lastReportTicks;
var elapsed = TimeSpan.FromTicks(elapsedTicks);

UpdateText(GetProgressBarText(CurrentProgress));
ResetTimer();

if (elapsed < TimeSpanFileStalled) return;

FileTransferStalled?.Invoke(this,
new ProgressEventArgs
{
LastDataReceived = new DateTime(_lastReportTicks),
TimeOutTriggered = DateTime.Now
});
}
}

private string GetProgressBarText(double currentProgress)
{
const string singleSpace = " ";

var numBlocksCompleted = (int) (currentProgress * NumberOfBlocks);

var completedBlocks =
Enumerable.Range(0, numBlocksCompleted).Aggregate(
string.Empty,
(current, _) => current + CompletedBlock);

var incompleteBlocks =
Enumerable.Range(0, NumberOfBlocks - numBlocksCompleted).Aggregate(
string.Empty,
(current, _) => current + IncompleteBlock);

var progressBar =
$"{StartBracket}{completedBlocks}{incompleteBlocks}{EndBracket}";
var percent = $"{currentProgress:P0}".PadLeft(4, '\u00a0');

var fileSizeInBytes = FileSizeToString(FileSizeInBytes);
var padLength = fileSizeInBytes.Length;
var bytesReceived = FileSizeToString(BytesReceived)
.PadLeft(padLength, '\u00a0');
var bytes = $"{bytesReceived} of {fileSizeInBytes}";

var animationFrame =
AnimationSequence[AnimationIndex++ % AnimationSequence.Length];
var animation = $"{animationFrame}";

progressBar = DisplayBar
? progressBar + singleSpace
: string.Empty;

percent = DisplayPercentComplete
? percent + singleSpace
: string.Empty;

bytes = DisplayBytes
? bytes + singleSpace
: string.Empty;

if (!DisplayAnimation || currentProgress is 1)
{
animation = string.Empty;
}

return progressBar + bytes + percent + animation;
}

// not worthwhile referencing a DLL for just this one method
public static string FileSizeToString(long fileSizeInBytes)
{
if ((double) fileSizeInBytes > 1073741824.0)
return $"{(object) ((double) fileSizeInBytes / 1073741824.0):F2} GB";
return (double) fileSizeInBytes > 1048576.0
? $"{(object) ((double) fileSizeInBytes / 1048576.0):F2} MB"
: ((double) fileSizeInBytes > 1024.0
? $"{(object) ((double) fileSizeInBytes / 1024.0):F2} KB"
: $"{(object) fileSizeInBytes} bytes");
}
}
}
32 changes: 19 additions & 13 deletions ConsoleProgressBar/ProgressAnimations.cs
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
namespace AaronLuna.ConsoleProgressBar
{
using System;
using System.Linq;

using Microsoft.VisualBasic;
using System;
using System.Linq;

namespace AaronLuna.ConsoleProgressBar
{
public static class ProgressAnimations
{
// These characters render correctly on Windows and Mac
Expand All @@ -18,23 +16,31 @@ public static class ProgressAnimations

// These characters render correctly only on Mac
public const string RotatingDot = "\u25dc\u25dd\u25de\u25df";
public const string GrowingBarVertical = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2587\u2586\u2585\u2584\u2583\u2581";
public const string RotatingPipe = "\u2524\u2518\u2534\u2514\u251c\u250c\u252c\u2510";

public const string GrowingBarVertical =
"\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2587\u2586\u2585\u2584\u2583\u2581";

public const string RotatingPipe =
"\u2524\u2518\u2534\u2514\u251c\u250c\u252c\u2510";

public const string RotatingCircle = "\u25d0\u25d3\u25d1\u25d2";
public const string GrowingBarHorizontal = "\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u258e\u258d\u258c\u258b\u258a\u2589";

public const string GrowingBarHorizontal =
"\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u258e\u258d\u258c\u258b\u258a\u2589";

public static string RandomBrailleSequence()
{
var rand = new Random();
var sequence = string.Empty;
foreach(int i in Enumerable.Range(0, 40))
foreach (var i in Enumerable.Range(0, 40))
{
var charIndex = rand.Next(10241, 10496);
var randChar = Strings.ChrW(charIndex);
sequence += randChar;
// don't pull in VB for this!
var randChar = Convert.ToChar(charIndex);
sequence += randChar;
}

return sequence;
}
}
}
}
8 changes: 4 additions & 4 deletions ConsoleProgressBar/ProgressEventArgs.cs
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace AaronLuna.ConsoleProgressBar
{
using System;
using System;

namespace AaronLuna.ConsoleProgressBar
{
public class ProgressEventArgs : EventArgs
{
public ProgressEventArgs()
Expand All @@ -14,4 +14,4 @@ public ProgressEventArgs()
public DateTime TimeOutTriggered { get; set; }
public TimeSpan Elapsed => TimeOutTriggered - LastDataReceived;
}
}
}
Loading