Move TrackedQueue to LibationUiBase
This commit is contained in:
parent
2a59329350
commit
df224cc7f3
@ -1,4 +1,5 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
@ -16,9 +17,8 @@ namespace LibationAvalonia.ViewModels
|
|||||||
public class ProcessQueueViewModel : ViewModelBase, ILogForm
|
public class ProcessQueueViewModel : ViewModelBase, ILogForm
|
||||||
{
|
{
|
||||||
public ObservableCollection<LogEntry> LogEntries { get; } = new();
|
public ObservableCollection<LogEntry> LogEntries { get; } = new();
|
||||||
public TrackedQueue<ProcessBookViewModel> Items { get; } = new();
|
public AvaloniaList<ProcessBookViewModel> Items { get; } = new();
|
||||||
|
public TrackedQueue<ProcessBookViewModel> Queue { get; }
|
||||||
private TrackedQueue<ProcessBookViewModel> Queue => Items;
|
|
||||||
public ProcessBookViewModel SelectedItem { get; set; }
|
public ProcessBookViewModel SelectedItem { get; set; }
|
||||||
public Task QueueRunner { get; private set; }
|
public Task QueueRunner { get; private set; }
|
||||||
public bool Running => !QueueRunner?.IsCompleted ?? false;
|
public bool Running => !QueueRunner?.IsCompleted ?? false;
|
||||||
@ -28,6 +28,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
public ProcessQueueViewModel()
|
public ProcessQueueViewModel()
|
||||||
{
|
{
|
||||||
Logger = LogMe.RegisterForm(this);
|
Logger = LogMe.RegisterForm(this);
|
||||||
|
Queue = new(Items);
|
||||||
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
||||||
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
||||||
|
|
||||||
@ -88,19 +89,19 @@ namespace LibationAvalonia.ViewModels
|
|||||||
|
|
||||||
public decimal SpeedLimitIncrement { get; private set; }
|
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 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);
|
int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success);
|
||||||
|
|
||||||
ErrorCount = errCount;
|
ErrorCount = errCount;
|
||||||
CompletedCount = completeCount;
|
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;
|
QueuedCount = cueCount;
|
||||||
Dispatcher.UIThread.Post(() => this.RaisePropertyChanged(nameof(Progress)));
|
await Dispatcher.UIThread.InvokeAsync(() => this.RaisePropertyChanged(nameof(Progress)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteLine(string text)
|
public void WriteLine(string text)
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
public partial class ProcessQueueControl : UserControl
|
public partial class ProcessQueueControl : UserControl
|
||||||
{
|
{
|
||||||
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Items;
|
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Queue;
|
||||||
private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel;
|
private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel;
|
||||||
|
|
||||||
public ProcessQueueControl()
|
public ProcessQueueControl()
|
||||||
@ -76,14 +76,14 @@ namespace LibationAvalonia.Views
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.Items.Enqueue(testList);
|
vm.Queue.Enqueue(testList);
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
vm.Items.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia.ViewModels
|
namespace LibationUiBase
|
||||||
{
|
{
|
||||||
public enum QueuePosition
|
public enum QueuePosition
|
||||||
{
|
{
|
||||||
@ -33,7 +32,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
* and is stored in ObservableCollection.Items. When the primary list changes, the
|
* and is stored in ObservableCollection.Items. When the primary list changes, the
|
||||||
* secondary list is cleared and reset to match the primary.
|
* 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> CompletedCountChanged;
|
||||||
public event EventHandler<int> QueuededCountChanged;
|
public event EventHandler<int> QueuededCountChanged;
|
||||||
@ -47,6 +46,60 @@ namespace LibationAvalonia.ViewModels
|
|||||||
private readonly List<T> _completed = new();
|
private readonly List<T> _completed = new();
|
||||||
private readonly object lockObject = 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)
|
public bool RemoveQueued(T item)
|
||||||
{
|
{
|
||||||
bool itemsRemoved;
|
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)
|
public void Enqueue(IEnumerable<T> item)
|
||||||
{
|
{
|
||||||
int queueCount;
|
int queueCount;
|
||||||
@ -220,15 +250,15 @@ namespace LibationAvalonia.ViewModels
|
|||||||
queueCount = _queued.Count;
|
queueCount = _queued.Count;
|
||||||
}
|
}
|
||||||
foreach (var i in item)
|
foreach (var i in item)
|
||||||
base.Add(i);
|
_underlyingList?.Add(i);
|
||||||
QueuededCountChanged?.Invoke(this, queueCount);
|
QueuededCountChanged?.Invoke(this, queueCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RebuildSecondary()
|
private void RebuildSecondary()
|
||||||
{
|
{
|
||||||
base.ClearItems();
|
_underlyingList?.Clear();
|
||||||
foreach (var item in GetAllItems())
|
foreach (var item in GetAllItems())
|
||||||
base.Add(item);
|
_underlyingList?.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<T> GetAllItems()
|
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