补充依赖

This commit is contained in:
zhengxuan.zhang
2026-04-22 16:33:38 +08:00
parent 077a7215f0
commit 1794ba9051
2 changed files with 431 additions and 0 deletions
@@ -0,0 +1,401 @@
using Microsoft.Win32;
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Windows;
using System.Windows.Input;
using XplorePlane.Models;
using XplorePlane.Services;
using XP.Common.Logging.Interfaces;
namespace XplorePlane.ViewModels.Cnc
{
public class CncInspectionModulePipelineViewModel : BindableBase, IPipelineEditorHostViewModel
{
private readonly CncEditorViewModel _editorViewModel;
private readonly IImageProcessingService _imageProcessingService;
private readonly IPipelinePersistenceService _persistenceService;
private readonly ILoggerService _logger;
private CncNodeViewModel _activeModuleNode;
private PipelineNodeViewModel _selectedNode;
private string _statusMessage = "请选择检测模块以编辑其流水线。";
private string _currentFilePath;
private bool _isSynchronizing;
public CncInspectionModulePipelineViewModel(
CncEditorViewModel editorViewModel,
IImageProcessingService imageProcessingService,
IPipelinePersistenceService persistenceService,
ILoggerService logger)
{
_editorViewModel = editorViewModel ?? throw new ArgumentNullException(nameof(editorViewModel));
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<CncInspectionModulePipelineViewModel>();
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
AddOperatorCommand = new DelegateCommand<string>(AddOperator, _ => HasActiveModule);
RemoveOperatorCommand = new DelegateCommand<PipelineNodeViewModel>(RemoveOperator);
MoveNodeUpCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeUp);
MoveNodeDownCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeDown);
NewPipelineCommand = new DelegateCommand(NewPipeline);
SavePipelineCommand = new DelegateCommand(SavePipelineToModule);
SaveAsPipelineCommand = new DelegateCommand(async () => await SaveAsPipelineAsync());
LoadPipelineCommand = new DelegateCommand(async () => await LoadPipelineAsync());
_editorViewModel.PropertyChanged += OnEditorPropertyChanged;
RefreshFromSelection();
}
public ObservableCollection<PipelineNodeViewModel> PipelineNodes { get; }
public PipelineNodeViewModel SelectedNode
{
get => _selectedNode;
set => SetProperty(ref _selectedNode, value);
}
public string StatusMessage
{
get => _statusMessage;
private set => SetProperty(ref _statusMessage, value);
}
public bool HasActiveModule => _activeModuleNode?.IsInspectionModule == true;
public Visibility EditorVisibility => HasActiveModule ? Visibility.Visible : Visibility.Collapsed;
public Visibility EmptyStateVisibility => HasActiveModule ? Visibility.Collapsed : Visibility.Visible;
public ICommand AddOperatorCommand { get; }
public ICommand RemoveOperatorCommand { get; }
public ICommand MoveNodeUpCommand { get; }
public ICommand MoveNodeDownCommand { get; }
public ICommand NewPipelineCommand { get; }
public ICommand SavePipelineCommand { get; }
public ICommand SaveAsPipelineCommand { get; }
public ICommand LoadPipelineCommand { get; }
private void OnEditorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(CncEditorViewModel.SelectedNode))
{
RefreshFromSelection();
}
}
private void RefreshFromSelection()
{
var selected = _editorViewModel.SelectedNode;
if (selected == null || !selected.IsInspectionModule)
{
_activeModuleNode = null;
PipelineNodes.Clear();
SelectedNode = null;
StatusMessage = "请选择检测模块以编辑其流水线。";
RaiseModuleVisibilityChanged();
RaiseCommandCanExecuteChanged();
return;
}
_activeModuleNode = selected;
LoadPipelineModel(_activeModuleNode.Pipeline ?? new PipelineModel
{
Name = _activeModuleNode.Name
});
RaiseModuleVisibilityChanged();
RaiseCommandCanExecuteChanged();
}
private void AddOperator(string operatorKey)
{
if (!HasActiveModule || string.IsNullOrWhiteSpace(operatorKey))
return;
var available = _imageProcessingService.GetAvailableProcessors();
if (!available.Contains(operatorKey))
{
StatusMessage = $"算子 '{operatorKey}' 未注册。";
return;
}
var displayName = _imageProcessingService.GetProcessorDisplayName(operatorKey) ?? operatorKey;
var icon = ProcessorUiMetadata.GetOperatorIcon(operatorKey);
var node = new PipelineNodeViewModel(operatorKey, displayName, icon)
{
Order = PipelineNodes.Count
};
LoadNodeParameters(node, null);
PipelineNodes.Add(node);
SelectedNode = node;
PersistActiveModule($"已添加算子:{displayName}");
}
private void RemoveOperator(PipelineNodeViewModel node)
{
if (!HasActiveModule || node == null || !PipelineNodes.Contains(node))
return;
PipelineNodes.Remove(node);
RenumberNodes();
if (SelectedNode == node)
{
SelectedNode = PipelineNodes.LastOrDefault();
}
PersistActiveModule($"已移除算子:{node.DisplayName}");
}
private void MoveNodeUp(PipelineNodeViewModel node)
{
if (!HasActiveModule || node == null)
return;
var index = PipelineNodes.IndexOf(node);
if (index <= 0)
return;
PipelineNodes.Move(index, index - 1);
RenumberNodes();
PersistActiveModule($"已上移算子:{node.DisplayName}");
}
private void MoveNodeDown(PipelineNodeViewModel node)
{
if (!HasActiveModule || node == null)
return;
var index = PipelineNodes.IndexOf(node);
if (index < 0 || index >= PipelineNodes.Count - 1)
return;
PipelineNodes.Move(index, index + 1);
RenumberNodes();
PersistActiveModule($"已下移算子:{node.DisplayName}");
}
private void NewPipeline()
{
if (!HasActiveModule)
return;
PipelineNodes.Clear();
SelectedNode = null;
_currentFilePath = null;
PersistActiveModule("已为当前检测模块新建空流水线。");
}
private void SavePipelineToModule()
{
if (!HasActiveModule)
return;
PersistActiveModule("当前检测模块流水线已同步到 CNC 程序。");
}
private async System.Threading.Tasks.Task SaveAsPipelineAsync()
{
if (!HasActiveModule)
return;
var dialog = new SaveFileDialog
{
Filter = "图像处理流水线 (*.imw)|*.imw",
FileName = GetActivePipelineName()
};
if (dialog.ShowDialog() != true)
return;
var model = BuildPipelineModel();
await _persistenceService.SaveAsync(model, dialog.FileName);
_currentFilePath = dialog.FileName;
StatusMessage = $"已导出模块流水线:{Path.GetFileName(dialog.FileName)}";
}
private async System.Threading.Tasks.Task LoadPipelineAsync()
{
if (!HasActiveModule)
return;
var dialog = new OpenFileDialog
{
Filter = "图像处理流水线 (*.imw)|*.imw"
};
if (dialog.ShowDialog() != true)
return;
var model = await _persistenceService.LoadAsync(dialog.FileName);
_currentFilePath = dialog.FileName;
LoadPipelineModel(model);
PersistActiveModule($"已加载模块流水线:{model.Name}");
}
private void LoadPipelineModel(PipelineModel pipeline)
{
_isSynchronizing = true;
try
{
PipelineNodes.Clear();
SelectedNode = null;
var orderedNodes = (pipeline?.Nodes ?? new List<PipelineNodeModel>())
.OrderBy(node => node.Order)
.ToList();
foreach (var nodeModel in orderedNodes)
{
var displayName = _imageProcessingService.GetProcessorDisplayName(nodeModel.OperatorKey) ?? nodeModel.OperatorKey;
var icon = ProcessorUiMetadata.GetOperatorIcon(nodeModel.OperatorKey);
var node = new PipelineNodeViewModel(nodeModel.OperatorKey, displayName, icon)
{
Order = nodeModel.Order,
IsEnabled = nodeModel.IsEnabled
};
LoadNodeParameters(node, nodeModel.Parameters);
PipelineNodes.Add(node);
}
SelectedNode = PipelineNodes.FirstOrDefault();
StatusMessage = HasActiveModule
? $"正在编辑检测模块:{_activeModuleNode.Name}"
: "请选择检测模块以编辑其流水线。";
}
finally
{
_isSynchronizing = false;
}
}
private void LoadNodeParameters(PipelineNodeViewModel node, IDictionary<string, object> savedValues)
{
var parameterDefinitions = _imageProcessingService.GetProcessorParameters(node.OperatorKey);
if (parameterDefinitions == null)
return;
node.Parameters.Clear();
foreach (var definition in parameterDefinitions)
{
var parameterVm = new ProcessorParameterVM(definition);
if (savedValues != null && savedValues.TryGetValue(definition.Name, out var savedValue))
{
parameterVm.Value = ConvertSavedValue(savedValue, definition.ValueType);
}
parameterVm.PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(ProcessorParameterVM.Value) && !_isSynchronizing)
{
PersistActiveModule($"已更新参数:{parameterVm.DisplayName}");
}
};
node.Parameters.Add(parameterVm);
}
}
private void PersistActiveModule(string statusMessage)
{
if (!HasActiveModule || _isSynchronizing)
return;
_activeModuleNode.Pipeline = BuildPipelineModel();
StatusMessage = statusMessage;
}
private PipelineModel BuildPipelineModel()
{
return new PipelineModel
{
Id = _activeModuleNode?.Pipeline?.Id ?? Guid.NewGuid(),
Name = GetActivePipelineName(),
DeviceId = _activeModuleNode?.Pipeline?.DeviceId ?? string.Empty,
CreatedAt = _activeModuleNode?.Pipeline?.CreatedAt ?? DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
Nodes = PipelineNodes.Select((node, index) => new PipelineNodeModel
{
Id = node.Id,
OperatorKey = node.OperatorKey,
Order = index,
IsEnabled = node.IsEnabled,
Parameters = node.Parameters.ToDictionary(parameter => parameter.Name, parameter => parameter.Value)
}).ToList()
};
}
private string GetActivePipelineName()
{
if (!HasActiveModule)
return "InspectionModulePipeline";
return string.IsNullOrWhiteSpace(_activeModuleNode.Pipeline?.Name)
? _activeModuleNode.Name
: _activeModuleNode.Pipeline.Name;
}
private void RenumberNodes()
{
for (var i = 0; i < PipelineNodes.Count; i++)
{
PipelineNodes[i].Order = i;
}
}
private void RaiseModuleVisibilityChanged()
{
RaisePropertyChanged(nameof(HasActiveModule));
RaisePropertyChanged(nameof(EditorVisibility));
RaisePropertyChanged(nameof(EmptyStateVisibility));
}
private void RaiseCommandCanExecuteChanged()
{
(AddOperatorCommand as DelegateCommand<string>)?.RaiseCanExecuteChanged();
}
private static object ConvertSavedValue(object savedValue, Type targetType)
{
if (savedValue is not JsonElement jsonElement)
return savedValue;
try
{
if (targetType == typeof(int))
return jsonElement.GetInt32();
if (targetType == typeof(double))
return jsonElement.GetDouble();
if (targetType == typeof(bool))
return jsonElement.GetBoolean();
if (targetType == typeof(string))
return jsonElement.GetString() ?? string.Empty;
return jsonElement.ToString();
}
catch
{
return jsonElement.ToString();
}
}
}
}
@@ -0,0 +1,30 @@
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace XplorePlane.ViewModels
{
public interface IPipelineEditorHostViewModel
{
ObservableCollection<PipelineNodeViewModel> PipelineNodes { get; }
PipelineNodeViewModel SelectedNode { get; set; }
string StatusMessage { get; }
ICommand AddOperatorCommand { get; }
ICommand RemoveOperatorCommand { get; }
ICommand MoveNodeUpCommand { get; }
ICommand MoveNodeDownCommand { get; }
ICommand NewPipelineCommand { get; }
ICommand SavePipelineCommand { get; }
ICommand SaveAsPipelineCommand { get; }
ICommand LoadPipelineCommand { get; }
}
}