diff --git a/Sinvo.EplanHpD.Plugin.WPFUI/View/ScannerWindow.xaml b/Sinvo.EplanHpD.Plugin.WPFUI/View/ScannerWindow.xaml
new file mode 100644
index 0000000..2197be0
--- /dev/null
+++ b/Sinvo.EplanHpD.Plugin.WPFUI/View/ScannerWindow.xaml
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sinvo.EplanHpD.Plugin.WPFUI/View/ScannerWindow.xaml.cs b/Sinvo.EplanHpD.Plugin.WPFUI/View/ScannerWindow.xaml.cs
new file mode 100644
index 0000000..d56d15d
--- /dev/null
+++ b/Sinvo.EplanHpD.Plugin.WPFUI/View/ScannerWindow.xaml.cs
@@ -0,0 +1,110 @@
+using CommunityToolkit.Mvvm.Messaging;
+using EPLAN.Harness.API;
+using EPLAN.Harness.Common.Extensions;
+using EPLAN.Harness.Core;
+using EPLAN.Harness.Core.Controls;
+using EPLAN.Harness.Core.Settings;
+using EPLAN.Harness.IO;
+using EPLAN.Harness.PlatformCore.P8Objects;
+using EPLAN.Harness.ProjectCore;
+using EPLAN.Harness.ProjectCore.Occurrences;
+using EPLAN.Harness.ProjectCore.Occurrences.Designer;
+using EPLAN.Harness.ProjectCore.Occurrences.Nailboard;
+using HandyControl.Controls;
+using Microsoft.Win32;
+using Sinvo.EplanHpD.Plugin.WPFUI.Common;
+using Sinvo.EplanHpD.Plugin.WPFUI.Models;
+using Sinvo.EplanHpD.Plugin.WPFUI.Utils;
+using Sinvo.EplanHpD.Plugin.WPFUI.ViewModel;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+
+namespace Sinvo.EplanHpD.Plugin.WPFUI.View
+{
+ ///
+ /// ScannerWindow.xaml 的交互逻辑
+ ///
+ public partial class ScannerWindow : System.Windows.Window
+ {
+ private ScannerViewModel _viewModel;
+
+ public ScannerWindow()
+ {
+ InitializeComponent();
+ _viewModel = new ScannerViewModel();
+ this.DataContext = _viewModel;
+ }
+
+ private void TopMostWindow_Checked(object sender, RoutedEventArgs e)
+ {
+ this.Topmost = true;
+ }
+
+ private void TopMostWindow_Unchecked(object sender, RoutedEventArgs e)
+ {
+ this.Topmost = false;
+ }
+
+ private void ShowAllWire_Click(object sender, RoutedEventArgs e)
+ {
+ _viewModel.OthersOccShow(true);
+ }
+
+ private void GetDoc_Click(object sender, RoutedEventArgs e)
+ {
+ _viewModel.GetCurrentDoc();
+ _viewModel.GetAllCables();
+ }
+
+
+ private void ExportScanedListBtn_Click(object sender, RoutedEventArgs e)
+ {
+ SaveFileDialog saveFileDialog = new()
+ {
+ Filter = "MS Excel (*.xlsx)|*.xlsx",//Singleton.Instance["Report_ExpFilter", "Report_ExpFilter"],
+ FilterIndex = 1,
+ //FileName = $"{单芯线下单}{DateTime.Now:yyyy_MM_dd}.xlsx",
+ FileName = $"线材扫描记录_{DateTime.Now:yyyy-MM-dd_hF}.xlsx",
+ AddExtension = true,
+ InitialDirectory = StudioSettings.Instance.ReportExport_Path,
+ CheckPathExists = true
+ };
+ if (saveFileDialog.ShowDialog() == true)
+ {
+ _viewModel.ExportWires(System.IO.Path.Combine(FlexIOBase.GetParentPath(saveFileDialog.FileName), saveFileDialog.FileName));
+ //ExcelHelper.SaveByTemplate(ViewModel.ExportData, Path.Combine(FlexIOBase.GetParentPath(saveFileDialog.FileName), saveFileDialog.FileName));
+ FlexMessageBox.Info($"导出完成!");
+ }
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ _viewModel.GetCurrentDoc();
+ _viewModel.GetAllCables();
+ WeakReferenceMessenger.Default.Register(sender, "ScanedIndexChanged", (r, m) =>
+ {
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ CableList.ScrollIntoView(CableList.SelectedItem);
+ }));
+ }
+ );
+ }
+ }
+}
diff --git a/Sinvo.EplanHpD.Plugin.WPFUI/ViewModel/ScannerViewModel.cs b/Sinvo.EplanHpD.Plugin.WPFUI/ViewModel/ScannerViewModel.cs
new file mode 100644
index 0000000..b5110e5
--- /dev/null
+++ b/Sinvo.EplanHpD.Plugin.WPFUI/ViewModel/ScannerViewModel.cs
@@ -0,0 +1,330 @@
+using CommunityToolkit.Mvvm.Messaging;
+using EPLAN.Harness.API;
+using EPLAN.Harness.AppCore.StudioCommon.TreeView;
+using EPLAN.Harness.Core.Controls;
+using EPLAN.Harness.Primitives.Treeviews;
+using EPLAN.Harness.ProjectCore;
+using EPLAN.Harness.ProjectCore.Occurrences.Designer;
+using Sinvo.EplanHpD.Plugin.WPFUI.Common;
+using Sinvo.EplanHpD.Plugin.WPFUI.Models;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Security;
+using System.Security.Permissions;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Threading;
+
+namespace Sinvo.EplanHpD.Plugin.WPFUI.ViewModel
+{
+ public class ScannerViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
+ {
+ private FlexDesigner _currentFlexDesigner;
+ private bool _othersWireShow = true;
+ private bool _continueScan = true;
+ private bool _enableEvent = true;
+ private bool _isIncludeAnnotation = true;
+ private bool _scanAndAssembled = true;
+ private int _scanedIndex = 1;
+
+ public int ScanedIndex
+ {
+ get => _scanedIndex;
+ set
+ {
+ _scanedIndex = value;
+ OnPropertyChanged(nameof(ScanedIndex));
+ WeakReferenceMessenger.Default.Send(new CommonMessage(value), "ScanedIndexChanged");
+ }
+ }
+ private string _inputCode;
+ public string InputCode
+ {
+ get => _inputCode;
+ set
+ {
+ if (_inputCode != value)
+ {
+ _inputCode = value;
+ OnPropertyChanged(nameof(InputCode));
+ if (_enableEvent)
+ {
+ if (_debounceTimer == null)
+ {
+ _debounceTimer = new DispatcherTimer();
+ _debounceTimer.Interval = TimeSpan.FromMilliseconds(1000);
+ _debounceTimer.Tick += DebounceTimer_Tick;
+ }
+ _debounceTimer.Stop();
+ _debounceTimer.Start();
+ }
+ }
+ }
+ }
+
+ public ObservableCollection ScanCableModels { get; set; } = [];
+
+ private DispatcherTimer _debounceTimer;
+
+ public ScannerViewModel()
+ {
+
+ }
+
+
+ public void GetAllCables()
+ {
+ ScanCableModels.Clear();
+ _currentFlexDesigner.GetOrganizerOccurrences().ToList().ForEach(occ =>
+ {
+ if (occ is OccWire wire)
+ {
+ if (wire.Name.StartsWith("w_", StringComparison.OrdinalIgnoreCase))
+ {
+ ScanCableModels.Add(new ScanCableModel()
+ {
+ Index = ScanCableModels.Count + 1,
+ Name = wire.LibraryName,
+ Code = wire.Name,
+ Imprint = wire.Imprint
+ });
+ }
+ }
+ });
+ }
+
+ public void GetCurrentDoc()
+ {
+ try
+ {
+ HpdApi api = HpdApi.GetInstance();
+ var currentDocId = api.CurrentProject.GetActiveDocument()?.ID;
+ var designer = SelfControler.FindInstance(currentDocId) as FlexDesigner;
+ if (designer != null)
+ {
+ _currentFlexDesigner = designer;
+ }
+ else
+ {
+ FlexMessageBox.Warning("未找到当前打开的工作区,请先打开工作区");
+ }
+ }
+ catch (Exception ex)
+ {
+ FlexMessageBox.Error(ex.Message);
+ }
+ }
+
+ private void DebounceTimer_Tick(object sender, EventArgs e)
+ {
+ _debounceTimer.Stop();
+ string searchText = InputCode;
+ PerformSearch(searchText);
+ }
+
+ private void PerformSearch(string searchText)
+ {
+ // 清除错误信息
+ ClearErrors(nameof(InputCode));
+ var cable = ScanCableModels.FirstOrDefault(c => c.Code.Equals(searchText, StringComparison.OrdinalIgnoreCase));
+ if (cable != null)
+ {
+
+ OthersOccShow(!OthersWireShow);
+ if (_continueScan)
+ {
+ _enableEvent = false;
+ InputCode = string.Empty;
+ _enableEvent = true;
+ }
+ if (ScanAndAssembled)
+ {
+ cable.IsChecked = true;
+ }
+ ScanedIndex = Math.Max(cable.Index - 1, 0);
+ ToSourceByName(searchText);
+
+ }
+ else
+ {
+ // 添加错误信息
+ AddError(nameof(InputCode), "该条码不存在于列表中");
+ }
+ }
+
+ private void ToSourceByName(string wireName)
+ {
+ var occWire = _currentFlexDesigner.GetOccurrenceByName(wireName, typeof(OccWire));
+ if (occWire != null)
+ {
+ occWire.SetVisibility(true, null);
+ _currentFlexDesigner.SelectSet.Clear();
+ _currentFlexDesigner.SelectSet.Add(occWire);
+ _currentFlexDesigner.FitToSelectSet();
+ _currentFlexDesigner.SelectSet.OnSelectionChanged();
+
+ //_currentFlexDesigner.Camera.GraphicControl._HighlightNode()
+ /*
+ List> nodeByEntity = this._occTreeView.GetNodeByEntity(baseOccurrence);
+ if (nodeByEntity != null)
+ {
+ foreach (DatTreeNodeEx datTreeNodeEx in nodeByEntity)
+ {
+ if (!this._occTreeView.SelectedNodes.Contains(datTreeNodeEx))
+ {
+ this._occTreeView.SelectedNodes.Add(datTreeNodeEx);
+ }
+ }
+ }
+ */
+ }
+ }
+
+ public void OthersOccShow(bool show = false)
+ {
+ _currentFlexDesigner.GetOrganizerOccurrences().ToList().ForEach(occ =>
+ {
+ if (occ is OccWire wire)
+ {
+ wire.SetVisibility(show, null);
+ }
+ if (occ is OccCableForked cable)
+ {
+ cable.SetVisibility(show, null);
+ }
+ if (_isIncludeAnnotation)
+ {
+ if (occ is OccAnnotation annotation)
+ {
+ Debug.WriteLine($"BaseOccurrence -> {annotation.Name} Type: {annotation.GetType().Name}");
+ annotation.SetVisibility(show, null);
+ }
+ }
+ });
+ if (show)
+ {
+ _currentFlexDesigner.FitToSelectSet();
+ }
+ }
+
+ // 添加属性以绑定界面上的控件,例如复选框等
+ public bool OthersWireShow
+ {
+ get => _othersWireShow;
+ set
+ {
+ if (_othersWireShow != value)
+ {
+ _othersWireShow = value;
+ OnPropertyChanged(nameof(OthersWireShow));
+ }
+ }
+ }
+
+ public bool ContinueScan
+ {
+ get => _continueScan;
+ set
+ {
+ if (_continueScan != value)
+ {
+ _continueScan = value;
+ OnPropertyChanged(nameof(ContinueScan));
+ }
+ }
+ }
+
+ public bool IsIncludeAnnotation
+ {
+ get => _isIncludeAnnotation;
+ set
+ {
+ if (_isIncludeAnnotation != value)
+ {
+ _isIncludeAnnotation = value;
+ OnPropertyChanged(nameof(IsIncludeAnnotation));
+ }
+ }
+ }
+ public bool ScanAndAssembled
+ {
+ get => _scanAndAssembled;
+ set
+ {
+ if (_scanAndAssembled != value)
+ {
+ _scanAndAssembled = value;
+ OnPropertyChanged(nameof(ScanAndAssembled));
+ }
+ }
+ }
+
+ public void ExportWires(string path)
+ {
+ MiniExcelLibs.MiniExcel.SaveAs(path, ScanCableModels.Select(it => new
+ {
+ 序号 = it.Index,
+ 导线名称 = it.Code,
+ 导线型号 = it.Name,
+ 导线是否已扫描 = it.IsChecked ? "是" : "否"
+ }));
+
+ }
+ #region Notify
+ // 实现 INotifyPropertyChanged 接口
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ // 实现 INotifyDataErrorInfo 接口
+ private readonly Dictionary> _errors = new Dictionary>();
+
+ public bool HasErrors => _errors.Any();
+
+ public event EventHandler ErrorsChanged;
+
+ public IEnumerable GetErrors(string propertyName)
+ {
+ if (string.IsNullOrEmpty(propertyName))
+ return null;
+
+ _errors.TryGetValue(propertyName, out var errorsForName);
+ return errorsForName;
+ }
+
+ protected void OnErrorsChanged(string propertyName)
+ {
+ ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
+ }
+
+ private void AddError(string propertyName, string error)
+ {
+ if (!_errors.ContainsKey(propertyName))
+ {
+ _errors[propertyName] = new List();
+ }
+
+ if (!_errors[propertyName].Contains(error))
+ {
+ _errors[propertyName].Add(error);
+ OnErrorsChanged(propertyName);
+ }
+ }
+
+ private void ClearErrors(string propertyName)
+ {
+ if (_errors.ContainsKey(propertyName))
+ {
+ _errors.Remove(propertyName);
+ OnErrorsChanged(propertyName);
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Sinvo.EplanHpD.Plugin/ScanPluginEntry.cs b/Sinvo.EplanHpD.Plugin/ScanPluginEntry.cs
new file mode 100644
index 0000000..abc71ab
--- /dev/null
+++ b/Sinvo.EplanHpD.Plugin/ScanPluginEntry.cs
@@ -0,0 +1,75 @@
+using EPLAN.Harness.API;
+using EPLAN.Harness.API.Plugins.Core;
+using EPLAN.Harness.AppCore;
+using Sinvo.EplanHpD.Plugin.WPFUI.Utils;
+using Sinvo.EplanHpD.Plugin.WPFUI.View;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms.Integration;
+
+namespace Sinvo.EplanHpD.Plugin
+{
+ public class ScanPluginEntry : EPLAN.Harness.API.Plugins.IHpDPlugin
+ {
+ public string Name => "兴禾ProD插件-3D-扫描";
+
+ public string Author => "Sinvo";
+
+ public string Description => "兴禾EPLAN Harness proD 2.9 Studio插件,提供扫描定位功能";
+
+ ///
+ /// 获取DLL版本
+ ///
+ public Version Version => Assembly.GetAssembly(this.GetType()).GetName().Version;
+
+ public System.Drawing.Image ToolbarIcon
+ {
+ get
+ {
+ var imageBytes = Resource.Sinvo;
+ if (imageBytes != null)
+ {
+ using (var ms = new MemoryStream(imageBytes))
+ {
+ return Image.FromStream(ms);
+ }
+ }
+ return null;
+ }
+ }
+
+ public string ToolbarText => "扫描器";
+
+ public HpDModule Module => HpDModule.WorkSpace;
+
+ public string ToolbarTooltip => "扫描器";
+
+ public void Execute(HpdApi api)
+ {
+ //var doc = api.CurrentProject.GetActiveDocument();
+ var window = new ScannerWindow();
+ ElementHost.EnableModelessKeyboardInterop(window);
+ var mainApp = BaseApp.ActiveApplication;
+ var helper = new System.Windows.Interop.WindowInteropHelper(window);
+ helper.Owner = mainApp.Handle;
+ window.Show();
+ }
+
+ public void Initialize()
+ {
+ AppDomainDllLoader.SetLaoder();
+ ApplicationExt.InitApplication();
+ }
+
+ public void Terminate()
+ {
+ //throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Sinvo.EplanHpD.Plugin/Sinvo.EplanHpD.Plugin.csproj b/Sinvo.EplanHpD.Plugin/Sinvo.EplanHpD.Plugin.csproj
index acbda1b..a8d00b3 100644
--- a/Sinvo.EplanHpD.Plugin/Sinvo.EplanHpD.Plugin.csproj
+++ b/Sinvo.EplanHpD.Plugin/Sinvo.EplanHpD.Plugin.csproj
@@ -120,6 +120,7 @@
True
Resource.resx
+