You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

203 lines
6.4 KiB

using AuroraDesk.Presentation.ViewModels.Base;
using Microsoft.Extensions.Logging;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
namespace AuroraDesk.Presentation.ViewModels.Pages;
public class TcpClientPageViewModel : RoutableViewModel
{
private readonly ILogger<TcpClientPageViewModel>? _logger;
private readonly ObservableCollection<TcpClientSessionViewModel> _sessions = new();
private readonly Dictionary<TcpClientSessionViewModel, IDisposable> _sessionSubscriptions = new();
private TcpClientSessionViewModel? _selectedSession;
private string _globalStatusMessage = "尚未创建会话";
private int _sessionCounter = 1;
public TcpClientPageViewModel(
IScreen hostScreen,
ILogger<TcpClientPageViewModel>? logger = null)
: base(hostScreen, "TcpClient")
{
_logger = logger;
Sessions = new ReadOnlyObservableCollection<TcpClientSessionViewModel>(_sessions);
AddSessionCommand = ReactiveCommand.Create(AddSession);
RemoveSessionCommand = ReactiveCommand.Create<TcpClientSessionViewModel?>(RemoveSession);
_sessions.CollectionChanged += SessionsOnCollectionChanged;
UpdateAggregates();
}
public ReadOnlyObservableCollection<TcpClientSessionViewModel> Sessions { get; }
public TcpClientSessionViewModel? SelectedSession
{
get => _selectedSession;
set => this.RaiseAndSetIfChanged(ref _selectedSession, value);
}
public string GlobalStatusMessage
{
get => _globalStatusMessage;
private set => this.RaiseAndSetIfChanged(ref _globalStatusMessage, value);
}
public int ActiveSessionCount => _sessions.Count(x => x.IsConnected);
public int TotalSentMessages => _sessions.Sum(x => x.SentMessages.Count);
public int TotalReceivedMessages => _sessions.Sum(x => x.ReceivedMessages.Count);
public ReactiveCommand<Unit, Unit> AddSessionCommand { get; }
public ReactiveCommand<TcpClientSessionViewModel?, Unit> RemoveSessionCommand { get; }
private void AddSession()
{
var name = $"会话 {_sessionCounter++}";
var session = new TcpClientSessionViewModel(name);
AttachSession(session);
_sessions.Add(session);
SelectedSession = session;
_logger?.LogInformation("已创建 TCP 会话 {Session}", name);
UpdateAggregates();
}
private void RemoveSession(TcpClientSessionViewModel? session)
{
if (session is null)
{
return;
}
if (_sessions.Contains(session))
{
DetachSession(session);
_sessions.Remove(session);
session.Dispose();
_logger?.LogInformation("已移除 TCP 会话 {Session}", session.SessionName);
if (ReferenceEquals(SelectedSession, session))
{
SelectedSession = _sessions.LastOrDefault();
}
UpdateAggregates();
}
}
private void SessionsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (var item in e.NewItems.OfType<TcpClientSessionViewModel>())
{
AttachSession(item);
}
}
if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
{
foreach (var item in e.OldItems.OfType<TcpClientSessionViewModel>())
{
DetachSession(item);
}
}
if (e.Action == NotifyCollectionChangedAction.Reset)
{
foreach (var subscription in _sessionSubscriptions)
{
subscription.Value.Dispose();
}
_sessionSubscriptions.Clear();
}
UpdateAggregates();
}
private void AttachSession(TcpClientSessionViewModel session)
{
if (_sessionSubscriptions.ContainsKey(session))
{
return;
}
void OnPropertyChanged(object? _, System.ComponentModel.PropertyChangedEventArgs args)
{
if (string.IsNullOrEmpty(args.PropertyName) ||
args.PropertyName is nameof(TcpClientSessionViewModel.IsConnected) or
nameof(TcpClientSessionViewModel.SessionName) or
nameof(TcpClientSessionViewModel.StatusMessage))
{
UpdateAggregates();
}
}
void OnCollectionChanged(object? _, NotifyCollectionChangedEventArgs __) => UpdateAggregates();
void OnMetricsUpdated(object? _, EventArgs __) => UpdateAggregates();
session.PropertyChanged += OnPropertyChanged;
session.SentMessages.CollectionChanged += OnCollectionChanged;
session.ReceivedMessages.CollectionChanged += OnCollectionChanged;
session.MetricsUpdated += OnMetricsUpdated;
var disposer = Disposable.Create(() =>
{
session.PropertyChanged -= OnPropertyChanged;
session.SentMessages.CollectionChanged -= OnCollectionChanged;
session.ReceivedMessages.CollectionChanged -= OnCollectionChanged;
session.MetricsUpdated -= OnMetricsUpdated;
});
_sessionSubscriptions[session] = disposer;
}
private void DetachSession(TcpClientSessionViewModel session)
{
if (_sessionSubscriptions.TryGetValue(session, out var disposer))
{
disposer.Dispose();
_sessionSubscriptions.Remove(session);
}
}
private void UpdateAggregates()
{
this.RaisePropertyChanged(nameof(ActiveSessionCount));
this.RaisePropertyChanged(nameof(TotalSentMessages));
this.RaisePropertyChanged(nameof(TotalReceivedMessages));
GlobalStatusMessage = _sessions.Count switch
{
0 => "尚未创建任何会话",
_ => $"共 {_sessions.Count} 个会话,活动 {_sessions.Count(x => x.IsConnected)} 个"
};
}
public void Dispose()
{
foreach (var session in _sessions.ToList())
{
session.Dispose();
}
foreach (var subscription in _sessionSubscriptions)
{
subscription.Value.Dispose();
}
_sessionSubscriptions.Clear();
_sessions.Clear();
}
}