WIP CommandScheduler UI

This commit is contained in:
2023-01-31 23:26:36 +01:00
parent 06a9fc27d7
commit 30ecc6e515
37 changed files with 553 additions and 181 deletions

View File

@@ -2,6 +2,7 @@ namespace FileTime.Core.Command;
public enum ExecutionState
{
Initializing,
Waiting,
Running,
Finished

View File

@@ -4,6 +4,10 @@ namespace FileTime.Core.Command;
public interface ICommand
{
IObservable<string> DisplayLabel { get; }
IObservable<int> TotalProgress { get; }
IObservable<int> CurrentProgress { get; }
Task<CanCommandRun> CanRun(PointInTime currentTime);
Task<PointInTime> SimulateCommand(PointInTime currentTime);
}

View File

@@ -0,0 +1,27 @@
using DynamicData;
using FileTime.Core.Models;
namespace FileTime.Core.Extensions;
public static class BindedCollectionExtensions
{
public static BindedCollection<T> ToBindedCollection<T>(this IObservable<IChangeSet<T>> source)
{
return new BindedCollection<T>(source);
}
public static BindedCollection<T> ToBindedCollection<T>(this IObservable<IObservable<IChangeSet<T>>?> source)
{
return new BindedCollection<T>(source);
}
public static BindedCollection<T, TKey> ToBindedCollection<T, TKey>(this IObservable<IChangeSet<T, TKey>> source) where TKey : notnull
{
return new BindedCollection<T, TKey>(source);
}
public static BindedCollection<T, TKey> ToBindedCollection<T, TKey>(this IObservable<IObservable<IChangeSet<T, TKey>>?> source) where TKey : notnull
{
return new BindedCollection<T, TKey>(source);
}
}

View File

@@ -53,7 +53,7 @@ public static class DynamicDataExtensions
this IObservable<IChangeSet<AbsolutePath, string>> stream)
=> await GetItemsAsync(stream.ToCollection());
private static Task<IEnumerable<AbsolutePath>?> GetItemsAsync(
public static Task<IEnumerable<AbsolutePath>?> GetItemsAsync(
this IObservable<IReadOnlyCollection<AbsolutePath>> stream)
{
var taskCompletionSource = new TaskCompletionSource<IEnumerable<AbsolutePath>?>();

View File

@@ -0,0 +1,106 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using DynamicData;
using PropertyChanged.SourceGenerator;
namespace FileTime.Core.Models;
public partial class BindedCollection<T> : IDisposable, INotifyPropertyChanged
{
private readonly IDisposable? _disposable;
private IDisposable? _innerDisposable;
[Notify] private ReadOnlyObservableCollection<T>? _collection;
public BindedCollection()
{
}
public BindedCollection(IObservable<IChangeSet<T>> dynamicList)
{
_disposable = dynamicList
.Bind(out var collection)
.DisposeMany()
.Subscribe();
_collection = collection;
}
public BindedCollection(IObservable<IObservable<IChangeSet<T>>?> dynamicListSource)
{
_disposable = dynamicListSource.Subscribe(dynamicList =>
{
_innerDisposable?.Dispose();
if (dynamicList is not null)
{
_innerDisposable = dynamicList
.Bind(out var collection)
.DisposeMany()
.Subscribe();
Collection = collection;
}
else
{
Collection = null;
}
});
}
public void Dispose()
{
_disposable?.Dispose();
_innerDisposable?.Dispose();
GC.SuppressFinalize(this);
}
}
public partial class BindedCollection<T, TKey> : IDisposable, INotifyPropertyChanged where TKey : notnull
{
private readonly IDisposable? _disposable;
private IDisposable? _innerDisposable;
[Notify] private ReadOnlyObservableCollection<T>? _collection;
public BindedCollection()
{
}
public BindedCollection(IObservable<IChangeSet<T, TKey>> dynamicList)
{
_disposable = dynamicList
.Bind(out var collection)
.DisposeMany()
.Subscribe();
_collection = collection;
}
public BindedCollection(IObservable<IObservable<IChangeSet<T, TKey>>?> dynamicListSource)
{
_disposable = dynamicListSource.Subscribe(dynamicList =>
{
_innerDisposable?.Dispose();
if (dynamicList is not null)
{
_innerDisposable = dynamicList
.Bind(out var collection)
.DisposeMany()
.Subscribe();
Collection = collection;
}
else
{
Collection = null;
}
});
}
public void Dispose()
{
_disposable?.Dispose();
_innerDisposable?.Dispose();
GC.SuppressFinalize(this);
}
}

View File

@@ -4,18 +4,50 @@ namespace FileTime.Core.Timeline;
public class CommandTimeState
{
private object _executionStateLock = new object();
private ExecutionState _executionState;
public ICommand Command { get; }
public CanCommandRun CanRun { get; private set; } = CanCommandRun.False;
public bool ForceRun { get; set; }
public ExecutionState ExecutionState { get; set; }
public ExecutionState ExecutionState
{
get
{
lock (_executionStateLock)
{
return _executionState;
}
}
set
{
lock (_executionStateLock)
{
_executionState = value;
}
}
}
public CommandTimeState(ICommand command)
{
Command = command;
SetToWait();
}
public async Task UpdateStateAsync(PointInTime? startPoint)
{
CanRun = startPoint == null ? CanCommandRun.False : await Command.CanRun(startPoint);
}
private async void SetToWait()
{
// This command won't be shown in the scheduler until this timeout is over
// Short living commands while not "blink on" the list
await Task.Delay(100);
lock (_executionStateLock)
{
if (_executionState == ExecutionState.Initializing)
_executionState = ExecutionState.Waiting;
}
}
}

View File

@@ -1,3 +1,4 @@
using DynamicData;
using FileTime.Core.Command;
using FileTime.Core.Models;
@@ -7,5 +8,8 @@ public interface ICommandScheduler
{
Task AddCommand(ICommand command, int? batchId = null, bool toNewBatch = false);
IObservable<FullName> ContainerToRefresh { get; }
IObservable<IChangeSet<ParallelCommands>> CommandsToRun { get; }
bool IsRunningEnabled { get; }
void RefreshContainer(FullName container);
Task SetRunningEnabledAsync(bool value);
}

View File

@@ -1,15 +1,32 @@
using DynamicData;
using FileTime.Core.Command;
using FileTime.Core.Extensions;
using FileTime.Core.Models;
namespace FileTime.Core.Timeline;
public class ParallelCommands
public class ParallelCommands : IDisposable
{
private static ushort _idCounter;
private List<CommandTimeState> _commands;
private readonly SourceList<CommandTimeState> _commands;
private PointInTime? _startTime;
public ushort Id { get; }
public IReadOnlyList<CommandTimeState> Commands { get; }
public IObservable<IChangeSet<CommandTimeState>> Commands { get; }
public BindedCollection<CommandTimeState> CommandsCollection { get; }
public PointInTime? Result { get; private set; }
public PointInTime? StartTime => _startTime;
public async Task<PointInTime?> SetStartTimeAsync(PointInTime? startTime)
{
_startTime = startTime;
return await RefreshResult();
}
public ParallelCommands(PointInTime? result)
: this(new List<CommandTimeState>(), result)
{
@@ -19,8 +36,10 @@ public class ParallelCommands
{
Id = _idCounter++;
_commands = commands;
Commands = _commands.AsReadOnly();
_commands = new SourceList<CommandTimeState>();
_commands.Edit((innerList) => innerList.AddRange(commands));
Commands = _commands.Connect();
CommandsCollection = Commands.ToBindedCollection();
Result = result;
}
@@ -63,10 +82,17 @@ public class ParallelCommands
}
}
public async Task<PointInTime?> RefreshResult(PointInTime? startPoint)
public async Task RemoveCommand(ICommand command)
{
var result = startPoint;
foreach (var commandTimeState in _commands)
var commandTimeState = _commands.Items.First(c => c.Command == command);
_commands.Remove(commandTimeState);
await RefreshResult();
}
public async Task<PointInTime?> RefreshResult()
{
var result = StartTime;
foreach (var commandTimeState in _commands.Items)
{
await commandTimeState.UpdateStateAsync(result);
if (result != null)
@@ -87,7 +113,8 @@ public class ParallelCommands
return Result;
}
public void RemoveAt(int number) => _commands.RemoveAt(number);
internal void Remove(CommandTimeState command) => _commands.Remove(command);
public void Dispose()
{
CommandsCollection.Dispose();
}
}