using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading; using System.Windows.Input; using AmebaPro3_ControlPanel.Services.Uart; using AmebaPro3_ControlPanel.Utilities; namespace AmebaPro3_ControlPanel.ViewModels; public class UartConsoleViewModel : ConsolePanelViewModel { private string? _selectedPort; private int _baudRate; private readonly PythonUartSession _uartSession; private bool _isConnecting; public UartConsoleViewModel(string title, int defaultBaudRate) : base(title) { _baudRate = defaultBaudRate; _uartSession = new PythonUartSession(GetUartBridgePath(), SynchronizationContext.Current); _uartSession.LineReceived += line => LogLines.Add(line); _uartSession.ErrorReceived += line => LogLines.Add($"[UART ERROR] {line}"); _uartSession.Exited += OnSessionExited; ConnectCommand = new RelayCommand(_ => ConnectAsync(), _ => !IsConnected && !_isConnecting && !string.IsNullOrWhiteSpace(SelectedPort)); DisconnectCommand = new RelayCommand(_ => Disconnect(), _ => IsConnected); OpenTeraTermCommand = new RelayCommand(_ => OpenTeraTerm(), _ => !string.IsNullOrWhiteSpace(SelectedPort)); } public ObservableCollection AvailablePorts { get; } = new(); public string? SelectedPort { get => _selectedPort; set { if (SetProperty(ref _selectedPort, value)) { (ConnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); (OpenTeraTermCommand as RelayCommand)?.RaiseCanExecuteChanged(); } } } public int BaudRate { get => _baudRate; set => SetProperty(ref _baudRate, value); } public ICommand ConnectCommand { get; } public ICommand DisconnectCommand { get; } public ICommand OpenTeraTermCommand { get; } public void SetPorts(IEnumerable ports) { AvailablePorts.Clear(); foreach (var port in ports) { AvailablePorts.Add(port); } if (string.IsNullOrWhiteSpace(SelectedPort)) { SelectedPort = AvailablePorts.FirstOrDefault(); } } protected override void SendCurrentCommand(bool addToHistory) { if (string.IsNullOrWhiteSpace(CurrentCommand)) { return; } var command = CurrentCommand.Trim(); base.SendCurrentCommand(addToHistory); if (IsConnected) { _ = _uartSession.SendAsync(command); } else { LogLines.Add("[UART] Not connected."); } } private async void ConnectAsync() { if (_isConnecting || string.IsNullOrWhiteSpace(SelectedPort)) { return; } _isConnecting = true; (ConnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); LogLines.Add($"[UART] Connecting to {SelectedPort} @ {BaudRate}..."); var started = await _uartSession.StartAsync(SelectedPort, BaudRate); if (started) { IsConnected = true; LogLines.Add($"[UART] Connected to {SelectedPort} @ {BaudRate}."); } else { LogLines.Add("[UART] Failed to start session. Ensure Python + pyserial are installed."); } _isConnecting = false; (DisconnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); (ConnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); } private void Disconnect() { _ = DisconnectAsync(); } private async System.Threading.Tasks.Task DisconnectAsync() { IsConnected = false; _isConnecting = false; (DisconnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); (ConnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); await _uartSession.StopAsync(); LogLines.Add("[UART] Disconnected."); } private void OnSessionExited() { if (!IsConnected) { return; } IsConnected = false; _isConnecting = false; LogLines.Add("[UART] Session closed."); (DisconnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); (ConnectCommand as RelayCommand)?.RaiseCanExecuteChanged(); } private void OpenTeraTerm() { if (TeraTermLauncher.TryLaunch(SelectedPort, BaudRate, out var message)) { LogLines.Add($"[UART] {message}"); } else { LogLines.Add($"[UART] {message}"); } } private static string GetUartBridgePath() { return Path.Combine(AppContext.BaseDirectory, "Scripts", "uart_bridge.py"); } }