CopyCommand detail: speed

This commit is contained in:
2023-07-27 13:56:22 +02:00
parent 9171a3de54
commit bfa971f352
13 changed files with 143 additions and 43 deletions

View File

@@ -5,5 +5,6 @@ public interface ICommandTimeStateViewModel
IObservable<int> TotalProgress { get; }
IObservable<int> CurrentProgress { get; }
IObservable<string> DisplayLabel { get; }
IObservable<string> DisplayDetailLabel { get; }
IObservable<bool> IsSelected { get; }
}

View File

@@ -9,12 +9,14 @@ public class CommandTimeStateViewModel : ICommandTimeStateViewModel
public IObservable<int> CurrentProgress { get; }
public IObservable<string> DisplayLabel { get; }
public IObservable<string> DisplayDetailLabel { get; }
public IObservable<bool> IsSelected { get; }
public CommandTimeStateViewModel(CommandTimeState commandTimeState)
{
DisplayLabel = commandTimeState.Command.DisplayLabel;
DisplayDetailLabel = commandTimeState.Command.DisplayDetailLabel;
TotalProgress = commandTimeState.Command.TotalProgress;
CurrentProgress = commandTimeState.Command.CurrentProgress;
//TODO

View File

@@ -5,6 +5,7 @@ namespace FileTime.Core.Command;
public interface ICommand
{
IObservable<string> DisplayLabel { get; }
IObservable<string> DisplayDetailLabel { get; }
IObservable<int> TotalProgress { get; }
IObservable<int> CurrentProgress { get; }

View File

@@ -7,28 +7,33 @@ namespace FileTime.Core.Command;
public abstract class CommandBase : ICommand
{
private readonly BehaviorSubject<string> _displayLabel;
private readonly BehaviorSubject<string> _displayDetailLabel;
private readonly BehaviorSubject<int> _totalProgress;
private readonly BehaviorSubject<int> _currentProgress;
public IObservable<string> DisplayLabel { get; }
public IObservable<string> DisplayDetailLabel { get; }
public IObservable<int> TotalProgress { get; }
public IObservable<int> CurrentProgress { get; }
protected CommandBase(string displayLabel = "", int totalProgress = 0, int currentProgress = 0)
protected CommandBase(string displayLabel = "", string displayDetailLabel = "", int totalProgress = 0, int currentProgress = 0)
{
_displayLabel = new(displayLabel);
_displayDetailLabel = new(displayDetailLabel);
_totalProgress = new(totalProgress);
_currentProgress = new(currentProgress);
DisplayLabel = _displayLabel.AsObservable();
DisplayDetailLabel = _displayDetailLabel.AsObservable();
TotalProgress = _totalProgress.AsObservable();
CurrentProgress = _currentProgress.AsObservable();
}
public abstract Task<CanCommandRun> CanRun(PointInTime currentTime);
public abstract Task<PointInTime> SimulateCommand(PointInTime currentTime);
protected void SetDisplayLabel(string displayLabel) => _displayLabel.OnNext(displayLabel);
protected void SetDisplayLabel(string? displayLabel) => _displayLabel.OnNext(displayLabel ?? string.Empty);
protected void SetDisplayDetailLabel(string? displayLabel) => _displayDetailLabel.OnNext(displayLabel ?? string.Empty);
protected void SetTotalProgress(int totalProgress) => _totalProgress.OnNext(totalProgress);
@@ -42,7 +47,7 @@ public abstract class CommandBase : ICommand
{
var total = data.Sum(d => d.TotalProgress);
if (total == 0) return 0;
return (int)(data.Sum(d => d.Progress) * 100 / total);
return (int) (data.Sum(d => d.Progress) * 100 / total);
})
.Subscribe(SetTotalProgress);
}

View File

@@ -1,8 +1,11 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using ByteSizeLib;
using DeclarativeProperty;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using Microsoft.Extensions.Logging;
namespace FileTime.Core.Command.Copy;
@@ -10,19 +13,25 @@ public class CopyCommand : CommandBase, ITransportationCommand
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
private readonly ILogger<CopyCommand> _logger;
private readonly List<OperationProgress> _operationProgresses = new();
private readonly BehaviorSubject<OperationProgress?> _currentOperationProgress = new(null);
private long _recentTotalSum;
private readonly DeclarativeProperty<long> _recentTotalProcessed = new();
private readonly DeclarativeProperty<DateTime> _recentStartTime = new();
public IReadOnlyList<FullName> Sources { get; }
public FullName Target { get; }
public TransportMode TransportMode { get; }
public CopyCommand(
internal CopyCommand(
ITimelessContentProvider timelessContentProvider,
ICommandSchedulerNotifier commandSchedulerNotifier,
ILogger<CopyCommand> logger,
IReadOnlyCollection<FullName>? sources,
TransportMode? mode,
FullName? targetFullName)
@@ -30,11 +39,12 @@ public class CopyCommand : CommandBase, ITransportationCommand
{
_timelessContentProvider = timelessContentProvider;
_commandSchedulerNotifier = commandSchedulerNotifier;
_logger = logger;
_currentOperationProgress
.Select(p =>
{
if (p is null) return Observable.Never<int>();
return p.Progress.Select(currentProgress => (int)(currentProgress * 100 / p.TotalCount));
return p.Progress.Select(currentProgress => (int) (currentProgress * 100 / p.TotalCount));
})
.Switch()
.Subscribe(SetCurrentProgress);
@@ -46,6 +56,21 @@ public class CopyCommand : CommandBase, ITransportationCommand
Sources = new List<FullName>(sources).AsReadOnly();
TransportMode = mode.Value;
Target = targetFullName;
var recentSpeed = DeclarativePropertyHelpers.CombineLatest(
_recentTotalProcessed,
_recentStartTime,
(total, start) =>
{
var elapsed = DateTime.Now - start;
var size = new ByteSize(total / elapsed.TotalSeconds);
return Task.FromResult(size + "/s");
});
recentSpeed
.Debounce(TimeSpan.FromMilliseconds(500))
.Subscribe(SetDisplayDetailLabel);
}
public override Task<CanCommandRun> CanRun(PointInTime currentTime)
@@ -79,13 +104,16 @@ public class CopyCommand : CommandBase, ITransportationCommand
var resolvedTarget = await _timelessContentProvider.GetItemByFullNameAsync(Target, currentTime);
_recentTotalSum = 0;
await _recentTotalProcessed.SetValue(0);
await _recentStartTime.SetValue(DateTime.Now);
await TraverseTree(
currentTime,
Sources,
new AbsolutePath(_timelessContentProvider, resolvedTarget),
TransportMode,
copyOperation);
//await TimeRunner.RefreshContainer.InvokeAsync(this, Target);
}
private async Task CalculateProgressAsync(PointInTime currentTime)
@@ -119,7 +147,7 @@ public class CopyCommand : CommandBase, ITransportationCommand
.Subscribe(statuses =>
{
var done = statuses.Count(s => s) + 1;
if(done > statuses.Count) done = statuses.Count;
if (done > statuses.Count) done = statuses.Count;
SetDisplayLabel($"Copy - {done} / {statuses.Count}");
});
@@ -135,7 +163,7 @@ public class CopyCommand : CommandBase, ITransportationCommand
{
foreach (var source in sources)
{
var resolvedTarget = (IContainer)await target.ResolveAsync() ?? throw new Exception();
var resolvedTarget = (IContainer) await target.ResolveAsync() ?? throw new Exception();
var item = await _timelessContentProvider.GetItemByFullNameAsync(source, currentTime);
if (item is IContainer container)
@@ -165,8 +193,25 @@ public class CopyCommand : CommandBase, ITransportationCommand
}
}
private Task UpdateProgress() =>
private readonly object _updateProgressLock = new();
private Task UpdateProgress()
{
lock (_updateProgressLock)
{
var now = DateTime.Now;
var delta = now - _recentStartTime.Value;
if (delta.TotalSeconds > 5)
{
_recentTotalSum += _recentTotalProcessed.Value;
_recentStartTime.SetValueSafe(now);
}
var totalProcessedBytes = _operationProgresses.Select(o => o.Progress.Value).Sum();
_recentTotalProcessed.SetValueSafe(totalProcessedBytes - _recentTotalSum);
}
//Not used, progress is reactive in this command
//Note: Maybe this should be removed altogether, and every command should use reactive progress
Task.CompletedTask;
return Task.CompletedTask;
}
}

View File

@@ -12,5 +12,5 @@ public class CopyCommandContext
public OperationProgress? CurrentProgress { get; }
public async Task UpdateProgress() => await _updateProgress.Invoke();
public async Task UpdateProgressAsync() => await _updateProgress.Invoke();
}

View File

@@ -1,5 +1,7 @@
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace FileTime.Core.Command.Copy;
@@ -7,19 +9,29 @@ public class CopyCommandFactory : ITransportationCommandFactory<CopyCommand>
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
private readonly IServiceProvider _serviceProvider;
public CopyCommandFactory(
ITimelessContentProvider timelessContentProvider,
ICommandSchedulerNotifier commandSchedulerNotifier)
ICommandSchedulerNotifier commandSchedulerNotifier,
IServiceProvider serviceProvider)
{
_timelessContentProvider = timelessContentProvider;
_commandSchedulerNotifier = commandSchedulerNotifier;
_serviceProvider = serviceProvider;
}
public CopyCommand GenerateCommand(
IReadOnlyCollection<FullName> sources,
TransportMode mode,
FullName targetFullName)
=> new(_timelessContentProvider, _commandSchedulerNotifier, sources, mode, targetFullName);
FullName targetFullName)
=> new(
_timelessContentProvider,
_commandSchedulerNotifier,
_serviceProvider.GetRequiredService<ILogger<CopyCommand>>(),
sources,
mode,
targetFullName
);
}

View File

@@ -18,7 +18,7 @@ public class CopyStrategy : ICopyStrategy
{
foreach (var item in _copyStrategyParam.OperationProgresses.FindAll(o => o.Key.StartsWith(containerPath.Path.Path)))
{
item.SetProgress(item.TotalCount);
await item.SetProgressAsync(item.TotalCount);
}
await _copyStrategyParam.RefreshContainerAsync(containerPath.Path);
@@ -27,7 +27,11 @@ public class CopyStrategy : ICopyStrategy
public async Task CopyAsync(AbsolutePath from, AbsolutePath to, CopyCommandContext context)
{
await _copy(from, to, context);
context.CurrentProgress?.SetProgress(context.CurrentProgress.TotalCount);
if (context.CurrentProgress is not null)
{
await context.CurrentProgress.SetProgressAsync(context.CurrentProgress.TotalCount);
}
if (to.Path.GetParent() is { } parent)
await _copyStrategyParam.RefreshContainerAsync(parent);
}

View File

@@ -46,6 +46,12 @@ public class DeleteCommand : CommandBase, IExecutableCommand
new Dictionary<string, IItemDeleter>(),
new DeleteStrategy()
);
var parents = ItemsToDelete.Select(i => i.GetParent()).OfType<FullName>().Distinct();
foreach (var parent in parents)
{
await _commandSchedulerNotifier.RefreshContainer(parent);
}
}
private async Task TraverseTree(

View File

@@ -75,7 +75,7 @@ public class MoveCommand : CommandBase, IExecutableCommand
if (itemToMove.Source.GetParent() is { } parent)
await _commandSchedulerNotifier.RefreshContainer(parent);
currentOperationProgress.SetProgress(1);
await currentOperationProgress.SetProgressAsync(1);
}
IItemMover GetOrAddItemMover(IContentProvider provider)

View File

@@ -1,24 +1,26 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DeclarativeProperty;
namespace FileTime.Core.Command;
public class OperationProgress
{
private readonly BehaviorSubject<long> _currentProgress = new(0);
private readonly DeclarativeProperty<long> _currentProgress = new(0);
public string Key { get; }
public IObservable<long> Progress { get; }
public IDeclarativeProperty<long> Progress { get; }
public long TotalCount { get; }
public IObservable<bool> IsDone { get; }
public IDeclarativeProperty<bool> IsDone { get; }
public OperationProgress(string key, long totalCount)
{
Key = key;
TotalCount = totalCount;
Progress = _currentProgress.AsObservable();
IsDone = Progress.Select(p => p >= TotalCount);
Progress = _currentProgress;
IsDone = Progress.Map(p => p >= TotalCount);
}
public void SetProgress(long progress) => _currentProgress.OnNext(progress);
public async Task SetProgressAsync(long progress) => await _currentProgress.SetValue(progress);
public void SetProgressSafe(long progress) =>
_currentProgress.SetValueSafe(progress);
}

View File

@@ -76,8 +76,12 @@ public class StreamCopyCommandHandler : ICommandHandler
await writer.WriteBytesAsync(dataRead);
await writer.FlushAsync();
currentProgress += dataRead.LongLength;
copyCommandContext.CurrentProgress?.SetProgress(currentProgress);
await copyCommandContext.UpdateProgress();
if (copyCommandContext.CurrentProgress is not null)
{
copyCommandContext.CurrentProgress.SetProgressSafe(currentProgress);
}
await copyCommandContext.UpdateProgressAsync();
}
} while (dataRead.Length > 0);
}

View File

@@ -316,25 +316,43 @@
<Border
Background="{DynamicResource ContainerBackgroundColor}"
CornerRadius="10"
Height="50"
Height="70"
Margin="0,0,10,0"
Padding="5">
Padding="5"
Width="300">
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Commands.Collection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" Classes.SelectedTimelineCommand="{Binding IsSelected}">
<StackPanel>
<TextBlock Text="{Binding DisplayLabel^}" />
<ProgressBar
Margin="0,5,0,0"
Maximum="100"
Value="{Binding CurrentProgress^}" />
<ProgressBar
Margin="0,5,0,0"
Maximum="100"
Value="{Binding TotalProgress^}" />
</StackPanel>
<Grid ColumnDefinitions="*, *" RowDefinitions="Auto, Auto">
<TextBlock Grid.ColumnSpan="2" Text="{Binding DisplayLabel^}" />
<StackPanel
Grid.Column="0"
Grid.Row="1"
Margin="0,5,5,5">
<TextBlock Text="{Binding DisplayDetailLabel^}" TextAlignment="Right" />
<ProgressBar
Margin="0,5,0,0"
Maximum="100"
MinWidth="50"
Value="{Binding CurrentProgress^}" />
</StackPanel>
<StackPanel
Grid.Column="1"
Grid.Row="1"
Margin="5,5,0,5">
<TextBlock Text="{Binding TotalProgress^, StringFormat={}{0}%}" TextAlignment="Right" />
<ProgressBar
Margin="0,5,0,0"
Maximum="100"
MinWidth="50"
Value="{Binding TotalProgress^}" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>