WIP CommandScheduler UI
This commit is contained in:
@@ -2,6 +2,7 @@ namespace FileTime.Core.Command;
|
||||
|
||||
public enum ExecutionState
|
||||
{
|
||||
Initializing,
|
||||
Waiting,
|
||||
Running,
|
||||
Finished
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>?>();
|
||||
|
||||
106
src/Core/FileTime.Core.Abstraction/Models/BindedCollection.cs
Normal file
106
src/Core/FileTime.Core.Abstraction/Models/BindedCollection.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user