105040 Add 扫码器插件

This commit is contained in:
lihanbo 2024-12-02 09:28:54 +08:00
parent 6818840420
commit 4ffa6d478a
5 changed files with 692 additions and 0 deletions

View File

@ -0,0 +1,176 @@
<Window
x:Class="Sinvo.EplanHpD.Plugin.WPFUI.View.ScannerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:local="clr-namespace:Sinvo.EplanHpD.Plugin.WPFUI.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:Sinvo.EplanHpD.Plugin.WPFUI.ViewModel"
Title="扫描"
Width="340"
Height="450"
MinWidth="340"
d:DataContext="{d:DesignInstance Type=viewmodel:ScannerViewModel}"
Left="1580"
Loaded="Window_Loaded"
Top="590"
WindowStartupLocation="Manual"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Sinvo.EplanHpD.Plugin.WPFUI;component/Themes/Theme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="220" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WrapPanel
Grid.Row="0"
MaxWidth="320"
HorizontalAlignment="Left">
<hc:TextBox
x:Name="ScanCodeTextBox"
Width="300"
Height="80"
Margin="10"
VerticalContentAlignment="Center"
hc:InfoElement.Placeholder="请扫描条码,例: w_00012"
hc:TitleElement.Title="扫描条码:"
FontSize="16">
<hc:TextBox.Text>
<Binding
Path="InputCode"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnNotifyDataErrors="True" />
</hc:TextBox.Text>
</hc:TextBox>
<WrapPanel Margin="10">
<CheckBox
x:Name="HideOthersWire"
Margin="3"
Content="自动隐藏其他线"
FontSize="16"
IsChecked="{Binding OthersWireShow}" />
<CheckBox
x:Name="ContinueScan"
Margin="3"
Content="连续扫描"
FontSize="16"
IsChecked="{Binding ContinueScan}" />
<!--<CheckBox Content="窗口透明" FontSize="16" />-->
<CheckBox
x:Name="TopMostWindow"
Margin="3"
Checked="TopMostWindow_Checked"
Content="窗口置顶"
FontSize="16"
IsChecked="True"
Unchecked="TopMostWindow_Unchecked" />
<CheckBox
Margin="3"
Content="扫描即装配"
FontSize="16"
IsChecked="{Binding ScanAndAssembled}" />
<CheckBox
Margin="3"
Content="隐藏或显示时同时包括注释"
FontSize="16"
IsChecked="{Binding IsIncludeAnnotation}" />
<Button
x:Name="ShowAllWire"
Margin="3"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Click="ShowAllWire_Click"
Content="显示所有线" />
<Button
x:Name="GetDoc"
Margin="3"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Click="GetDoc_Click"
Content="刷新打开的项目" />
</WrapPanel>
</WrapPanel>
<TabControl Grid.Row="1">
<TabItem Header="线材列表">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<DataGrid
x:Name="CableList"
AutoGenerateColumns="False"
ColumnHeaderHeight="20"
EnableColumnVirtualization="False"
EnableRowVirtualization="False"
HeadersVisibility="Column"
IsReadOnly="True"
ItemsSource="{Binding ScanCableModels}"
SelectedIndex="{Binding ScanedIndex}"
VirtualizingPanel.IsVirtualizing="False">
<DataGrid.Resources>
<Style BasedOn="{StaticResource DataGridCellStyle}" TargetType="DataGridCell">
<Setter Property="Height" Value="Auto" />
<Setter Property="MaxHeight" Value="9999" />
<Setter Property="Margin" Value="0" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style BasedOn="{StaticResource DataGridRowStyle}" TargetType="DataGridRow">
<Setter Property="Foreground" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="false">
<Setter Property="Background" Value="#dc4d41" />
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked}" Value="true">
<Setter Property="Background" Value="#18a05d" />
</DataTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="#33326cf3" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn
Width="50"
Binding="{Binding Index}"
Header="序号" />
<DataGridTextColumn
Width="80"
Binding="{Binding Code}"
Header="线材编号" />
<DataGridTextColumn
Width="120"
hc:Poptip.Content="{Binding Name}"
hc:Poptip.HitMode="Hover"
Binding="{Binding Name}"
Header="线材名称" />
<DataGridTextColumn Binding="{Binding Imprint}" Header="印记" />
<!--<DataGridTextColumn Binding="{Binding IsChecked}" Header="线材名称" />-->
</DataGrid.Columns>
</DataGrid>
<Button
Grid.Row="1"
Click="ExportScanedListBtn_Click"
Content="导出线材列表" />
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@ -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
{
/// <summary>
/// ScannerWindow.xaml 的交互逻辑
/// </summary>
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<LRS>.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<CommonMessage, string>(sender, "ScanedIndexChanged", (r, m) =>
{
Dispatcher.BeginInvoke(new Action(() =>
{
CableList.ScrollIntoView(CableList.SelectedItem);
}));
}
);
}
}
}

View File

@ -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<ScanCableModel> 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<FlexBaseOrganizer>.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<DatTreeNodeEx<BaseOccurrence>> nodeByEntity = this._occTreeView.GetNodeByEntity(baseOccurrence);
if (nodeByEntity != null)
{
foreach (DatTreeNodeEx<BaseOccurrence> 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<string, List<string>> _errors = new Dictionary<string, List<string>>();
public bool HasErrors => _errors.Any();
public event EventHandler<DataErrorsChangedEventArgs> 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<string>();
}
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
}
}

View File

@ -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插件,提供扫描定位功能";
/// <summary>
/// 获取DLL版本
/// </summary>
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();
}
}
}

View File

@ -120,6 +120,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Include="ScanPluginEntry.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx">