当因为算子参数输入不合理,或者执行异常,要在状态栏显示
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user