Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ namespace ServiceControl.Config.Commands
{
using System;
using System.Threading.Tasks;
using Caliburn.Micro;
using Events;
using Framework;
using Framework.Commands;
using ServiceControlInstaller.Engine.Instances;
Expand All @@ -10,20 +12,22 @@ namespace ServiceControl.Config.Commands

class AdvancedMonitoringOptionsCommand : AwaitableAbstractCommand<InstanceDetailsViewModel>
{
public AdvancedMonitoringOptionsCommand(IServiceControlWindowManager windowManager, Func<BaseService, MonitoringAdvancedViewModel> advancedOptionsModel)
public AdvancedMonitoringOptionsCommand(IServiceControlWindowManager windowManager, IEventAggregator eventAggregator, Func<BaseService, MonitoringAdvancedViewModel> advancedOptionsModel)
{
this.windowManager = windowManager;
this.eventAggregator = eventAggregator;
this.advancedOptionsModel = advancedOptionsModel;
}

public override Task ExecuteAsync(InstanceDetailsViewModel viewModel)
public override async Task ExecuteAsync(InstanceDetailsViewModel viewModel)
{
var screen = advancedOptionsModel(viewModel.MonitoringInstance);

return windowManager.ShowInnerDialog(screen);
await windowManager.ShowInnerDialog(screen);
await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances());
}

readonly Func<BaseService, MonitoringAdvancedViewModel> advancedOptionsModel;
readonly IEventAggregator eventAggregator;
readonly IServiceControlWindowManager windowManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ namespace ServiceControl.Config.Commands
{
using System;
using System.Threading.Tasks;
using Caliburn.Micro;
using Events;
using Framework;
using Framework.Commands;
using ServiceControlInstaller.Engine.Instances;
Expand All @@ -10,16 +12,18 @@ namespace ServiceControl.Config.Commands

class AdvancedServiceControlOptionsCommand : AwaitableAbstractCommand<InstanceDetailsViewModel>
{
public AdvancedServiceControlOptionsCommand(IServiceControlWindowManager windowManager, Func<BaseService, ServiceControlAdvancedViewModel> advancedOptionsModel)
public AdvancedServiceControlOptionsCommand(IServiceControlWindowManager windowManager, IEventAggregator eventAggregator, Func<BaseService, ServiceControlAdvancedViewModel> advancedOptionsModel)
{
this.windowManager = windowManager;
this.eventAggregator = eventAggregator;
this.advancedOptionsModel = advancedOptionsModel;
}

public override Task ExecuteAsync(InstanceDetailsViewModel viewModel)
public override async Task ExecuteAsync(InstanceDetailsViewModel viewModel)
{
var screen = CreateAdvancedScreen(viewModel);
return windowManager.ShowInnerDialog(screen);
await windowManager.ShowInnerDialog(screen);
await eventAggregator.PublishOnUIThreadAsync(new RefreshInstances());
}

ServiceControlAdvancedViewModel CreateAdvancedScreen(InstanceDetailsViewModel viewModel)
Expand All @@ -34,6 +38,7 @@ ServiceControlAdvancedViewModel CreateAdvancedScreen(InstanceDetailsViewModel vi
}

readonly Func<BaseService, ServiceControlAdvancedViewModel> advancedOptionsModel;
readonly IEventAggregator eventAggregator;
readonly IServiceControlWindowManager windowManager;
}
}
24 changes: 24 additions & 0 deletions src/ServiceControl.Config/Styles/Icons.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,30 @@
</Viewbox>
</ControlTemplate>

<ControlTemplate x:Key="CopyIcon">
<Viewbox Stretch="Uniform">
<Canvas Width="16" Height="16">
<!-- back page -->
<Rectangle Canvas.Left="4" Canvas.Top="2" Width="9" Height="11"
Fill="{TemplateBinding Foreground}" />
<Rectangle Canvas.Left="5" Canvas.Top="3" Width="7" Height="9"
Fill="White" />
<!-- front page -->
<Rectangle Canvas.Left="2" Canvas.Top="4" Width="9" Height="11"
Fill="{TemplateBinding Foreground}" />
<Rectangle Canvas.Left="3" Canvas.Top="5" Width="7" Height="9"
Fill="White" />
<!-- lines on front page -->
<Rectangle Canvas.Left="4" Canvas.Top="7" Width="5" Height="1"
Fill="{TemplateBinding Foreground}" />
<Rectangle Canvas.Left="4" Canvas.Top="9" Width="5" Height="1"
Fill="{TemplateBinding Foreground}" />
<Rectangle Canvas.Left="4" Canvas.Top="11" Width="3" Height="1"
Fill="{TemplateBinding Foreground}" />
</Canvas>
</Viewbox>
</ControlTemplate>

<ControlTemplate x:Key="RemoveIcon">
<Viewbox Stretch="Uniform">
<Canvas Width="24" Height="24">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,53 @@
Text="{Binding InstanceTypeDisplayName}" />

</StackPanel>

<!-- Configuration Error Display -->
<StackPanel Grid.Row="3" Grid.ColumnSpan="2" Margin="0 0 0 10"
Visibility="{Binding HasConfigurationError, Converter={StaticResource boolToVis}}">
<Border Background="#FFF4E5" BorderBrush="#FFA500" BorderThickness="1" Padding="10" CornerRadius="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="⚠" FontSize="16" Foreground="#FFA500" VerticalAlignment="Top" Margin="0,0,8,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="CONFIGURATION ERROR"
FontWeight="Bold"
Foreground="#D84315"
FontSize="13"
Margin="0,0,0,5"/>
<TextBlock Text="{Binding ConfigurationErrorMessage}"
TextWrapping="Wrap"
Foreground="#5D4037"
FontSize="12"/>
</StackPanel>
<Button Grid.Column="2" Padding="0" Background="Transparent" BorderThickness="0"
VerticalAlignment="Top" Margin="8,0,0,0"
Command="{Binding CopyToClipboard}" CommandParameter="{Binding ConfigurationErrorMessage}"
ToolTip="Copy error message" Cursor="Hand">
<ContentControl Template="{StaticResource CopyIcon}" Foreground="#5D4037" Width="14" Height="14" />
</Button>
</Grid>
</Border>
</StackPanel>

<StackPanel Grid.Row="2" Grid.Column="1" Margin="0 0 0 10">
<StackPanel Orientation="Horizontal">
<!-- Show warning icon for configuration errors -->
<TextBlock Margin="0,0,3,0"
VerticalAlignment="Center"
FontSize="16px"
Foreground="#FFA500"
Text="⚠"
Visibility="{Binding HasConfigurationError, Converter={StaticResource boolToVis}}" />

<ContentControl Margin="0,0,0,0"
VerticalAlignment="Center"
Template="{StaticResource RunningIcon}"
Visibility="{Binding IsRunning,
Converter={StaticResource boolToVis}}" />
VerticalAlignment="Center"
Template="{StaticResource RunningIcon}"
Visibility="{Binding IsRunning, Converter={StaticResource boolToVis}}" />

<ContentControl Margin="0,0,0,0"
VerticalAlignment="Center"
Expand All @@ -75,8 +115,19 @@
<TextBlock Margin="3,0,0,0"
VerticalAlignment="Center"
FontSize="13px"
Foreground="{StaticResource Gray40Brush}"
Text="{Binding Status}" />
Text="{Binding Status}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource Gray40Brush}" />
<Style.Triggers>
<DataTrigger Binding="{Binding HasConfigurationError}" Value="True">
<Setter Property="Foreground" Value="#D84315" />
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

<TextBlock Margin="3,0,0,0"
VerticalAlignment="Center"
Expand Down Expand Up @@ -107,12 +158,14 @@
Command="{Binding EditCommand}"
CommandParameter="{Binding DataContext,
ElementName=root}"
Style="{StaticResource ConfigurationButton}" />
Style="{StaticResource ConfigurationButton}"
Visibility="{Binding AllowEdit, Converter={StaticResource boolToVis}}" />
<Button Margin="5,0"
Command="{Binding AdvancedOptionsCommand}"
CommandParameter="{Binding DataContext,
ElementName=root}"
Style="{StaticResource AdvancedOptionsButton}" />
Style="{StaticResource AdvancedOptionsButton}"
Visibility="{Binding AllowEdit, Converter={StaticResource boolToVis}}" />
</StackPanel>

<GroupBox Grid.Row="4" Header="VERSION" Margin="0 5 0 0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ public InstanceDetailsViewModel(
StartCommand = Command.Create(() => StartService());
StopCommand = Command.Create(() => StopService());

ServiceInstance = instance;

if (instance.GetType() == typeof(ServiceControlInstance))
{
ServiceControlInstance = (ServiceControlInstance)instance;
Expand Down Expand Up @@ -72,7 +70,37 @@ public InstanceDetailsViewModel(
throw new Exception("Unknown instance type");
}

public BaseService ServiceInstance { get; }
public void UpdateServiceInstance(BaseService updatedInstance)
{
if (updatedInstance.Name != ServiceInstance.Name)
{
throw new ArgumentException("Cannot update with an instance of a different name");
}

if (updatedInstance.GetType() != ServiceInstance.GetType())
{
throw new ArgumentException("Cannot update with an instance of a different type");
}

// Update the internal reference based on type
if (updatedInstance.GetType() == typeof(ServiceControlInstance))
{
ServiceControlInstance = (ServiceControlInstance)updatedInstance;
}
else if (updatedInstance.GetType() == typeof(MonitoringInstance))
{
MonitoringInstance = (MonitoringInstance)updatedInstance;
}
else if (updatedInstance.GetType() == typeof(ServiceControlAuditInstance))
{
ServiceControlAuditInstance = (ServiceControlAuditInstance)updatedInstance;
}
}

public BaseService ServiceInstance =>
(BaseService)ServiceControlInstance ??
(BaseService)MonitoringInstance ??
ServiceControlAuditInstance;

public bool InMaintenanceMode =>
ServiceControlInstance?.InMaintenanceMode == true ||
Expand Down Expand Up @@ -165,20 +193,25 @@ string GetDBPathIfAvailable()

public bool HasNewVersion => Version < NewVersion;

public TransportInfo Transport => ((ITransportConfig)ServiceInstance).TransportPackage;
public TransportInfo Transport => HasConfigurationError ? null : ((ITransportConfig)ServiceInstance).TransportPackage;

public string Persister
{
get
{
if (HasConfigurationError)
{
return string.Empty; // Leave blank for corrupt instances
}

if (ServiceInstance is IServiceControlInstance primaryInstance)
{
return primaryInstance.PersistenceManifest.DisplayName;
return primaryInstance.PersistenceManifest?.DisplayName ?? "Unknown";
}

if (ServiceInstance is IServiceControlAuditInstance auditInstance)
{
return auditInstance.PersistenceManifest.DisplayName;
return auditInstance.PersistenceManifest?.DisplayName ?? "Unknown";
}

if (ServiceInstance is IMonitoringInstance)
Expand All @@ -194,6 +227,12 @@ public string Status
{
get
{
// If there's a configuration error, show that instead of service status
if (HasConfigurationError)
{
return "ERROR";
}

try
{
return ServiceInstance.Service.Status.ToString().ToUpperInvariant();
Expand All @@ -209,6 +248,12 @@ public bool IsRunning
{
get
{
// If there's a configuration error, don't show running icon
if (HasConfigurationError)
{
return false;
}

try
{
return ServiceInstance.Service.Status != ServiceControllerStatus.Stopped;
Expand All @@ -224,6 +269,12 @@ public bool IsStopped
{
get
{
// If there's a configuration error, don't show stopped icon either
if (HasConfigurationError)
{
return false;
}

try
{
return ServiceInstance.Service.Status == ServiceControllerStatus.Stopped;
Expand All @@ -239,6 +290,12 @@ public bool AllowStart
{
get
{
// Don't allow start for instances with configuration errors
if (HasConfigurationError)
{
return false;
}

try
{
var dontAllowStartOn = new[]
Expand All @@ -260,6 +317,12 @@ public bool AllowStop
{
get
{
// Don't allow stop for instances with configuration errors
if (HasConfigurationError)
{
return false;
}

try
{
var dontAllowStopOn = new[]
Expand All @@ -277,6 +340,12 @@ public bool AllowStop
}
}

public bool HasConfigurationError => !string.IsNullOrEmpty(ServiceInstance?.ConfigurationLoadError);

public bool AllowEdit => !HasConfigurationError; // Disable edit for corrupt instances

public string ConfigurationErrorMessage => ServiceInstance?.ConfigurationLoadError;

public ICommand OpenUrl { get; private set; }

public ICommand CopyToClipboard { get; private set; }
Expand Down Expand Up @@ -308,6 +377,9 @@ public Task HandleAsync(PostRefreshInstances message, CancellationToken cancella
NotifyOfPropertyChange("Transport");
NotifyOfPropertyChange("BrowsableUrl");
NotifyOfPropertyChange("UrlHeading");
NotifyOfPropertyChange(nameof(HasConfigurationError));
NotifyOfPropertyChange(nameof(AllowEdit));
NotifyOfPropertyChange(nameof(ConfigurationErrorMessage));
return Task.CompletedTask;
}

Expand Down Expand Up @@ -376,7 +448,20 @@ await Task.Run(() =>

void UpdateServiceProperties()
{
ServiceInstance.Reload();
try
{
ServiceInstance.Reload();
}
catch (Exception ex)
{
// Handle reload failure gracefully - configuration error will be shown in UI
ServiceInstance.ConfigurationLoadError = $"Failed to load configuration file '{ServiceInstance.ConfigurationFilePath}': {ex.Message}";
// Ensure basic properties are set so UI can still display the instance
if (string.IsNullOrEmpty(ServiceInstance.InstanceName))
{
ServiceInstance.InstanceName = ServiceInstance.Name;
}
}

NotifyOfPropertyChange("Status");
NotifyOfPropertyChange("AllowStop");
Expand Down
Loading
Loading