当因为算子参数输入不合理,或者执行异常,要在状态栏显示

This commit is contained in:
zhengxuan.zhang
2026-04-23 17:04:41 +08:00
parent 338358a71c
commit 5ae5963353
4 changed files with 124 additions and 24 deletions
@@ -37,11 +37,10 @@ namespace XplorePlane.Services
if (enabledNodes.Count == 0)
return source;
// 大图像预览缩放
var current = ScaleForPreview(source);
int total = enabledNodes.Count;
for (int step = 0; step < total; step++)
var total = enabledNodes.Count;
for (var step = 0; step < total; step++)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -53,15 +52,14 @@ namespace XplorePlane.Services
if (invalidParameters.Count > 0)
{
var invalidParameterText = string.Join("、", invalidParameters);
throw new PipelineExecutionException(
$"算子 '{node.DisplayName}' 存在无效参数:{string.Join("", invalidParameters)}",
$"算子 '{node.DisplayName}' 存在无效参数:{invalidParameterText}",
node.Order,
node.OperatorKey);
}
var parameters = node.Parameters
.Where(p => p.IsValueValid)
.ToDictionary(p => p.Name, p => p.Value);
var parameters = node.Parameters.ToDictionary(p => p.Name, p => p.Value);
try
{
@@ -69,9 +67,12 @@ namespace XplorePlane.Services
current, node.OperatorKey, parameters, null, cancellationToken);
if (current == null)
{
throw new PipelineExecutionException(
$"算子 '{node.OperatorKey}' 返回了空图像",
node.Order, node.OperatorKey);
$"算子 '{node.DisplayName}' 返回了空图像",
node.Order,
node.OperatorKey);
}
}
catch (OperationCanceledException)
{
@@ -85,7 +86,9 @@ namespace XplorePlane.Services
{
throw new PipelineExecutionException(
$"算子 '{node.DisplayName}' 执行失败:{ex.Message}",
node.Order, node.OperatorKey, ex);
node.Order,
node.OperatorKey,
ex);
}
progress?.Report(new PipelineProgress(step + 1, total, node.DisplayName));
@@ -102,7 +105,7 @@ namespace XplorePlane.Services
if (source.PixelWidth <= UhdThreshold && source.PixelHeight <= UhdThreshold)
return source;
double scale = (double)PreviewMaxHeight / source.PixelHeight;
var scale = (double)PreviewMaxHeight / source.PixelHeight;
if (source.PixelWidth * scale > UhdThreshold)
scale = (double)UhdThreshold / source.PixelWidth;
@@ -34,6 +34,7 @@ namespace XplorePlane.ViewModels
private string _pipelineName = "新建流水线";
private string _selectedDevice = string.Empty;
private bool _isExecuting;
private bool _isStatusError;
private string _statusMessage = string.Empty;
private string _currentFilePath;
@@ -148,6 +149,12 @@ namespace XplorePlane.ViewModels
set => SetProperty(ref _statusMessage, value);
}
public bool IsStatusError
{
get => _isStatusError;
private set => SetProperty(ref _isStatusError, value);
}
// ── Commands ──────────────────────────────────────────────────
public DelegateCommand<string> AddOperatorCommand { get; }
@@ -329,7 +336,12 @@ namespace XplorePlane.ViewModels
vm.PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(ProcessorParameterVM.Value))
{
if (TryReportInvalidParameters())
return;
TriggerDebouncedExecution();
}
};
node.Parameters.Add(vm);
}
@@ -339,36 +351,39 @@ namespace XplorePlane.ViewModels
{
if (SourceImage == null || IsExecuting) return;
if (TryReportInvalidParameters())
return;
_executionCts?.Cancel();
_executionCts = new CancellationTokenSource();
var token = _executionCts.Token;
IsExecuting = true;
StatusMessage = "正在执行流水线...";
SetInfoStatus("正在执行流水线...");
try
{
var progress = new Progress<PipelineProgress>(p =>
StatusMessage = $"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})");
SetInfoStatus($"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})"));
var result = await _executionService.ExecutePipelineAsync(
PipelineNodes, SourceImage, progress, token);
PreviewImage = result;
StatusMessage = "流水线执行完成";
SetInfoStatus("流水线执行完成");
PublishPipelinePreviewUpdated(result, StatusMessage);
}
catch (OperationCanceledException)
{
StatusMessage = "流水线执行已取消";
SetInfoStatus("流水线执行已取消");
}
catch (PipelineExecutionException ex)
{
StatusMessage = $"节点 '{ex.FailedOperatorKey}' 执行失败:{ex.Message}";
SetErrorStatus($"执行失败:{ex.Message}");
}
catch (Exception ex)
{
StatusMessage = $"执行错误:{ex.Message}";
SetErrorStatus($"执行错误:{ex.Message}");
}
finally
{
@@ -376,6 +391,36 @@ namespace XplorePlane.ViewModels
}
}
private bool TryReportInvalidParameters()
{
var firstInvalidNode = PipelineNodes
.Where(n => n.IsEnabled)
.OrderBy(n => n.Order)
.FirstOrDefault(n => n.Parameters.Any(p => !p.IsValueValid));
if (firstInvalidNode == null)
return false;
var invalidNames = firstInvalidNode.Parameters
.Where(p => !p.IsValueValid)
.Select(p => p.DisplayName);
SetErrorStatus($"参数错误:算子 '{firstInvalidNode.DisplayName}' 的 {string.Join("", invalidNames)} 输入不合理,请修正后重试。");
return true;
}
private void SetInfoStatus(string message)
{
IsStatusError = false;
StatusMessage = message;
}
private void SetErrorStatus(string message)
{
IsStatusError = true;
StatusMessage = message;
PublishPipelinePreviewUpdated(PreviewImage ?? SourceImage, message);
}
private void LoadImage()
{
var dialog = new OpenFileDialog
@@ -53,8 +53,40 @@ namespace XplorePlane.ViewModels
set
{
var normalizedValue = NormalizeValue(value);
if (SetProperty(ref _value, normalizedValue))
if (!Equals(_value, normalizedValue))
{
_value = normalizedValue;
ValidateValue(normalizedValue);
RaisePropertyChanged(nameof(Value));
RaisePropertyChanged(nameof(BoolValue));
RaisePropertyChanged(nameof(SelectedOption));
}
}
}
public bool BoolValue
{
get => ParameterType == "bool" && TryConvertToBool(_value, out var boolValue) && boolValue;
set
{
if (ParameterType == "bool")
{
Value = value;
}
}
}
public string SelectedOption
{
get => HasOptions
? Convert.ToString(_value, CultureInfo.InvariantCulture) ?? string.Empty
: string.Empty;
set
{
if (HasOptions)
{
Value = value;
}
}
}
@@ -285,7 +285,7 @@
FontFamily="Microsoft YaHei UI"
FontSize="11"
ItemsSource="{Binding Options}"
SelectedItem="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
SelectedItem="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Collapsed" />
@@ -302,7 +302,7 @@
VerticalAlignment="Center"
FontFamily="Microsoft YaHei UI"
FontSize="11"
IsChecked="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
IsChecked="{Binding BoolValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Collapsed" />
@@ -324,15 +324,35 @@
<Border
Grid.Row="4"
Padding="6,4"
Background="#F5F5F5"
BorderBrush="{StaticResource PanelBorder}"
BorderThickness="0,1,0,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F5F5F5" />
<Setter Property="BorderBrush" Value="{StaticResource PanelBorder}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsStatusError}" Value="True">
<Setter Property="Background" Value="#FFF1F1" />
<Setter Property="BorderBrush" Value="#D9534F" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
FontFamily="{StaticResource CsdFont}"
FontSize="11"
Foreground="#555"
Text="{Binding StatusMessage, StringFormat='Status: {0}'}"
TextTrimming="CharacterEllipsis" />
TextTrimming="CharacterEllipsis">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#555" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsStatusError}" Value="True">
<Setter Property="Foreground" Value="#A12A2A" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</Grid>
</Border>