using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; using VisionHost.Contracts; namespace VisionHost.Runtime; public sealed class RelayCommand : ICommand { private readonly Action _exec; private readonly Func? _can; public RelayCommand(Action exec, Func? can = null) { _exec = exec; _can = can; } public event EventHandler? CanExecuteChanged; public bool CanExecute(object? parameter) => _can?.Invoke() ?? true; public void Execute(object? parameter) => _exec(); public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty); } public sealed class RunViewModel : INotifyPropertyChanged, IDisposable { private readonly RunService _run; private readonly IUiDispatch _ui; private StationSnapshot _s; private int _disposed; public RunViewModel(RunService run, IUiDispatch? ui = null) { _run = run; _ui = ui ?? new SynchronizationContextDispatch(); _s = run.Snapshot(); StartCommand = new RelayCommand(() => _run.RequestStart(), () => CanStart); StopCommand = new RelayCommand(() => _run.RequestStop(), () => CanStop); _run.Changed += OnChanged; } public static RunViewModel CreateStub(IUserConfirm? confirm = null, IUiDispatch? ui = null) { var cam = new StubFrameSource(); var run = new RunService( cam, cam, new StubInspectService(), new StubPlcIo(), confirm ?? new AlwaysConfirm(), new RuntimeOptions()); return new RunViewModel(run, ui); } public event PropertyChangedEventHandler? PropertyChanged; public ICommand StartCommand { get; } public ICommand StopCommand { get; } public RunState RunState => _s.RunState; public string RecipeName => _s.RecipeName; public string ConnectionText => _s.ConnectionText; public object? LiveImage => _s.LiveImage; public object? OverlayImage => _s.OverlayImage; public string BadgeText => _s.BadgeText; public string LastResultText => _s.LastResultText; public string LastResultBrush => _s.LastResultBrush; public int OkCount => _s.OkCount; public int NgCount => _s.NgCount; public int TotalCount => _s.TotalCount; public string AlarmText => _s.AlarmText; public string AlarmBrush => _s.AlarmBrush; public bool CanStart => _s.CanStart; public bool CanStop => _s.CanStop; public RunService Service => _run; private void OnChanged() => _ui.Post(ApplyOnUi); private void ApplyOnUi() { if (Volatile.Read(ref _disposed) != 0) return; _s = _run.Snapshot(); OnPropertyChanged(string.Empty); ((RelayCommand)StartCommand).RaiseCanExecuteChanged(); ((RelayCommand)StopCommand).RaiseCanExecuteChanged(); } private void OnPropertyChanged([CallerMemberName] string? name = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) != 0) return; _run.Changed -= OnChanged; _run.Dispose(); } }