using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MikrotikInstaller.App.Credentials; using MikrotikInstaller.Core.Connectivity; using MikrotikInstaller.Core.Connectivity.Demo; using MikrotikInstaller.Core.Devices; namespace MikrotikInstaller.App.ViewModels.Steps; /// /// Erster Schritt: Verbindungsdaten zum MikroTik-Gerät erfassen und testen — nur noch für neue, /// unbekannte Geräte gedacht. "Bekannte Router" leben seit dem Sidebar-Umbau (MainWindow.xaml) /// dauerhaft dort als Kurzauswahl statt hier im Hauptfenster verdoppelt zu werden; dieses ViewModel /// hält aber weiterhin (die Sidebar bindet direkt über /// MainViewModel.Connection darauf) und stellt / /// dafür bereit. Ein Eintrag wird erst im nächsten Schritt /// (Geräte-Erkennung) tatsächlich gespeichert/aktualisiert — dort sind Modell und Seriennummer für /// den Namensvorschlag bzw. die Unterscheidung zweier Geräte mit gleichem Host+Benutzername bekannt. /// public partial class ConnectionStepViewModel : WizardStepViewModelBase { private readonly WizardSession _session; private readonly SavedRoutersStore _savedRoutersStore = new(); public ConnectionStepViewModel(WizardSession session) : base( title: "Verbindung", description: "Gib die Adresse deines MikroTik-Geräts sowie Benutzername und Passwort ein.") { _session = session; RefreshSavedRouters(); } public ObservableCollection SavedRouters { get; } = []; public bool HasSavedRouters => SavedRouters.Count > 0; /// Simulierte Testgeräte (Mac v1.8.0 „Testumgebung"), abschaltbar in den Einstellungen. public IReadOnlyList TestDevices => DemoRouterOsClient.Profiles; public bool ShowTestEnvironment => UserSettings.Current.TestEnvironment; /// Per MNDP gefundene Router (wie „Neighbors" in WinBox), Mac v1.6.0. public ObservableCollection Neighbors { get; } = []; [ObservableProperty] private bool _isSearchingNeighbors; [ObservableProperty] private string? _neighborSearchMessage; [RelayCommand] private async Task SearchNeighborsAsync() { IsSearchingNeighbors = true; NeighborSearchMessage = null; Neighbors.Clear(); try { foreach (var neighbor in await MndpDiscovery.DiscoverAsync(TimeSpan.FromSeconds(4))) { Neighbors.Add(neighbor); } NeighborSearchMessage = Neighbors.Count == 0 ? "Kein MikroTik-Router gefunden. Ist der PC per Kabel oder WLAN mit dem Router verbunden?" : null; } catch (System.Net.Sockets.SocketException) { NeighborSearchMessage = "Die Router-Suche ist gerade nicht möglich. Läuft auf diesem PC ein anderes Programm, das Port 5678 belegt?"; } finally { IsSearchingNeighbors = false; } } [RelayCommand] private void UseNeighbor(MndpNeighbor neighbor) { if (neighbor.IPv4Address is { } address) { Host = address; } } [RelayCommand] private void CopyMac(MndpNeighbor neighbor) => System.Windows.Clipboard.SetText(neighbor.MacAddress); [RelayCommand] private async Task ConnectToTestDeviceAsync(DemoDeviceProfile profile) { Host = profile.Host; Username = DemoRouterOsClient.DemoUsername; Password = DemoRouterOsClient.DemoPassword; UseEncryption = false; await TestConnectionAsync(); } [ObservableProperty] private string _host = string.Empty; [ObservableProperty] private string _username = "admin"; [ObservableProperty] private string _password = string.Empty; [ObservableProperty] private bool _useEncryption; [ObservableProperty] private bool _rememberPassword = true; [ObservableProperty] private bool _isTesting; [ObservableProperty] private string? _statusMessage; [ObservableProperty] private bool _statusIsError; [ObservableProperty] [NotifyPropertyChangedFor(nameof(CanGoNext))] private bool _isConnected; public override bool CanGoNext => IsConnected; private bool CanTestConnection => !IsTesting && !string.IsNullOrWhiteSpace(Host) && !string.IsNullOrWhiteSpace(Username); public override Task OnActivatedAsync() { RefreshSavedRouters(); OnPropertyChanged(nameof(ShowTestEnvironment)); return Task.CompletedTask; } private void RefreshSavedRouters() { SavedRouters.Clear(); var routers = _savedRoutersStore.Load().OrderByDescending(r => r.LastConnectedAt); foreach (var router in routers) { SavedRouters.Add(new SavedRouterEntry(router)); } OnPropertyChanged(nameof(HasSavedRouters)); } /// Nur noch intern von genutzt — Name/Standort werden nirgends mehr manuell bearbeitet, daher kein eigenes Kommando mehr dafür. private void UseSavedRouter(SavedRouterEntry entry) { Host = entry.Host; Username = entry.Username; var storedPassword = WindowsCredentialStore.TryRead(entry.Id); Password = storedPassword ?? string.Empty; RememberPassword = storedPassword is not null; } /// Ein-Klick-Verbinden für die Sidebar-Kurzauswahl — übernimmt die Zugangsdaten und verbindet direkt, ohne den Umweg über die Formularfelder. [RelayCommand] private async Task ConnectToSavedRouterAsync(SavedRouterEntry entry) { UseSavedRouter(entry); await TestConnectionAsync(); } [RelayCommand] private void RemoveSavedRouter(SavedRouterEntry entry) { _savedRoutersStore.Remove(entry.Id); WindowsCredentialStore.Delete(entry.Id); SavedRouters.Remove(entry); OnPropertyChanged(nameof(HasSavedRouters)); } [RelayCommand(CanExecute = nameof(CanTestConnection))] private async Task TestConnectionAsync() { IsTesting = true; StatusMessage = "Verbindung wird getestet …"; StatusIsError = false; IsConnected = false; await _session.DisposeClientAsync(); _session.Device = null; try { var credentials = new RouterOsCredentials(Host.Trim(), Username.Trim(), Password) { UseEncryption = UseEncryption, }; _session.Client = await RouterOsClientFactory.ConnectAsync(credentials); _session.Credentials = credentials; _session.RememberPassword = RememberPassword; IsConnected = true; StatusIsError = false; StatusMessage = "Verbindung erfolgreich hergestellt."; } catch (RouterOsException ex) { IsConnected = false; StatusIsError = true; StatusMessage = ex.Message; } finally { IsTesting = false; } } partial void OnHostChanged(string value) => TestConnectionCommand.NotifyCanExecuteChanged(); partial void OnUsernameChanged(string value) => TestConnectionCommand.NotifyCanExecuteChanged(); partial void OnIsTestingChanged(bool value) => TestConnectionCommand.NotifyCanExecuteChanged(); }