当因为算子参数输入不合理,或者执行异常,要在状态栏显示
This commit is contained in:
@@ -37,11 +37,10 @@ namespace XplorePlane.Services
|
|||||||
if (enabledNodes.Count == 0)
|
if (enabledNodes.Count == 0)
|
||||||
return source;
|
return source;
|
||||||
|
|
||||||
// 大图像预览缩放
|
|
||||||
var current = ScaleForPreview(source);
|
var current = ScaleForPreview(source);
|
||||||
|
|
||||||
int total = enabledNodes.Count;
|
var total = enabledNodes.Count;
|
||||||
for (int step = 0; step < total; step++)
|
for (var step = 0; step < total; step++)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -53,15 +52,14 @@ namespace XplorePlane.Services
|
|||||||
|
|
||||||
if (invalidParameters.Count > 0)
|
if (invalidParameters.Count > 0)
|
||||||
{
|
{
|
||||||
|
var invalidParameterText = string.Join("、", invalidParameters);
|
||||||
throw new PipelineExecutionException(
|
throw new PipelineExecutionException(
|
||||||
$"算子 '{node.DisplayName}' 存在无效参数:{string.Join("、", invalidParameters)}",
|
$"算子 '{node.DisplayName}' 存在无效参数:{invalidParameterText}",
|
||||||
node.Order,
|
node.Order,
|
||||||
node.OperatorKey);
|
node.OperatorKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
var parameters = node.Parameters
|
var parameters = node.Parameters.ToDictionary(p => p.Name, p => p.Value);
|
||||||
.Where(p => p.IsValueValid)
|
|
||||||
.ToDictionary(p => p.Name, p => p.Value);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -69,9 +67,12 @@ namespace XplorePlane.Services
|
|||||||
current, node.OperatorKey, parameters, null, cancellationToken);
|
current, node.OperatorKey, parameters, null, cancellationToken);
|
||||||
|
|
||||||
if (current == null)
|
if (current == null)
|
||||||
|
{
|
||||||
throw new PipelineExecutionException(
|
throw new PipelineExecutionException(
|
||||||
$"算子 '{node.OperatorKey}' 返回了空图像",
|
$"算子 '{node.DisplayName}' 返回了空图像",
|
||||||
node.Order, node.OperatorKey);
|
node.Order,
|
||||||
|
node.OperatorKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -85,7 +86,9 @@ namespace XplorePlane.Services
|
|||||||
{
|
{
|
||||||
throw new PipelineExecutionException(
|
throw new PipelineExecutionException(
|
||||||
$"算子 '{node.DisplayName}' 执行失败:{ex.Message}",
|
$"算子 '{node.DisplayName}' 执行失败:{ex.Message}",
|
||||||
node.Order, node.OperatorKey, ex);
|
node.Order,
|
||||||
|
node.OperatorKey,
|
||||||
|
ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
progress?.Report(new PipelineProgress(step + 1, total, node.DisplayName));
|
progress?.Report(new PipelineProgress(step + 1, total, node.DisplayName));
|
||||||
@@ -102,7 +105,7 @@ namespace XplorePlane.Services
|
|||||||
if (source.PixelWidth <= UhdThreshold && source.PixelHeight <= UhdThreshold)
|
if (source.PixelWidth <= UhdThreshold && source.PixelHeight <= UhdThreshold)
|
||||||
return source;
|
return source;
|
||||||
|
|
||||||
double scale = (double)PreviewMaxHeight / source.PixelHeight;
|
var scale = (double)PreviewMaxHeight / source.PixelHeight;
|
||||||
if (source.PixelWidth * scale > UhdThreshold)
|
if (source.PixelWidth * scale > UhdThreshold)
|
||||||
scale = (double)UhdThreshold / source.PixelWidth;
|
scale = (double)UhdThreshold / source.PixelWidth;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace XplorePlane.ViewModels
|
|||||||
private string _pipelineName = "新建流水线";
|
private string _pipelineName = "新建流水线";
|
||||||
private string _selectedDevice = string.Empty;
|
private string _selectedDevice = string.Empty;
|
||||||
private bool _isExecuting;
|
private bool _isExecuting;
|
||||||
|
private bool _isStatusError;
|
||||||
private string _statusMessage = string.Empty;
|
private string _statusMessage = string.Empty;
|
||||||
private string _currentFilePath;
|
private string _currentFilePath;
|
||||||
|
|
||||||
@@ -148,6 +149,12 @@ namespace XplorePlane.ViewModels
|
|||||||
set => SetProperty(ref _statusMessage, value);
|
set => SetProperty(ref _statusMessage, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsStatusError
|
||||||
|
{
|
||||||
|
get => _isStatusError;
|
||||||
|
private set => SetProperty(ref _isStatusError, value);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Commands ──────────────────────────────────────────────────
|
// ── Commands ──────────────────────────────────────────────────
|
||||||
|
|
||||||
public DelegateCommand<string> AddOperatorCommand { get; }
|
public DelegateCommand<string> AddOperatorCommand { get; }
|
||||||
@@ -329,7 +336,12 @@ namespace XplorePlane.ViewModels
|
|||||||
vm.PropertyChanged += (_, e) =>
|
vm.PropertyChanged += (_, e) =>
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(ProcessorParameterVM.Value))
|
if (e.PropertyName == nameof(ProcessorParameterVM.Value))
|
||||||
|
{
|
||||||
|
if (TryReportInvalidParameters())
|
||||||
|
return;
|
||||||
|
|
||||||
TriggerDebouncedExecution();
|
TriggerDebouncedExecution();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
node.Parameters.Add(vm);
|
node.Parameters.Add(vm);
|
||||||
}
|
}
|
||||||
@@ -339,36 +351,39 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
if (SourceImage == null || IsExecuting) return;
|
if (SourceImage == null || IsExecuting) return;
|
||||||
|
|
||||||
|
if (TryReportInvalidParameters())
|
||||||
|
return;
|
||||||
|
|
||||||
_executionCts?.Cancel();
|
_executionCts?.Cancel();
|
||||||
_executionCts = new CancellationTokenSource();
|
_executionCts = new CancellationTokenSource();
|
||||||
var token = _executionCts.Token;
|
var token = _executionCts.Token;
|
||||||
|
|
||||||
IsExecuting = true;
|
IsExecuting = true;
|
||||||
StatusMessage = "正在执行流水线...";
|
SetInfoStatus("正在执行流水线...");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var progress = new Progress<PipelineProgress>(p =>
|
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(
|
var result = await _executionService.ExecutePipelineAsync(
|
||||||
PipelineNodes, SourceImage, progress, token);
|
PipelineNodes, SourceImage, progress, token);
|
||||||
|
|
||||||
PreviewImage = result;
|
PreviewImage = result;
|
||||||
StatusMessage = "流水线执行完成";
|
SetInfoStatus("流水线执行完成");
|
||||||
PublishPipelinePreviewUpdated(result, StatusMessage);
|
PublishPipelinePreviewUpdated(result, StatusMessage);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
StatusMessage = "流水线执行已取消";
|
SetInfoStatus("流水线执行已取消");
|
||||||
}
|
}
|
||||||
catch (PipelineExecutionException ex)
|
catch (PipelineExecutionException ex)
|
||||||
{
|
{
|
||||||
StatusMessage = $"节点 '{ex.FailedOperatorKey}' 执行失败:{ex.Message}";
|
SetErrorStatus($"执行失败:{ex.Message}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
StatusMessage = $"执行错误:{ex.Message}";
|
SetErrorStatus($"执行错误:{ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
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()
|
private void LoadImage()
|
||||||
{
|
{
|
||||||
var dialog = new OpenFileDialog
|
var dialog = new OpenFileDialog
|
||||||
|
|||||||
@@ -53,8 +53,40 @@ namespace XplorePlane.ViewModels
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
var normalizedValue = NormalizeValue(value);
|
var normalizedValue = NormalizeValue(value);
|
||||||
if (SetProperty(ref _value, normalizedValue))
|
if (!Equals(_value, normalizedValue))
|
||||||
|
{
|
||||||
|
_value = normalizedValue;
|
||||||
ValidateValue(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"
|
FontFamily="Microsoft YaHei UI"
|
||||||
FontSize="11"
|
FontSize="11"
|
||||||
ItemsSource="{Binding Options}"
|
ItemsSource="{Binding Options}"
|
||||||
SelectedItem="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
SelectedItem="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||||
<ComboBox.Style>
|
<ComboBox.Style>
|
||||||
<Style TargetType="ComboBox">
|
<Style TargetType="ComboBox">
|
||||||
<Setter Property="Visibility" Value="Collapsed" />
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
FontFamily="Microsoft YaHei UI"
|
FontFamily="Microsoft YaHei UI"
|
||||||
FontSize="11"
|
FontSize="11"
|
||||||
IsChecked="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
IsChecked="{Binding BoolValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||||
<CheckBox.Style>
|
<CheckBox.Style>
|
||||||
<Style TargetType="CheckBox">
|
<Style TargetType="CheckBox">
|
||||||
<Setter Property="Visibility" Value="Collapsed" />
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
@@ -324,15 +324,35 @@
|
|||||||
<Border
|
<Border
|
||||||
Grid.Row="4"
|
Grid.Row="4"
|
||||||
Padding="6,4"
|
Padding="6,4"
|
||||||
Background="#F5F5F5"
|
|
||||||
BorderBrush="{StaticResource PanelBorder}"
|
|
||||||
BorderThickness="0,1,0,0">
|
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
|
<TextBlock
|
||||||
FontFamily="{StaticResource CsdFont}"
|
FontFamily="{StaticResource CsdFont}"
|
||||||
FontSize="11"
|
FontSize="11"
|
||||||
Foreground="#555"
|
|
||||||
Text="{Binding StatusMessage, StringFormat='Status: {0}'}"
|
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>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
Reference in New Issue
Block a user