diff --git a/Directory.Build.props b/Directory.Build.props index a1f13df..726ef14 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.0.20 + 3.0.24 Jonathan Horvath diff --git a/OSDP-Bench.sln b/OSDP-Bench.sln index 427a1bd..50cfe1f 100644 --- a/OSDP-Bench.sln +++ b/OSDP-Bench.sln @@ -26,8 +26,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{ED2EC291-3353-442C-AD2C-3D3438798EF0}" ProjectSection(SolutionItems) = preProject README.md = README.md - docs\CLAUDE.md = docs\CLAUDE.md docs\ConnectionButtonBehavior.md = docs\ConnectionButtonBehavior.md + CLAUDE.md = CLAUDE.md EndProjectSection EndProject Global diff --git a/OSDP-Bench.sln.DotSettings b/OSDP-Bench.sln.DotSettings index ec3bac6..bb3b972 100644 --- a/OSDP-Bench.sln.DotSettings +++ b/OSDP-Bench.sln.DotSettings @@ -1,5 +1,6 @@  CRC + PD .XX True True \ No newline at end of file diff --git a/ci/package.yml b/ci/package.yml index 3b19bbc..d52117b 100644 --- a/ci/package.yml +++ b/ci/package.yml @@ -6,48 +6,27 @@ steps: version: '10.x' - task: DotNetCoreCLI@2 - displayName: 'dotnet pack Core library' + displayName: 'dotnet restore' inputs: - command: 'pack' - arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/nuget' - packagesToPack: 'src/Core/Core.csproj' + command: 'restore' + projects: 'src/Core/Core.csproj' - task: DotNetCoreCLI@2 - displayName: 'dotnet publish for win-x64' + displayName: 'dotnet build Core library' inputs: - command: 'publish' - publishWebProjects: false - projects: 'src/UI/Windows/Windows.csproj' - arguments: >- - -r win-x64 - --configuration $(buildConfiguration) - --self-contained true - -p:PublishSingleFile=true - -p:IncludeNativeLibrariesForSelfExtract=true - -p:EnableCompressionInSingleFile=true - --output $(Build.ArtifactStagingDirectory)/OSDPBench/win-x64 - zipAfterPublish: false - modifyOutputPath: false + command: 'build' + projects: 'src/Core/Core.csproj' + arguments: '--configuration $(buildConfiguration) --no-restore' - task: DotNetCoreCLI@2 - displayName: 'dotnet publish for win-arm64' + displayName: 'dotnet pack Core library' inputs: - command: 'publish' - publishWebProjects: false - projects: 'src/UI/Windows/Windows.csproj' - arguments: >- - -r win-arm64 - --configuration $(buildConfiguration) - --self-contained true - -p:PublishSingleFile=true - -p:IncludeNativeLibrariesForSelfExtract=true - -p:EnableCompressionInSingleFile=true - --output $(Build.ArtifactStagingDirectory)/OSDPBench/win-arm64 - zipAfterPublish: false - modifyOutputPath: false + command: 'pack' + arguments: '--configuration $(buildConfiguration) --no-build --output $(Build.ArtifactStagingDirectory)/nuget' + packagesToPack: 'src/Core/Core.csproj' - task: PublishPipelineArtifact@1 - displayName: 'Publish artifacts' + displayName: 'Publish NuGet package' inputs: - targetPath: '$(Build.ArtifactStagingDirectory)' - artifactName: Release + targetPath: '$(Build.ArtifactStagingDirectory)/nuget' + artifactName: NuGet diff --git a/docs/ConnectionButtonBehavior.md b/docs/ConnectionButtonBehavior.md index af611a2..8092793 100644 --- a/docs/ConnectionButtonBehavior.md +++ b/docs/ConnectionButtonBehavior.md @@ -1,51 +1,60 @@ ## Implementation Status Summary -### Discover Mode (ConnectionTypeComboBox Index = 0) - -| State | Discovery Button | Connect Button | Disconnect Button | Cancel Button | ConnectionTypeComboBox | Status | -|-------------------------|------------------|----------------|-------------------|---------------|------------------------|---------------| -| **Disconnected** | Visible | Hidden | Hidden | Hidden | Enabled | ✓ Correct | -| **Discovering** | Hidden | Hidden | Hidden | Visible | Disabled | ✓ Correct | -| **Discovery Cancelled** | Visible | Hidden | Hidden | Hidden | Enabled | ✓ Correct | -| **Discovered** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | -| **Connecting** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | -| **Connected** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | -| **Error** | Hidden | Hidden | Visible | Hidden | Enabled | ✓ Correct | - -### Manual Mode (ConnectionTypeComboBox Index = 1) - -| State | Discovery Button | Connect Button | Disconnect Button | Cancel Button | ConnectionTypeComboBox | Status | -|------------------|------------------|----------------|-------------------|---------------|------------------------|-----------| -| **Disconnected** | Hidden | Visible | Hidden | Hidden | Enabled | ✓ Correct | -| **Connecting** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | -| **Connected** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | -| **Error** | Hidden | Hidden | Visible | Hidden | Enabled | ✓ Correct | +### Connection Mode Selection + +The Configuration page uses radio buttons for connection mode selection: + +- **Connect to PD** (IsConnectToPDSelected = true) + - **Discover** (IsDiscoverModeSelected = true) + - **Manual** (IsDiscoverModeSelected = false) +- **Passive Monitoring** (IsConnectToPDSelected = false) + +### Discover Mode (Connect to PD > Discover) + +| State | Discovery Button | Connect Button | Disconnect Button | Cancel Button | Mode Selection | Status | +|-------------------------|------------------|----------------|-------------------|---------------|----------------|---------------| +| **Disconnected** | Visible | Hidden | Hidden | Hidden | Enabled | ✓ Correct | +| **Discovering** | Hidden | Hidden | Hidden | Visible | Disabled | ✓ Correct | +| **Discovery Cancelled** | Visible | Hidden | Hidden | Hidden | Enabled | ✓ Correct | +| **Discovered** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | +| **Connecting** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | +| **Connected** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | +| **Error** | Hidden | Hidden | Visible | Hidden | Enabled | ✓ Correct | + +### Manual Mode (Connect to PD > Manual) + +| State | Discovery Button | Connect Button | Disconnect Button | Cancel Button | Mode Selection | Status | +|------------------|------------------|----------------|-------------------|---------------|----------------|-----------| +| **Disconnected** | Hidden | Visible | Hidden | Hidden | Enabled | ✓ Correct | +| **Connecting** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | +| **Connected** | Hidden | Hidden | Visible | Hidden | Disabled | ✓ Correct | +| **Error** | Hidden | Hidden | Visible | Hidden | Enabled | ✓ Correct | ### Fixed Bugs -**Bug #1: ConnectionTypeComboBox Enable/Disable** ✓ FIXED -- Added `IsConnectionTypeEnabled` property to ConnectViewModel -- ComboBox is now properly disabled during: Discovering, Discovered, Connecting, ConnectingManually, Connected states -- Location: `ConnectViewModel.cs:523-531` +**Bug #1: Mode Selection Enable/Disable** ✓ FIXED +- Added `IsConnectionTypeEnabled` property to ConfigurationViewModel +- Radio buttons are now properly disabled during: Discovering, Discovered, Connecting, ConnectingManually, Connected states +- Location: `ConfigurationViewModel.cs` **Bug #2: Discovery Cancelled State** ✓ FIXED - Changed cancelled/failed discovery to use `StatusLevel.Disconnected` instead of `StatusLevel.Error` - Start Discovery button now correctly appears after cancelling discovery -- Location: `ConnectViewModel.cs:335-348` +- Location: `ConfigurationViewModel.cs` **Bug #3: Connecting States Missing from isConnected Check** ✓ FIXED - Added `StatusLevel.Connecting` and `StatusLevel.ConnectingManually` to button visibility checks - Buttons now show correct visibility during connection attempts -- Location: `ConnectViewModel.cs:479-545` +- Location: `ConfigurationViewModel.cs` **Bug #4: Discovery Button May Appear During Connection** ✓ FIXED - Updated StartDiscovery visibility check to account for all connecting states - Discovery button no longer appears during connection attempts -- Location: `ConnectViewModel.cs:503-515` +- Location: `ConfigurationViewModel.cs` **Bug #5: Disconnect Button Not Visible for Invalid Security Key Error** ✓ FIXED - Added `StatusLevel.Error` to disconnect button visibility check - When an invalid security key error occurs, the disconnect button is now shown - This allows users to properly disconnect and clean up the connection state -- Location: `ConnectViewModel.cs:520-534` -- Test: `ConnectViewModelTests.cs:292-304` \ No newline at end of file +- Location: `ConfigurationViewModel.cs` +- Test: `ConfigurationViewModelTests.cs` \ No newline at end of file diff --git a/src/Core/Resources/Resources.resx b/src/Core/Resources/Resources.resx index 0691e2f..ebe2484 100644 --- a/src/Core/Resources/Resources.resx +++ b/src/Core/Resources/Resources.resx @@ -154,8 +154,8 @@ - Connect - Title for the Connect page + Configuration + Title for the Configuration page Manage @@ -262,8 +262,8 @@ Message when device is not identified - The Connection page will provide more details - Message directing user to connection page + The Configuration page will provide more details + Message directing user to configuration page Device Information @@ -338,13 +338,9 @@ Unable to decrypt Message shown in packet details when decryption fails - - Default Key - Badge shown when passive monitoring with default key decryption - - - Custom Key - Badge shown when passive monitoring with custom key decryption + + Monitor + Badge shown when in passive monitoring mode Security @@ -375,8 +371,8 @@ Button text to expand row details - The Connection page will provide more details - Message directing users to connection page for details + The Configuration page will provide more details + Message directing users to configuration page for details Last Card Read @@ -441,8 +437,8 @@ - Connect To PD - Navigation menu item for Connect page + Config + Navigation menu item for Configuration page Manage PD @@ -627,10 +623,14 @@ Connection type option for manual connection - Passive Monitor + Passive Monitoring Connection type option for passive bus monitoring - + + Connect to PD + Radio button label for Connect to PD mode + + Language diff --git a/src/Core/ViewModels/Pages/ConnectViewModel.cs b/src/Core/ViewModels/Pages/ConfigurationViewModel.cs similarity index 89% rename from src/Core/ViewModels/Pages/ConnectViewModel.cs rename to src/Core/ViewModels/Pages/ConfigurationViewModel.cs index 250c745..dc7d8ea 100644 --- a/src/Core/ViewModels/Pages/ConnectViewModel.cs +++ b/src/Core/ViewModels/Pages/ConfigurationViewModel.cs @@ -9,9 +9,9 @@ namespace OSDPBench.Core.ViewModels.Pages; /// -/// ViewModel for the Connect page. +/// ViewModel for the Configuration page. /// -public partial class ConnectViewModel : ObservableObject, IDisposable +public partial class ConfigurationViewModel : ObservableObject, IDisposable { // Default baud rates available for connection private static readonly IReadOnlyList DefaultBaudRates = [9600, 19200, 38400, 57600, 115200, 230400]; @@ -35,9 +35,9 @@ public partial class ConnectViewModel : ObservableObject, IDisposable public Task InitializationComplete => _initializationComplete.Task; /// - /// ViewModel for the Connect page. + /// ViewModel for the Configuration page. /// - public ConnectViewModel(IDialogService dialogService, IDeviceManagementService deviceManagementService, + public ConfigurationViewModel(IDialogService dialogService, IDeviceManagementService deviceManagementService, ISerialPortConnectionService serialPortConnectionService, IUsbDeviceMonitorService? usbDeviceMonitorService = null) { _dialogService = dialogService ?? @@ -59,10 +59,7 @@ public ConnectViewModel(IDialogService dialogService, IDeviceManagementService d _usbDeviceMonitorService.StartMonitoring(); } - // Initialize connection types with localized strings - UpdateConnectionTypes(); - - // Subscribe to culture changes to update localized strings + // Subscribe to culture changes to update localized strings (for dynamic section title) Resources.Resources.PropertyChanged += OnResourcesPropertyChanged; // Perform an initial port scan @@ -71,21 +68,8 @@ public ConnectViewModel(IDialogService dialogService, IDeviceManagementService d private void OnResourcesPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { - // When culture changes, update the connection types with new localized strings - UpdateConnectionTypes(); - } - - private void UpdateConnectionTypes() - { - int previousSelectedIndex = SelectedConnectionTypeIndex; - - ConnectionTypes.Clear(); - ConnectionTypes.Add(Resources.Resources.GetString("ConnectionType_Discover")); - ConnectionTypes.Add(Resources.Resources.GetString("ConnectionType_Manual")); - ConnectionTypes.Add(Resources.Resources.GetString("ConnectionType_PassiveMonitor")); - - // Restore selection after update - SelectedConnectionTypeIndex = previousSelectedIndex; + // When culture changes, update the dynamic connection section title + OnPropertyChanged(nameof(ConnectionSectionTitle)); } private void OnDeviceManagementServiceOnTraceEntryReceived(object? sender, TraceEntry traceEntry) @@ -259,19 +243,41 @@ partial void OnSecurityKeyChanged(string value) [ObservableProperty] private string _usbStatusText = string.Empty; /// - /// Gets the available connection types (localized). + /// Gets or sets a value indicating whether Connect to PD mode is selected. /// - [ObservableProperty] private ObservableCollection _connectionTypes = []; + [ObservableProperty] private bool _isConnectToPDSelected = true; /// - /// Gets or sets the selected connection type index (0 = Discovery, 1 = Manual, 2 = Passive Monitor). + /// Gets or sets a value indicating whether Discover mode is selected (within Connect to PD). /// - [ObservableProperty] private int _selectedConnectionTypeIndex; + [ObservableProperty] private bool _isDiscoverModeSelected = true; /// /// Gets a value indicating whether Passive Monitor mode is selected. /// - public bool IsPassiveMode => SelectedConnectionTypeIndex == 2; + public bool IsPassiveMode => !IsConnectToPDSelected; + + /// + /// Gets a value indicating whether Manual mode settings should be visible. + /// + public bool IsManualModeVisible => IsConnectToPDSelected && !IsDiscoverModeSelected; + + /// + /// Gets a value indicating whether Discover mode settings should be visible. + /// + public bool IsDiscoverModeVisible => IsConnectToPDSelected && IsDiscoverModeSelected; + + /// + /// Gets a value indicating whether connection settings should be visible (Manual or Passive mode). + /// + public bool ShowConnectionSettings => !IsDiscoverModeVisible; + + /// + /// Gets the dynamic section title based on the selected connection mode. + /// + public string ConnectionSectionTitle => IsConnectToPDSelected + ? Resources.Resources.GetString("Connect_ConnectToPD") + : Resources.Resources.GetString("ConnectionType_PassiveMonitor"); /// /// Gets a value indicating whether the Connect button should be visible. @@ -315,7 +321,14 @@ partial void OnStatusLevelChanged(StatusLevel value) NotifyButtonVisibilityChanged(); } - partial void OnSelectedConnectionTypeIndexChanged(int value) + partial void OnIsConnectToPDSelectedChanged(bool value) + { + _ = value; // Intentionally unused - only triggering dependent property notification + NotifyButtonVisibilityChanged(); + OnPropertyChanged(nameof(ConnectionSectionTitle)); + } + + partial void OnIsDiscoverModeSelectedChanged(bool value) { _ = value; // Intentionally unused - only triggering dependent property notification NotifyButtonVisibilityChanged(); @@ -656,7 +669,7 @@ private bool CalculateConnectVisibility() StatusLevel == StatusLevel.Connecting || StatusLevel == StatusLevel.ConnectingManually; - return SelectedConnectionTypeIndex == 1 && !isConnected; + return IsConnectToPDSelected && !IsDiscoverModeSelected && !isConnected; } private bool CalculateDisconnectVisibility() @@ -684,7 +697,7 @@ private bool CalculateStartDiscoveryVisibility() StatusLevel == StatusLevel.Connecting || StatusLevel == StatusLevel.ConnectingManually; - return SelectedConnectionTypeIndex == 0 && + return IsConnectToPDSelected && IsDiscoverModeSelected && StatusLevel is not StatusLevel.Discovering and not StatusLevel.Discovered && !isConnectedOrConnecting; } @@ -692,7 +705,7 @@ StatusLevel is not StatusLevel.Discovering and not StatusLevel.Discovered && private bool CalculateCancelDiscoveryVisibility() { // Show Cancel button only when actively discovering - return SelectedConnectionTypeIndex == 0 && StatusLevel == StatusLevel.Discovering; + return IsConnectToPDSelected && IsDiscoverModeSelected && StatusLevel == StatusLevel.Discovering; } private bool CalculateConnectionTypeEnabled() @@ -709,7 +722,7 @@ private bool CalculateConnectionTypeEnabled() private bool CalculateStartPassiveMonitoringVisibility() { // Show Start Passive Monitoring when in Passive mode and not actively monitoring - return SelectedConnectionTypeIndex == 2 && StatusLevel != StatusLevel.PassiveMonitoring; + return !IsConnectToPDSelected && StatusLevel != StatusLevel.PassiveMonitoring; } private bool CalculateStopPassiveMonitoringVisibility() @@ -720,7 +733,7 @@ private bool CalculateStopPassiveMonitoringVisibility() /// /// Notifies UI that button visibility properties may have changed. - /// Should be called whenever StatusLevel or SelectedConnectionTypeIndex changes. + /// Should be called whenever StatusLevel or connection mode selection changes. /// private void NotifyButtonVisibilityChanged() { @@ -732,6 +745,9 @@ private void NotifyButtonVisibilityChanged() OnPropertyChanged(nameof(StopPassiveMonitoringVisible)); OnPropertyChanged(nameof(IsConnectionTypeEnabled)); OnPropertyChanged(nameof(IsPassiveMode)); + OnPropertyChanged(nameof(IsManualModeVisible)); + OnPropertyChanged(nameof(IsDiscoverModeVisible)); + OnPropertyChanged(nameof(ShowConnectionSettings)); } #endregion diff --git a/src/UI/Windows/App.xaml.cs b/src/UI/Windows/App.xaml.cs index 37d36be..860ee18 100644 --- a/src/UI/Windows/App.xaml.cs +++ b/src/UI/Windows/App.xaml.cs @@ -43,8 +43,8 @@ public partial class App services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -126,8 +126,8 @@ await Current.Dispatcher.InvokeAsync(async () => private async void OnExit(object sender, ExitEventArgs e) { // Dispose of services that need explicit cleanup - var connectViewModel = Host.Services.GetService(); - connectViewModel?.Dispose(); + var configurationViewModel = Host.Services.GetService(); + configurationViewModel?.Dispose(); var usbMonitor = Host.Services.GetService(); usbMonitor?.Dispose(); diff --git a/src/UI/Windows/Services/ApplicationHostService.cs b/src/UI/Windows/Services/ApplicationHostService.cs index faf1390..9523f12 100644 --- a/src/UI/Windows/Services/ApplicationHostService.cs +++ b/src/UI/Windows/Services/ApplicationHostService.cs @@ -42,7 +42,7 @@ private async Task HandleActivationAsync() )!; _navigationWindow.ShowWindow(); - _navigationWindow.Navigate(typeof(Views.Pages.ConnectPage)); + _navigationWindow.Navigate(typeof(Views.Pages.ConfigurationPage)); } await Task.CompletedTask; diff --git a/src/UI/Windows/Styles/ComponentStyles.xaml b/src/UI/Windows/Styles/ComponentStyles.xaml index f24c5e9..e4af3c0 100644 --- a/src/UI/Windows/Styles/ComponentStyles.xaml +++ b/src/UI/Windows/Styles/ComponentStyles.xaml @@ -220,4 +220,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/UI/Windows/Styles/LayoutTemplates.xaml b/src/UI/Windows/Styles/LayoutTemplates.xaml index d4deb6e..f985904 100644 --- a/src/UI/Windows/Styles/LayoutTemplates.xaml +++ b/src/UI/Windows/Styles/LayoutTemplates.xaml @@ -77,14 +77,10 @@ - - + + - + Style="{StaticResource Badge.Text.Error}"/> - - + + - + Style="{StaticResource Badge.Text.Error}"/> - + - + Style="{StaticResource Badge.Text}"/> - - + + - - - - - - - - - - + diff --git a/src/UI/Windows/Styles/StyleGuide.md b/src/UI/Windows/Styles/StyleGuide.md index 3098fd6..21f57bb 100644 --- a/src/UI/Windows/Styles/StyleGuide.md +++ b/src/UI/Windows/Styles/StyleGuide.md @@ -88,6 +88,48 @@ Reusable styles for UI components. ``` +#### Badges +Status badges for displaying security states and status indicators. + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +**Badge Styles:** +- `Badge.Success` / `Badge.Success.Small` - Green filled (secure/positive states) +- `Badge.Info` / `Badge.Info.Small` - Blue filled (informational states) +- `Badge.Warning` / `Badge.Warning.Small` - Orange filled (warning states) +- `Badge.Error.Outlined` / `Badge.Error.Outlined.Small` - Red outlined (error/insecure states) + +**Badge Text Styles:** +- `Badge.Text` / `Badge.Text.Small` - White text for filled badges +- `Badge.Text.Error` / `Badge.Text.Error.Small` - Red text for outlined error badges + ### 3. Layout Templates (`LayoutTemplates.xaml`) Templates for common layout patterns. diff --git a/src/UI/Windows/Views/Pages/ConnectPage.xaml b/src/UI/Windows/Views/Pages/ConfigurationPage.xaml similarity index 73% rename from src/UI/Windows/Views/Pages/ConnectPage.xaml rename to src/UI/Windows/Views/Pages/ConfigurationPage.xaml index 4c4efd9..b2dd055 100644 --- a/src/UI/Windows/Views/Pages/ConnectPage.xaml +++ b/src/UI/Windows/Views/Pages/ConfigurationPage.xaml @@ -1,22 +1,22 @@ - - + @@ -29,33 +29,54 @@ Text="{markup:Localize Connect_SerialPortSelection}" TextWrapping="Wrap" Margin="0,0,0,10"/> - - + + + + + + + + + @@ -71,10 +92,10 @@ - + @@ -141,20 +162,31 @@ - + + + + + + - + + Visibility="{Binding ViewModel.IsDiscoverModeVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/> + Visibility="{Binding ViewModel.IsPassiveMode, Converter={StaticResource BooleanToVisibilityConverter}}"/> + Visibility="{Binding ViewModel.ShowConnectionSettings, Converter={StaticResource BooleanToVisibilityConverter}}"> @@ -189,7 +221,7 @@ + Visibility="{Binding ViewModel.IsManualModeVisible, Converter={StaticResource BooleanToVisibilityConverter}}">