Move TrackedQueue to LibationUiBase
This commit is contained in:
parent
2a59329350
commit
df224cc7f3
@ -1,4 +1,5 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using DataLayer;
|
||||
@ -16,9 +17,8 @@ namespace LibationAvalonia.ViewModels
|
||||
public class ProcessQueueViewModel : ViewModelBase, ILogForm
|
||||
{
|
||||
public ObservableCollection<LogEntry> LogEntries { get; } = new();
|
||||
public TrackedQueue<ProcessBookViewModel> Items { get; } = new();
|
||||
|
||||
private TrackedQueue<ProcessBookViewModel> Queue => Items;
|
||||
public AvaloniaList<ProcessBookViewModel> Items { get; } = new();
|
||||
public TrackedQueue<ProcessBookViewModel> Queue { get; }
|
||||
public ProcessBookViewModel SelectedItem { get; set; }
|
||||
public Task QueueRunner { get; private set; }
|
||||
public bool Running => !QueueRunner?.IsCompleted ?? false;
|
||||
@ -28,6 +28,7 @@ namespace LibationAvalonia.ViewModels
|
||||
public ProcessQueueViewModel()
|
||||
{
|
||||
Logger = LogMe.RegisterForm(this);
|
||||
Queue = new(Items);
|
||||
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
||||
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
||||
|
||||
@ -88,19 +89,19 @@ namespace LibationAvalonia.ViewModels
|
||||
|
||||
public decimal SpeedLimitIncrement { get; private set; }
|
||||
|
||||
private void Queue_CompletedCountChanged(object sender, int e)
|
||||
private async void Queue_CompletedCountChanged(object sender, int e)
|
||||
{
|
||||
int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail);
|
||||
int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success);
|
||||
|
||||
ErrorCount = errCount;
|
||||
CompletedCount = completeCount;
|
||||
Dispatcher.UIThread.Post(() => this.RaisePropertyChanged(nameof(Progress)));
|
||||
await Dispatcher.UIThread.InvokeAsync(() => this.RaisePropertyChanged(nameof(Progress)));
|
||||
}
|
||||
private void Queue_QueuededCountChanged(object sender, int cueCount)
|
||||
private async void Queue_QueuededCountChanged(object sender, int cueCount)
|
||||
{
|
||||
QueuedCount = cueCount;
|
||||
Dispatcher.UIThread.Post(() => this.RaisePropertyChanged(nameof(Progress)));
|
||||
await Dispatcher.UIThread.InvokeAsync(() => this.RaisePropertyChanged(nameof(Progress)));
|
||||
}
|
||||
|
||||
public void WriteLine(string text)
|
||||
|
||||
@ -15,7 +15,7 @@ namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class ProcessQueueControl : UserControl
|
||||
{
|
||||
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Items;
|
||||
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Queue;
|
||||
private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel;
|
||||
|
||||
public ProcessQueueControl()
|
||||
@ -76,14 +76,14 @@ namespace LibationAvalonia.Views
|
||||
},
|
||||
};
|
||||
|
||||
vm.Items.Enqueue(testList);
|
||||
vm.Items.MoveNext();
|
||||
vm.Items.MoveNext();
|
||||
vm.Items.MoveNext();
|
||||
vm.Items.MoveNext();
|
||||
vm.Items.MoveNext();
|
||||
vm.Items.MoveNext();
|
||||
vm.Items.MoveNext();
|
||||
vm.Queue.Enqueue(testList);
|
||||
vm.Queue.MoveNext();
|
||||
vm.Queue.MoveNext();
|
||||
vm.Queue.MoveNext();
|
||||
vm.Queue.MoveNext();
|
||||
vm.Queue.MoveNext();
|
||||
vm.Queue.MoveNext();
|
||||
vm.Queue.MoveNext();
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
namespace LibationUiBase
|
||||
{
|
||||
public enum QueuePosition
|
||||
{
|
||||
@ -33,7 +32,7 @@ namespace LibationAvalonia.ViewModels
|
||||
* and is stored in ObservableCollection.Items. When the primary list changes, the
|
||||
* secondary list is cleared and reset to match the primary.
|
||||
*/
|
||||
public class TrackedQueue<T> : ObservableCollection<T> where T : class
|
||||
public class TrackedQueue<T> where T : class
|
||||
{
|
||||
public event EventHandler<int> CompletedCountChanged;
|
||||
public event EventHandler<int> QueuededCountChanged;
|
||||
@ -47,6 +46,60 @@ namespace LibationAvalonia.ViewModels
|
||||
private readonly List<T> _completed = new();
|
||||
private readonly object lockObject = new();
|
||||
|
||||
private readonly ICollection<T> _underlyingList;
|
||||
|
||||
public TrackedQueue(ICollection<T> underlyingList = null)
|
||||
{
|
||||
_underlyingList = underlyingList;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (index < _completed.Count)
|
||||
return _completed[index];
|
||||
index -= _completed.Count;
|
||||
|
||||
if (index == 0 && Current != null) return Current;
|
||||
|
||||
if (Current != null) index--;
|
||||
|
||||
if (index < _queued.Count) return _queued.ElementAt(index);
|
||||
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
return _queued.Count + _completed.Count + (Current == null ? 0 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_completed.Contains(item))
|
||||
return _completed.IndexOf(item);
|
||||
|
||||
if (Current == item) return _completed.Count;
|
||||
|
||||
if (_queued.Contains(item))
|
||||
return _queued.IndexOf(item) + (Current is null ? 0 : 1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveQueued(T item)
|
||||
{
|
||||
bool itemsRemoved;
|
||||
@ -188,29 +241,6 @@ namespace LibationAvalonia.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPeek(out T item)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_queued.Count == 0)
|
||||
{
|
||||
item = null;
|
||||
return false;
|
||||
}
|
||||
item = _queued[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_queued.Count == 0) throw new InvalidOperationException("Queue empty");
|
||||
return _queued.Count > 0 ? _queued[0] : default;
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(IEnumerable<T> item)
|
||||
{
|
||||
int queueCount;
|
||||
@ -220,15 +250,15 @@ namespace LibationAvalonia.ViewModels
|
||||
queueCount = _queued.Count;
|
||||
}
|
||||
foreach (var i in item)
|
||||
base.Add(i);
|
||||
_underlyingList?.Add(i);
|
||||
QueuededCountChanged?.Invoke(this, queueCount);
|
||||
}
|
||||
|
||||
private void RebuildSecondary()
|
||||
{
|
||||
base.ClearItems();
|
||||
_underlyingList?.Clear();
|
||||
foreach (var item in GetAllItems())
|
||||
base.Add(item);
|
||||
_underlyingList?.Add(item);
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAllItems()
|
||||
@ -1,250 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibationWinForms.ProcessQueue
|
||||
{
|
||||
public enum QueuePosition
|
||||
{
|
||||
Fisrt,
|
||||
OneUp,
|
||||
OneDown,
|
||||
Last
|
||||
}
|
||||
|
||||
/*
|
||||
* This data structure is like lifting a metal chain one link at a time.
|
||||
* Each time you grab and lift a new link (MoveNext call):
|
||||
*
|
||||
* 1) you're holding a new link in your hand (Current)
|
||||
* 2) the remaining chain to be lifted shortens by 1 link (Queued)
|
||||
* 3) the pile of chain at your feet grows by 1 link (Completed)
|
||||
*
|
||||
* The index is the link position from the first link you lifted to the
|
||||
* last one in the chain.
|
||||
*/
|
||||
public class TrackedQueue<T> where T : class
|
||||
{
|
||||
public event EventHandler<int> CompletedCountChanged;
|
||||
public event EventHandler<int> QueuededCountChanged;
|
||||
public T Current { get; private set; }
|
||||
|
||||
public IReadOnlyList<T> Queued => _queued;
|
||||
public IReadOnlyList<T> Completed => _completed;
|
||||
|
||||
private readonly List<T> _queued = new();
|
||||
private readonly List<T> _completed = new();
|
||||
private readonly object lockObject = new();
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (index < _completed.Count)
|
||||
return _completed[index];
|
||||
index -= _completed.Count;
|
||||
|
||||
if (index == 0 && Current != null) return Current;
|
||||
|
||||
if (Current != null) index--;
|
||||
|
||||
if (index < _queued.Count) return _queued.ElementAt(index);
|
||||
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
return _queued.Count + _completed.Count + (Current == null ? 0 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_completed.Contains(item))
|
||||
return _completed.IndexOf(item);
|
||||
|
||||
if (Current == item) return _completed.Count;
|
||||
|
||||
if (_queued.Contains(item))
|
||||
return _queued.IndexOf(item) + (Current is null ? 0 : 1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RemoveQueued(T item)
|
||||
{
|
||||
bool itemsRemoved;
|
||||
int queuedCount;
|
||||
|
||||
lock (lockObject)
|
||||
{
|
||||
itemsRemoved = _queued.Remove(item);
|
||||
queuedCount = _queued.Count;
|
||||
}
|
||||
|
||||
if (itemsRemoved)
|
||||
QueuededCountChanged?.Invoke(this, queuedCount);
|
||||
return itemsRemoved;
|
||||
}
|
||||
|
||||
public void ClearCurrent()
|
||||
{
|
||||
lock(lockObject)
|
||||
Current = null;
|
||||
}
|
||||
|
||||
public bool RemoveCompleted(T item)
|
||||
{
|
||||
bool itemsRemoved;
|
||||
int completedCount;
|
||||
|
||||
lock (lockObject)
|
||||
{
|
||||
itemsRemoved = _completed.Remove(item);
|
||||
completedCount = _completed.Count;
|
||||
}
|
||||
|
||||
if (itemsRemoved)
|
||||
CompletedCountChanged?.Invoke(this, completedCount);
|
||||
return itemsRemoved;
|
||||
}
|
||||
|
||||
public void ClearQueue()
|
||||
{
|
||||
lock (lockObject)
|
||||
_queued.Clear();
|
||||
QueuededCountChanged?.Invoke(this, 0);
|
||||
}
|
||||
|
||||
public void ClearCompleted()
|
||||
{
|
||||
lock (lockObject)
|
||||
_completed.Clear();
|
||||
CompletedCountChanged?.Invoke(this, 0);
|
||||
}
|
||||
|
||||
public bool Any(Func<T, bool> predicate)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
return (Current != null && predicate(Current)) || _completed.Any(predicate) || _queued.Any(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveQueuePosition(T item, QueuePosition requestedPosition)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_queued.Count == 0 || !_queued.Contains(item)) return;
|
||||
|
||||
if ((requestedPosition == QueuePosition.Fisrt || requestedPosition == QueuePosition.OneUp) && _queued[0] == item)
|
||||
return;
|
||||
if ((requestedPosition == QueuePosition.Last || requestedPosition == QueuePosition.OneDown) && _queued[^1] == item)
|
||||
return;
|
||||
|
||||
int queueIndex = _queued.IndexOf(item);
|
||||
|
||||
if (requestedPosition == QueuePosition.OneUp)
|
||||
{
|
||||
_queued.RemoveAt(queueIndex);
|
||||
_queued.Insert(queueIndex - 1, item);
|
||||
}
|
||||
else if (requestedPosition == QueuePosition.OneDown)
|
||||
{
|
||||
_queued.RemoveAt(queueIndex);
|
||||
_queued.Insert(queueIndex + 1, item);
|
||||
}
|
||||
else if (requestedPosition == QueuePosition.Fisrt)
|
||||
{
|
||||
_queued.RemoveAt(queueIndex);
|
||||
_queued.Insert(0, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
_queued.RemoveAt(queueIndex);
|
||||
_queued.Insert(_queued.Count, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
int completedCount = 0, queuedCount = 0;
|
||||
bool completedChanged = false;
|
||||
try
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (Current != null)
|
||||
{
|
||||
_completed.Add(Current);
|
||||
completedCount = _completed.Count;
|
||||
completedChanged = true;
|
||||
}
|
||||
if (_queued.Count == 0)
|
||||
{
|
||||
Current = null;
|
||||
return false;
|
||||
}
|
||||
Current = _queued[0];
|
||||
_queued.RemoveAt(0);
|
||||
|
||||
queuedCount = _queued.Count;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (completedChanged)
|
||||
CompletedCountChanged?.Invoke(this, completedCount);
|
||||
QueuededCountChanged?.Invoke(this, queuedCount);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPeek(out T item)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_queued.Count == 0)
|
||||
{
|
||||
item = null;
|
||||
return false;
|
||||
}
|
||||
item = _queued[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (_queued.Count == 0) throw new InvalidOperationException("Queue empty");
|
||||
return _queued.Count > 0 ? _queued[0] : default;
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(IEnumerable<T> item)
|
||||
{
|
||||
int queueCount;
|
||||
lock (lockObject)
|
||||
{
|
||||
_queued.AddRange(item);
|
||||
queueCount = _queued.Count;
|
||||
}
|
||||
QueuededCountChanged?.Invoke(this, queueCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user