using System.Collections.ObjectModel; using System.Diagnostics; using System.IO.Pipes; using System.Text.Json; using CommIpc; namespace ParentWpf; public sealed class ChildSession : NotifyBase, IAsyncDisposable { private readonly SemaphoreSlim _writeLock = new(1, 1); private string _status = "Starting"; private double _progressPercent; private string? _currentWorkId; private bool _isConnected; public int Id { get; } public string PipeName { get; } public string PipePath => CommIpc.PipeName.Describe(PipeName); public NamedPipeServerStream Pipe { get; } public Process Process { get; } public CancellationTokenSource LifetimeCts { get; } = new(); public ObservableCollection Logs { get; } = new(); public bool IsConnected { get => _isConnected; set { if (SetField(ref _isConnected, value)) { OnPropertyChanged(nameof(DisplayName)); OnPropertyChanged(nameof(StatusLine)); } } } public string Status { get => _status; set { if (SetField(ref _status, value)) { OnPropertyChanged(nameof(DisplayName)); OnPropertyChanged(nameof(StatusLine)); } } } public string DisplayName => $"Child {Id} ({(IsConnected ? "Connected" : "Disconnected")})"; public string StatusLine => $"{Status} | Work: {(_currentWorkId ?? "-")}"; public double ProgressPercent { get => _progressPercent; set => SetField(ref _progressPercent, value); } public string? CurrentWorkId { get => _currentWorkId; set { if (SetField(ref _currentWorkId, value)) { OnPropertyChanged(nameof(StatusLine)); } } } public ChildSession(int id, string pipeName, NamedPipeServerStream pipe, Process process) { Id = id; PipeName = pipeName; Pipe = pipe; Process = process; } public async Task SendAsync(IpcFrame frame, CancellationToken cancellationToken) { await _writeLock.WaitAsync(cancellationToken); try { await IpcProtocol.WriteFrameAsync(Pipe, frame, cancellationToken); } finally { _writeLock.Release(); } } public void AddLog(string line) { // keep it from growing without bounds during prototyping if (Logs.Count > 2000) { Logs.RemoveAt(0); } Logs.Add(line); } public async ValueTask DisposeAsync() { try { LifetimeCts.Cancel(); } catch { } try { if (!Process.HasExited) { Process.Kill(entireProcessTree: true); } } catch { } try { Pipe.Dispose(); } catch { } try { Process.Dispose(); } catch { } try { LifetimeCts.Dispose(); } catch { } try { _writeLock.Dispose(); } catch { } await Task.CompletedTask; } }