本次提交主要内容如下: - 新增“排序功能”菜单,支持重排刀序、生成/应用排序种子、按参考钻带重排等多种排序方式,提升刀具顺序管理灵活性。 - 支持用户选择参考钻带文件或排序种子文件,自动重排当前刀具顺序,并提供详细的匹配校验、警告提示和重排前后对比确认。 - 新增生成通用排序种子文件(General_sort.txt)功能,便于批量产品排序。 - 优化界面布局,提升信息展示美观性和空间利用率。 - 增加异常处理和详细注释,提升健壮性和可维护性。 - 新增多个文档,详细说明“使用指定钻带的刀序”功能的实现、使用方法、演示流程及开发过程中的问题修复,便于开发和用户理解。 - 新增两个排序种子文件示例(General_sort.txt、s40024079g0-a2-cs-jp-sort.txt),用于刀具顺序自动重排。 - 其他无实际代码变更的文件未影响功能。 本次改动极大提升了钻带刀具顺序管理的自动化、灵活性和用户体验,适用于多样化的生产场景。
2373 lines
96 KiB
C#
2373 lines
96 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.ComponentModel;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Windows;
|
||
using DrillTools.Integration;
|
||
using System.Diagnostics;
|
||
|
||
namespace DrillTools
|
||
{
|
||
/// <summary>
|
||
/// 主窗口视图模型
|
||
/// </summary>
|
||
public class MainWindowViewModel : INotifyPropertyChanged
|
||
{
|
||
private ObservableCollection<ToolItem> _tools = new();
|
||
private ObservableCollection<ToolItem> _originalTools = new(); // 保存原始刀具顺序
|
||
private ToolItem? _selectedTool;
|
||
private string _drillTapeContent = string.Empty;
|
||
private bool _canMoveUp;
|
||
private bool _canMoveDown;
|
||
private string _originalFilePath = string.Empty;
|
||
private bool _isTopmost = false; // 默认不顶置
|
||
private string _fileNameWithoutExtension = string.Empty;
|
||
private double _minDrillDiameter;
|
||
private double _minSlotDiameter;
|
||
private double _minEADiameter;
|
||
|
||
/// <summary>
|
||
/// 刀具列表
|
||
/// </summary>
|
||
public ObservableCollection<ToolItem> Tools
|
||
{
|
||
get => _tools;
|
||
set => SetProperty(ref _tools, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的刀具
|
||
/// </summary>
|
||
public ToolItem? SelectedTool
|
||
{
|
||
get => _selectedTool;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedTool, value))
|
||
{
|
||
UpdateMoveButtonsState();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 钻带内容
|
||
/// </summary>
|
||
public string DrillTapeContent
|
||
{
|
||
get => _drillTapeContent;
|
||
set => SetProperty(ref _drillTapeContent, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以上移
|
||
/// </summary>
|
||
public bool CanMoveUp
|
||
{
|
||
get => _canMoveUp;
|
||
set => SetProperty(ref _canMoveUp, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可以下移
|
||
/// </summary>
|
||
public bool CanMoveDown
|
||
{
|
||
get => _canMoveDown;
|
||
set => SetProperty(ref _canMoveDown, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 原始文件路径
|
||
/// </summary>
|
||
public string OriginalFilePath
|
||
{
|
||
get => _originalFilePath;
|
||
set
|
||
{
|
||
if (SetProperty(ref _originalFilePath, value))
|
||
{
|
||
// 当原始文件路径改变时,通知 HasOriginalFile 属性也已更改
|
||
OnPropertyChanged(nameof(HasOriginalFile));
|
||
|
||
// 更新文件名(不包含后缀)
|
||
if (!string.IsNullOrEmpty(value))
|
||
{
|
||
FileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(value);
|
||
}
|
||
else
|
||
{
|
||
FileNameWithoutExtension = string.Empty;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 原始刀具顺序集合(按文件中的出现顺序)
|
||
/// </summary>
|
||
public ObservableCollection<ToolItem> OriginalTools
|
||
{
|
||
get => _originalTools;
|
||
set => SetProperty(ref _originalTools, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否有原始文件
|
||
/// </summary>
|
||
public bool HasOriginalFile => !string.IsNullOrEmpty(OriginalFilePath);
|
||
|
||
/// <summary>
|
||
/// 窗口是否置顶
|
||
/// </summary>
|
||
public bool IsTopmost
|
||
{
|
||
get => _isTopmost;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isTopmost, value))
|
||
{
|
||
// 当IsTopmost改变时,通知TopmostButtonText属性也已更改
|
||
OnPropertyChanged(nameof(TopmostButtonText));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 置顶按钮显示文本
|
||
/// </summary>
|
||
public string TopmostButtonText => IsTopmost ? "取消置顶" : "窗口置顶";
|
||
|
||
/// <summary>
|
||
/// 文件名(不包含后缀)
|
||
/// </summary>
|
||
public string FileNameWithoutExtension
|
||
{
|
||
get => _fileNameWithoutExtension;
|
||
set => SetProperty(ref _fileNameWithoutExtension, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 最小钻咀直径
|
||
/// </summary>
|
||
public double MinDrillDiameter
|
||
{
|
||
get => _minDrillDiameter;
|
||
set => SetProperty(ref _minDrillDiameter, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 最小槽刀直径
|
||
/// </summary>
|
||
public double MinSlotDiameter
|
||
{
|
||
get => _minSlotDiameter;
|
||
set => SetProperty(ref _minSlotDiameter, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 最小EA刀直径
|
||
/// </summary>
|
||
public double MinEADiameter
|
||
{
|
||
get => _minEADiameter;
|
||
set => SetProperty(ref _minEADiameter, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从钻带内容加载刀具信息
|
||
/// </summary>
|
||
/// <param name="drillTapeContent">钻带内容</param>
|
||
public void LoadToolsFromDrillTape(string drillTapeContent)
|
||
{
|
||
DrillTapeContent = drillTapeContent;
|
||
Tools.Clear();
|
||
OriginalTools.Clear(); // 清空原始刀具顺序
|
||
|
||
try
|
||
{
|
||
// 使用现有的 DrillTapeProcessor 处理钻带数据
|
||
var result = DrillTapeProcessor.ProcessDrillTape(drillTapeContent);
|
||
|
||
if (result.Success)
|
||
{
|
||
foreach (var toolResult in result.ToolResults)
|
||
{
|
||
var toolItem = new ToolItem
|
||
{
|
||
ToolNumber = toolResult.ToolNumber,
|
||
Diameter = toolResult.Diameter,
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = toolResult.RegularHoles;
|
||
// SlotHoles = toolResult.SlotHoles;
|
||
// TotalHoles = toolResult.TotalHoles;
|
||
ToolType = toolResult.ToolType,
|
||
ToolSuffixType = toolResult.ToolSuffixType,
|
||
ToolCategory = toolResult.ToolCategory,
|
||
HoleLocations = toolResult.Locations ?? new List<string>()
|
||
};
|
||
|
||
// 如果是机台码,使用DrillTapeProcessor已经解析出的机台码信息
|
||
if (toolResult.ToolType == ToolType.MachineCode)
|
||
{
|
||
toolItem.MachineCodeCommand = toolResult.MachineCodeCommand;
|
||
toolItem.MachineCodeType = toolResult.MachineCodeType;
|
||
|
||
// 添加日志验证机台码信息传递
|
||
System.Diagnostics.Debug.WriteLine($"[机台码传递] T{toolResult.ToolNumber:D2}: 命令={toolItem.MachineCodeCommand}, 类型={toolItem.MachineCodeType}");
|
||
|
||
// 如果 toolResult.Locations 已经包含坐标数据,则使用它
|
||
// 否则从原始钻带内容中提取坐标行
|
||
if (toolItem.HoleLocations == null || toolItem.HoleLocations.Count == 0)
|
||
{
|
||
// 从钻带内容中提取机台码坐标行
|
||
var toolPattern = $@"%.+?T{toolResult.ToolNumber:D2}(.*?)(?=T\d{{2}}|M30)";
|
||
var match = System.Text.RegularExpressions.Regex.Match(DrillTapeContent, toolPattern, System.Text.RegularExpressions.RegexOptions.Singleline);
|
||
|
||
if (match.Success)
|
||
{
|
||
string holeSection = match.Groups[1].Value;
|
||
|
||
// 按行分割孔位数据,保持原始顺序
|
||
var lines = holeSection.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
toolItem.HoleLocations = new List<string>();
|
||
|
||
// 按原始顺序处理坐标行
|
||
foreach (var line in lines)
|
||
{
|
||
var trimmedLine = line.Trim();
|
||
if (string.IsNullOrEmpty(trimmedLine)) continue;
|
||
|
||
// 检查是否是坐标行
|
||
var coordinatePattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)";
|
||
var coordinateMatch = System.Text.RegularExpressions.Regex.Match(trimmedLine, coordinatePattern);
|
||
|
||
if (coordinateMatch.Success)
|
||
{
|
||
toolItem.HoleLocations.Add(coordinateMatch.Value);
|
||
}
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[机台码坐标] T{toolResult.ToolNumber:D2}: 找到{toolItem.HoleLocations.Count}个坐标");
|
||
}
|
||
}
|
||
}
|
||
|
||
Tools.Add(toolItem);
|
||
|
||
// 同时添加到原始刀具顺序集合中
|
||
var originalToolItem = new ToolItem
|
||
{
|
||
ToolNumber = toolResult.ToolNumber,
|
||
Diameter = toolResult.Diameter,
|
||
ToolType = toolResult.ToolType,
|
||
ToolSuffixType = toolResult.ToolSuffixType,
|
||
ToolCategory = toolResult.ToolCategory,
|
||
HoleLocations = toolResult.Locations ?? new List<string>(),
|
||
MachineCodeCommand = toolResult.MachineCodeCommand,
|
||
MachineCodeType = toolResult.MachineCodeType
|
||
};
|
||
OriginalTools.Add(originalToolItem);
|
||
}
|
||
|
||
// 更新按钮状态
|
||
UpdateMoveButtonsState();
|
||
|
||
// 计算并更新最小直径信息
|
||
UpdateMinDiameterInfo();
|
||
|
||
// 检查并应用-sort.txt文件中的刀具排序
|
||
if (!string.IsNullOrEmpty(OriginalFilePath))
|
||
{
|
||
CheckAndApplySortFile(OriginalFilePath, Tools);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
System.Windows.MessageBox.Show($"解析钻带失败: {result.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show($"解析钻带时发生异常: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重新排序刀具
|
||
/// </summary>
|
||
/// <param name="oldIndex">原索引</param>
|
||
/// <param name="newIndex">新索引</param>
|
||
public void ReorderTools(int oldIndex, int newIndex)
|
||
{
|
||
if (oldIndex < 0 || oldIndex >= Tools.Count || newIndex < 0 || newIndex >= Tools.Count)
|
||
return;
|
||
|
||
var tool = Tools[oldIndex];
|
||
|
||
// 检查是否是机台码刀具,如果是则不允许移动
|
||
if (tool.ToolType == ToolType.MachineCode)
|
||
{
|
||
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 检查目标位置是否是机台码刀具
|
||
var targetTool = Tools[newIndex];
|
||
if (targetTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
Tools.RemoveAt(oldIndex);
|
||
Tools.Insert(newIndex, tool);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上移选中的刀具
|
||
/// </summary>
|
||
public void MoveSelectedToolUp()
|
||
{
|
||
if (SelectedTool == null || !CanMoveUp)
|
||
return;
|
||
|
||
int currentIndex = Tools.IndexOf(SelectedTool);
|
||
System.Diagnostics.Debug.WriteLine($"[MoveUp] 开始上移操作,选中项索引: {currentIndex}, 刀具编号: T{SelectedTool.ToolNumber:D2}");
|
||
|
||
if (currentIndex > 0)
|
||
{
|
||
// 检查是否是机台码刀具
|
||
if (SelectedTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 检查目标位置是否是机台码刀具
|
||
var targetTool = Tools[currentIndex - 1];
|
||
if (targetTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 保存选中的刀具引用
|
||
var selectedTool = SelectedTool;
|
||
int newIndex = currentIndex - 1;
|
||
|
||
// 执行上移操作
|
||
Tools.RemoveAt(currentIndex);
|
||
Tools.Insert(newIndex, selectedTool);
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[MoveUp] 上移完成,新索引: {newIndex}");
|
||
|
||
// 使用延迟方法重新选中移动后的项
|
||
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[MoveUp] 延迟设置选中项,刀具编号: T{selectedTool.ToolNumber:D2}");
|
||
SelectedTool = selectedTool;
|
||
UpdateMoveButtonsState();
|
||
}), System.Windows.Threading.DispatcherPriority.Background);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下移选中的刀具
|
||
/// </summary>
|
||
public void MoveSelectedToolDown()
|
||
{
|
||
if (SelectedTool == null || !CanMoveDown)
|
||
return;
|
||
|
||
int currentIndex = Tools.IndexOf(SelectedTool);
|
||
System.Diagnostics.Debug.WriteLine($"[MoveDown] 开始下移操作,选中项索引: {currentIndex}, 刀具编号: T{SelectedTool.ToolNumber:D2}");
|
||
|
||
if (currentIndex >= 0 && currentIndex < Tools.Count - 1)
|
||
{
|
||
// 检查是否是机台码刀具
|
||
if (SelectedTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 检查目标位置是否是机台码刀具
|
||
var targetTool = Tools[currentIndex + 1];
|
||
if (targetTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 保存选中的刀具引用
|
||
var selectedTool = SelectedTool;
|
||
int newIndex = currentIndex + 1;
|
||
|
||
// 执行下移操作
|
||
Tools.RemoveAt(currentIndex);
|
||
Tools.Insert(newIndex, selectedTool);
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[MoveDown] 下移完成,新索引: {newIndex}");
|
||
|
||
// 使用延迟方法重新选中移动后的项
|
||
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[MoveDown] 延迟设置选中项,刀具编号: T{selectedTool.ToolNumber:D2}");
|
||
SelectedTool = selectedTool;
|
||
UpdateMoveButtonsState();
|
||
}), System.Windows.Threading.DispatcherPriority.Background);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新上移/下移按钮的状态
|
||
/// </summary>
|
||
private void UpdateMoveButtonsState()
|
||
{
|
||
if (SelectedTool == null)
|
||
{
|
||
CanMoveUp = false;
|
||
CanMoveDown = false;
|
||
System.Diagnostics.Debug.WriteLine("[UpdateButtons] 没有选中项,禁用所有按钮");
|
||
return;
|
||
}
|
||
|
||
int index = Tools.IndexOf(SelectedTool);
|
||
CanMoveUp = index > 0;
|
||
CanMoveDown = index >= 0 && index < Tools.Count - 1;
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[UpdateButtons] 选中项索引: {index}, CanMoveUp: {CanMoveUp}, CanMoveDown: {CanMoveDown}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存刀具顺序并应用到钻带处理
|
||
/// </summary>
|
||
/// <returns>重新排序后的钻带内容</returns>
|
||
public string ApplyToolOrderToDrillTape()
|
||
{
|
||
if (string.IsNullOrEmpty(OriginalFilePath))
|
||
{
|
||
throw new InvalidOperationException("没有原始文件路径,请先加载钻带文件");
|
||
}
|
||
|
||
if (Tools.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("没有刀具数据,请先加载钻带文件");
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 使用现有的扩展方法生成重新排序后的钻带内容
|
||
string reorderedDrillTape = DrillTapeProcessorExtensions.GenerateReorderedDrillTape(DrillTapeContent, Tools.ToList());
|
||
|
||
// 2. 改进的备份逻辑
|
||
if (File.Exists(OriginalFilePath))
|
||
{
|
||
string backupFilePath = OriginalFilePath + ".bak";
|
||
|
||
// 检查备份文件是否已存在
|
||
if (File.Exists(backupFilePath))
|
||
{
|
||
var result = System.Windows.MessageBox.Show(
|
||
"备份文件已存在,请选择处理方式:\n\n" +
|
||
"是(Y):覆盖现有备份文件\n" +
|
||
"否(N):创建带时间戳的新备份文件\n" +
|
||
"取消:中止保存操作\n\n" +
|
||
"提示:选择'否'可以保留之前的备份历史",
|
||
"备份选项",
|
||
MessageBoxButton.YesNoCancel,
|
||
MessageBoxImage.Question);
|
||
|
||
switch (result)
|
||
{
|
||
case MessageBoxResult.Yes:
|
||
// 覆盖现有备份文件
|
||
File.Copy(OriginalFilePath, backupFilePath, true);
|
||
break;
|
||
|
||
case MessageBoxResult.No:
|
||
// 创建带时间戳的新备份文件
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
string timestampBackupPath = $"{OriginalFilePath}.{timestamp}.bak";
|
||
File.Copy(OriginalFilePath, timestampBackupPath);
|
||
break;
|
||
|
||
case MessageBoxResult.Cancel:
|
||
// 用户取消操作
|
||
throw new OperationCanceledException("用户取消了保存操作");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 备份文件不存在,直接创建
|
||
File.Copy(OriginalFilePath, backupFilePath);
|
||
}
|
||
}
|
||
|
||
// 3. 将重新排序后的钻带内容写入原始文件,使用ANSI编码
|
||
Encoding ansiEncoding;
|
||
try
|
||
{
|
||
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
|
||
}
|
||
catch
|
||
{
|
||
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
|
||
}
|
||
|
||
// 使用StreamWriter写入文件,确保编码正确应用
|
||
using (var writer = new StreamWriter(OriginalFilePath, false, ansiEncoding))
|
||
{
|
||
writer.Write(reorderedDrillTape);
|
||
writer.Flush(); // 确保所有数据都被写入
|
||
}
|
||
|
||
// 4. 更新当前钻带内容
|
||
DrillTapeContent = reorderedDrillTape;
|
||
|
||
// 5. 生成刀具直径列表文件
|
||
GenerateToolDiameterListFile(OriginalFilePath, Tools);
|
||
|
||
// 6. 打开文件资源管理器并选中文件
|
||
OpenFileExplorerAndSelectFile(OriginalFilePath);
|
||
|
||
return reorderedDrillTape;
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
// 重新抛出取消异常,不包装
|
||
throw;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new InvalidOperationException($"保存钻带文件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重排刀序并重新编号
|
||
/// </summary>
|
||
/// <param name="isApply">是否直接应用到文件</param>
|
||
/// <param name="skipConfirmation">是否跳过确认窗口</param>
|
||
/// <returns>重排后的钻带内容</returns>
|
||
public string ReorderAndRenumberTools(bool isApply = false, bool skipConfirmation = false)
|
||
{
|
||
if (Tools.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("没有可重排的刀具");
|
||
}
|
||
|
||
// 保存原始刀具列表(按文件中的原始顺序)
|
||
var originalTools = OriginalTools.Select(t => new ToolItem
|
||
{
|
||
ToolNumber = t.ToolNumber,
|
||
Diameter = t.Diameter,
|
||
ToolType = t.ToolType,
|
||
ToolSuffixType = t.ToolSuffixType,
|
||
ToolCategory = t.ToolCategory,
|
||
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = t.MachineCodeCommand,
|
||
MachineCodeType = t.MachineCodeType
|
||
}).ToList();
|
||
|
||
// 1. 创建原始刀具编号到新编号的映射
|
||
|
||
// 创建重排后的刀具列表(基于用户已调整的顺序)
|
||
// 注意:Tools 列表已经反映了用户通过拖放操作调整后的顺序
|
||
var reorderedTools = new List<ToolItem>();
|
||
reorderedTools = Tools.Select(t => new ToolItem
|
||
{
|
||
ToolNumber = t.ToolNumber,
|
||
Diameter = t.Diameter,
|
||
ToolType = t.ToolType,
|
||
ToolSuffixType = t.ToolSuffixType,
|
||
ToolCategory = t.ToolCategory,
|
||
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = t.MachineCodeCommand,
|
||
MachineCodeType = t.MachineCodeType
|
||
}).ToList();
|
||
|
||
//重新排一下 reorderedTools 的刀序Txx
|
||
int number = 1;
|
||
foreach (var item in reorderedTools)
|
||
item.ToolNumber = number++;
|
||
|
||
//检查是否所有刀序没有变化
|
||
int checkindex = 1;
|
||
for (int i = 0; i < originalTools.Count; i++)
|
||
{
|
||
if (reorderedTools[i].ToolNumber == originalTools[i].ToolNumber)
|
||
checkindex++;
|
||
}
|
||
if (checkindex == originalTools.Count)
|
||
{
|
||
if (isApply)
|
||
{
|
||
ApplyToolOrderToDrillTape();
|
||
return DrillTapeContent;
|
||
}
|
||
throw new InvalidOperationException("刀具顺序未发生变化,无需重排");
|
||
}
|
||
|
||
// 显示确认窗口(除非跳过确认)
|
||
if (!skipConfirmation)
|
||
{
|
||
var confirmationWindow = new ToolReorderConfirmationWindow(originalTools, reorderedTools);
|
||
confirmationWindow.Owner = System.Windows.Application.Current.MainWindow;
|
||
confirmationWindow.ShowDialog();
|
||
if (!confirmationWindow.IsConfirmed)
|
||
throw new OperationCanceledException("取消刀序重排操作");
|
||
}
|
||
|
||
// 2. 按当前列表顺序重新编号(T01, T02, T03...)
|
||
|
||
var toolNumberMapping = new Dictionary<int, int>();
|
||
var originalToNewMapping = new Dictionary<int, int>(); // 保存原始映射关系
|
||
int newToolNumber = 1;
|
||
foreach (var tool in Tools.ToList())
|
||
{
|
||
// 机台码刀具不允许重新编号
|
||
if (tool.ToolType != ToolType.MachineCode)
|
||
{
|
||
originalToNewMapping[tool.ToolNumber] = newToolNumber;
|
||
toolNumberMapping[tool.ToolNumber] = newToolNumber;
|
||
tool.ToolNumber = newToolNumber++;
|
||
}
|
||
else
|
||
{
|
||
// 机台码刀具保持原始编号,但也要加入映射
|
||
toolNumberMapping[tool.ToolNumber] = tool.ToolNumber;
|
||
}
|
||
}
|
||
|
||
// 3. 更新钻带内容中的刀具编号和孔位数据
|
||
string updatedDrillTape = UpdateDrillTapeWithNewToolNumbers(DrillTapeContent, toolNumberMapping);
|
||
|
||
// 4. 更新钻带内容
|
||
DrillTapeContent = updatedDrillTape;
|
||
|
||
// 5. 更新界面显示
|
||
OnPropertyChanged(nameof(Tools));
|
||
|
||
if (isApply)
|
||
ApplyToolOrderToDrillTape();
|
||
|
||
return updatedDrillTape;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用新的刀具编号更新钻带内容
|
||
/// </summary>
|
||
/// <param name="originalDrillTape">原始钻带内容</param>
|
||
/// <param name="toolNumberMapping">刀具编号映射</param>
|
||
/// <returns>更新后的钻带内容</returns>
|
||
private string UpdateDrillTapeWithNewToolNumbers(string originalDrillTape, Dictionary<int, int> toolNumberMapping)
|
||
{
|
||
var lines = originalDrillTape.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
|
||
var result = new List<string>();
|
||
|
||
// 创建新编号到刀具对象的映射,以便获取对应的坐标数据
|
||
var newNumberToToolMap = new Dictionary<int, ToolItem>();
|
||
foreach (var tool in Tools)
|
||
{
|
||
if (toolNumberMapping.ContainsValue(tool.ToolNumber))
|
||
{
|
||
newNumberToToolMap[tool.ToolNumber] = tool;
|
||
}
|
||
}
|
||
|
||
// 首先解析原始钻带中的刀具定义行,保存参数信息
|
||
var originalToolDefinitions = new Dictionary<int, string>();
|
||
var headerLines = new List<string>();
|
||
|
||
foreach (var line in lines)
|
||
{
|
||
string trimmedLine = line.Trim();
|
||
|
||
if (trimmedLine == "%")
|
||
{
|
||
//headerLines.Add(line);
|
||
break;
|
||
}
|
||
|
||
// 处理刀具定义行(如 T01C1.049H05000Z+0.000S060.00F105.0U0700.0)
|
||
if (Regex.IsMatch(trimmedLine, @"^T(\d+)C"))
|
||
{
|
||
var match = Regex.Match(trimmedLine, @"^T(\d+)C(.*)$");
|
||
if (match.Success)
|
||
{
|
||
int originalToolNumber = int.Parse(match.Groups[1].Value);
|
||
originalToolDefinitions[originalToolNumber] = match.Groups[2].Value;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (line != "")
|
||
headerLines.Add(line);
|
||
}
|
||
}
|
||
|
||
// 添加头部信息
|
||
result.AddRange(headerLines);
|
||
|
||
// 按照新的刀具编号顺序输出刀具定义部分
|
||
var sortedTools = Tools.OrderBy(t => t.ToolNumber).ToList();
|
||
foreach (var tool in sortedTools)
|
||
{
|
||
// 查找原始刀具编号
|
||
int originalToolNumber = -1;
|
||
foreach (var kvp in toolNumberMapping)
|
||
{
|
||
if (kvp.Value == tool.ToolNumber)
|
||
{
|
||
originalToolNumber = kvp.Key;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (originalToolNumber != -1 && originalToolDefinitions.ContainsKey(originalToolNumber))
|
||
{
|
||
string toolDef = $"T{tool.ToolNumber:D2}C{originalToolDefinitions[originalToolNumber]}";
|
||
result.Add(toolDef);
|
||
}
|
||
}
|
||
|
||
// 添加 % 符号
|
||
result.Add("%");
|
||
|
||
// 处理刀具切换行和坐标数据部分
|
||
// 按新刀具编号顺序输出刀具切换行和对应的坐标数据
|
||
var sortedToolsForData = Tools.OrderBy(t => t.ToolNumber).ToList();
|
||
|
||
foreach (var tool in sortedToolsForData)
|
||
{
|
||
// 添加刀具切换行
|
||
result.Add($"T{tool.ToolNumber:D2}");
|
||
|
||
// 添加该刀具对应的所有坐标数据
|
||
// HoleLocations现在应该已经保持了原始顺序
|
||
if (tool.ToolType != ToolType.MachineCode && tool.HoleLocations != null && tool.HoleLocations.Count > 0)
|
||
{
|
||
foreach (var location in tool.HoleLocations)
|
||
{
|
||
result.Add(location);
|
||
}
|
||
}
|
||
|
||
// 如果是机台码刀具,添加机台码命令和坐标
|
||
if (tool.ToolType == ToolType.MachineCode)
|
||
{
|
||
if (!string.IsNullOrEmpty(tool.MachineCodeCommand) && !string.IsNullOrEmpty(tool.MachineCodeType))
|
||
{
|
||
result.Add($"{tool.MachineCodeCommand},{tool.MachineCodeType},$S $N");
|
||
|
||
// 添加机台码的坐标数据
|
||
// HoleLocations现在应该已经保持了原始顺序
|
||
if (tool.HoleLocations != null && tool.HoleLocations.Count > 0)
|
||
{
|
||
foreach (var location in tool.HoleLocations)
|
||
{
|
||
result.Add(location);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加结束标记
|
||
result.Add("M30");
|
||
|
||
return string.Join("\r\n", result);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载示例数据
|
||
/// </summary>
|
||
public void LoadSampleData()
|
||
{
|
||
// 清空原始文件路径,因为这是示例数据
|
||
OriginalFilePath = string.Empty;
|
||
Tools.Clear();
|
||
OriginalTools.Clear(); // 清空原始刀具顺序
|
||
|
||
// 添加示例刀具数据
|
||
Tools.Add(new ToolItem
|
||
{
|
||
ToolNumber = 1,
|
||
Diameter = 1.049, // 尾号9,特殊孔径固定为圆孔
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = 7,
|
||
// SlotHoles = 0,
|
||
// TotalHoles = 7,
|
||
ToolType = ToolType.Regular,
|
||
ToolSuffixType = ToolItem.GetToolSuffixType(1.049), // 特殊孔径
|
||
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.049)),
|
||
HoleLocations = new List<string>
|
||
{
|
||
"X-167525Y013500",
|
||
"X-167525Y018500",
|
||
"X-167525Y023500",
|
||
"X167525Y013500",
|
||
"X167525Y018500",
|
||
"X167525Y023500",
|
||
"X099366Y502000"
|
||
}
|
||
});
|
||
Tools.Add(new ToolItem
|
||
{
|
||
ToolNumber = 2,
|
||
Diameter = 1.550, // 尾号0 -> 钻针
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = 5,
|
||
// SlotHoles = 0,
|
||
// TotalHoles = 5,
|
||
ToolType = ToolType.Regular,
|
||
ToolSuffixType = ToolItem.GetToolSuffixType(1.550), // 基于孔径计算
|
||
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.550)),
|
||
HoleLocations = new List<string>
|
||
{
|
||
"X-065975Y115250",
|
||
"X-085825Y122450",
|
||
"X-085825Y124550",
|
||
"X-097425Y115250",
|
||
"X103093Y502000"
|
||
}
|
||
});
|
||
Tools.Add(new ToolItem
|
||
{
|
||
ToolNumber = 3,
|
||
Diameter = 1.156, // 尾号6 -> EA型槽刀
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = 1,
|
||
// SlotHoles = 528,
|
||
// TotalHoles = 529,
|
||
ToolType = ToolType.Slot,
|
||
ToolSuffixType = ToolItem.GetToolSuffixType(1.156), // 基于孔径计算
|
||
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.156)),
|
||
HoleLocations = new List<string>
|
||
{
|
||
"X-069659Y016450G85X-094159Y016450",
|
||
"X-181341Y195550G85X-156841Y195550",
|
||
"X-069659Y210450G85X-094159Y210450",
|
||
"X-181341Y389550G85X-156841Y389550",
|
||
"X-069659Y404450G85X-094159Y404450",
|
||
"X-181341Y583550G85X-156841Y583550",
|
||
"X162939Y596000"
|
||
}
|
||
});
|
||
Tools.Add(new ToolItem
|
||
{
|
||
ToolNumber = 4,
|
||
Diameter = 1.451, // 尾号1 -> 槽刀
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = 57,
|
||
// SlotHoles = 0,
|
||
// TotalHoles = 57,
|
||
ToolType = ToolType.Regular,
|
||
ToolSuffixType = ToolItem.GetToolSuffixType(1.451), // 基于孔径计算
|
||
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.451)),
|
||
HoleLocations = new List<string>
|
||
{
|
||
"X-065975Y115250",
|
||
"X-085825Y122450",
|
||
"X-085825Y124550",
|
||
"X-097425Y115250",
|
||
"X103093Y502000"
|
||
}
|
||
});
|
||
Tools.Add(new ToolItem
|
||
{
|
||
ToolNumber = 5,
|
||
Diameter = 1.153, // 尾号3 -> 粉尘刀
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = 10,
|
||
// SlotHoles = 0,
|
||
// TotalHoles = 10,
|
||
ToolType = ToolType.Regular,
|
||
ToolSuffixType = ToolItem.GetToolSuffixType(1.153), // 基于孔径计算
|
||
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.153)),
|
||
HoleLocations = new List<string>
|
||
{
|
||
"X-065975Y115250",
|
||
"X-085825Y122450",
|
||
"X-085825Y124550",
|
||
"X-097425Y115250",
|
||
"X103093Y502000"
|
||
}
|
||
});
|
||
Tools.Add(new ToolItem
|
||
{
|
||
ToolNumber = 6,
|
||
Diameter = 0.499, // 特殊孔径固定为圆孔(机台码)
|
||
// 孔数相关属性已移除
|
||
// RegularHoles = 57,
|
||
// SlotHoles = 0,
|
||
// TotalHoles = 57,
|
||
ToolType = ToolType.MachineCode,
|
||
MachineCodeCommand = "M97",
|
||
MachineCodeType = "A*",
|
||
ToolSuffixType = ToolItem.GetToolSuffixType(0.499), // 特殊孔径
|
||
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(0.499)),
|
||
HoleLocations = new List<string> { "X-194000Y002000" } // 机台码刀具的坐标数据
|
||
});
|
||
|
||
// 同时添加到原始刀具顺序集合中
|
||
foreach (var tool in Tools)
|
||
{
|
||
var originalToolItem = new ToolItem
|
||
{
|
||
ToolNumber = tool.ToolNumber,
|
||
Diameter = tool.Diameter,
|
||
ToolType = tool.ToolType,
|
||
ToolSuffixType = tool.ToolSuffixType,
|
||
ToolCategory = tool.ToolCategory,
|
||
HoleLocations = new List<string>(tool.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = tool.MachineCodeCommand,
|
||
MachineCodeType = tool.MachineCodeType
|
||
};
|
||
OriginalTools.Add(originalToolItem);
|
||
}
|
||
|
||
// 更新按钮状态
|
||
UpdateMoveButtonsState();
|
||
|
||
// 计算并更新最小直径信息
|
||
UpdateMinDiameterInfo();
|
||
|
||
// 加载示例钻带内容
|
||
DrillTapeContent = @"M48
|
||
;厚铜板参数-镀膜-EA-250618
|
||
T01C1.049H05000Z+0.000S060.00F105.0U0700.0
|
||
T02C1.550H01500Z+0.150S070.00F008.0U0800.0
|
||
T03C1.156H03000Z-0.200S040.00F030.0U0900.0
|
||
T04C1.451H01500Z+0.150S070.00F008.0U0800.0
|
||
T05C1.153H01500Z+0.150S070.00F008.0U0800.0
|
||
T06C0.499H01500Z+0.150S070.00F008.0U0800.0
|
||
%
|
||
T01
|
||
X-167525Y013500
|
||
X-167525Y018500
|
||
X-167525Y023500
|
||
X167525Y013500
|
||
X167525Y018500
|
||
X167525Y023500
|
||
X099366Y502000
|
||
T02
|
||
X-065975Y115250
|
||
X-085825Y122450
|
||
X-085825Y124550
|
||
X-097425Y115250
|
||
X103093Y502000
|
||
T03
|
||
X-069659Y016450G85X-094159Y016450
|
||
X-181341Y195550G85X-156841Y195550
|
||
X-069659Y210450G85X-094159Y210450
|
||
X-181341Y389550G85X-156841Y389550
|
||
X-069659Y404450G85X-094159Y404450
|
||
X-181341Y583550G85X-156841Y583550
|
||
X162939Y596000
|
||
T04
|
||
X-065975Y115250
|
||
X-085825Y122450
|
||
X-085825Y124550
|
||
X-097425Y115250
|
||
X103093Y502000
|
||
T05
|
||
X-065975Y115250
|
||
X-085825Y122450
|
||
X-085825Y124550
|
||
X-097425Y115250
|
||
X103093Y502000
|
||
T06
|
||
M97,A*,$S $N
|
||
X-194000Y002000
|
||
M30";
|
||
}
|
||
|
||
public event PropertyChangedEventHandler? PropertyChanged;
|
||
|
||
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||
{
|
||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||
return false;
|
||
|
||
field = value;
|
||
OnPropertyChanged(propertyName);
|
||
return true;
|
||
}
|
||
|
||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||
{
|
||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
}
|
||
|
||
private void VerifyResults(DrillTapeResult result)
|
||
{
|
||
// CAM350的预期结果
|
||
var expectedResults = new[]
|
||
{
|
||
new { ToolNumber = 1, Diameter = 0.799, ExpectedHoles = 7 },
|
||
new { ToolNumber = 2, Diameter = 0.656, ExpectedHoles = 5 },
|
||
new { ToolNumber = 3, Diameter = 1.601, ExpectedHoles = 529 },
|
||
new { ToolNumber = 4, Diameter = 0.499, ExpectedHoles = 57 }
|
||
};
|
||
|
||
bool allMatch = true;
|
||
|
||
foreach (var expected in expectedResults)
|
||
{
|
||
var actual = result.ToolResults.Find(t => t.ToolNumber == expected.ToolNumber);
|
||
if (actual != null)
|
||
{
|
||
bool match = Math.Abs(actual.Diameter - expected.Diameter) < 0.001 &&
|
||
actual.TotalHoles == expected.ExpectedHoles;
|
||
|
||
Console.WriteLine($"T{expected.ToolNumber:D2} ({expected.Diameter:F3}mm): " +
|
||
$"预期={expected.ExpectedHoles}, 实际={actual.TotalHoles}, " +
|
||
$"{(match ? "✓" : "✗")}");
|
||
|
||
if (!match)
|
||
allMatch = false;
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"T{expected.ToolNumber:D2}: 未找到结果 ✗");
|
||
allMatch = false;
|
||
}
|
||
}
|
||
|
||
Console.WriteLine($"\n总体结果: {(allMatch ? "✓ 所有结果与CAM350一致" : "✗ 存在不一致的结果")}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示刀具详情窗口
|
||
/// </summary>
|
||
/// <param name="tool">要显示详情的刀具</param>
|
||
public void ShowToolDetail(ToolItem tool)
|
||
{
|
||
if (tool == null)
|
||
return;
|
||
|
||
try
|
||
{
|
||
var detailWindow = new ToolDetailWindow(tool);
|
||
detailWindow.Owner = System.Windows.Application.Current.MainWindow;
|
||
detailWindow.ShowDialog();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show($"显示刀具详情失败: {ex.Message}", "错误",
|
||
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试重排刀序功能
|
||
/// </summary>
|
||
public void TestReorderAndRenumberTools()
|
||
{
|
||
Console.WriteLine("=== 重排刀序功能测试 ===");
|
||
|
||
// 1. 加载测试数据
|
||
LoadSampleData();
|
||
|
||
// 2. 记录原始状态
|
||
var originalOrder = Tools.Select(t => new
|
||
{
|
||
t.ToolNumber,
|
||
t.Diameter,
|
||
LocationCount = t.HoleLocations?.Count ?? 0,
|
||
FirstLocation = t.HoleLocations?.FirstOrDefault() ?? ""
|
||
}).ToList();
|
||
|
||
Console.WriteLine("原始刀具顺序:");
|
||
foreach (var tool in originalOrder)
|
||
{
|
||
Console.WriteLine($"T{tool.ToolNumber:D2}({tool.Diameter:F3}): {tool.LocationCount}个坐标, 首个坐标: {tool.FirstLocation}");
|
||
}
|
||
|
||
// 3. 执行重排操作(将T02移到第一位)
|
||
if (Tools.Count >= 2)
|
||
{
|
||
var toolToMove = Tools.FirstOrDefault(t => t.ToolNumber == 2);
|
||
if (toolToMove != null && toolToMove.ToolType != ToolType.MachineCode)
|
||
{
|
||
int oldIndex = Tools.IndexOf(toolToMove);
|
||
ReorderTools(oldIndex, 0);
|
||
}
|
||
}
|
||
|
||
// 4. 执行重新编号
|
||
string reorderedDrillTape = ReorderAndRenumberTools();
|
||
|
||
// 5. 验证结果
|
||
Console.WriteLine("\n重排后刀具顺序:");
|
||
var reorderedOrder = Tools.Select(t => new
|
||
{
|
||
t.ToolNumber,
|
||
t.Diameter,
|
||
LocationCount = t.HoleLocations?.Count ?? 0,
|
||
FirstLocation = t.HoleLocations?.FirstOrDefault() ?? ""
|
||
}).ToList();
|
||
|
||
foreach (var tool in reorderedOrder)
|
||
{
|
||
Console.WriteLine($"T{tool.ToolNumber:D2}({tool.Diameter:F3}): {tool.LocationCount}个坐标, 首个坐标: {tool.FirstLocation}");
|
||
}
|
||
|
||
// 6. 验证钻带内容
|
||
Console.WriteLine("\n重排后的钻带内容:");
|
||
Console.WriteLine(reorderedDrillTape);
|
||
|
||
// 7. 验证坐标跟随
|
||
VerifyCoordinateBinding(originalOrder.ToList<dynamic>(), reorderedOrder.ToList<dynamic>());
|
||
|
||
Console.WriteLine("\n重排刀序功能测试完成!");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证坐标绑定关系
|
||
/// </summary>
|
||
private void VerifyCoordinateBinding(List<dynamic> originalOrder, List<dynamic> reorderedOrder)
|
||
{
|
||
Console.WriteLine("\n=== 验证坐标绑定关系 ===");
|
||
|
||
// 创建直径到坐标的映射
|
||
var originalDiameterToLocation = new Dictionary<double, string>();
|
||
foreach (var tool in originalOrder)
|
||
{
|
||
originalDiameterToLocation[tool.Diameter] = tool.FirstLocation;
|
||
}
|
||
|
||
// 验证重排后的坐标绑定
|
||
bool allMatch = true;
|
||
foreach (var tool in reorderedOrder)
|
||
{
|
||
if (originalDiameterToLocation.ContainsKey(tool.Diameter))
|
||
{
|
||
string originalLocation = originalDiameterToLocation[tool.Diameter];
|
||
if (tool.FirstLocation != originalLocation)
|
||
{
|
||
Console.WriteLine($"✗ 刀具{tool.Diameter:F3}的坐标不匹配: 原始{originalLocation}, 重排后{tool.FirstLocation}");
|
||
allMatch = false;
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"✓ 刀具{tool.Diameter:F3}的坐标正确跟随: {tool.FirstLocation}");
|
||
}
|
||
}
|
||
}
|
||
|
||
Console.WriteLine($"\n坐标绑定验证结果: {(allMatch ? "✓ 全部正确" : "✗ 存在问题")}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开文件资源管理器并选中指定文件
|
||
/// </summary>
|
||
/// <param name="filePath">要选中的文件路径</param>
|
||
private void OpenFileExplorerAndSelectFile(string filePath)
|
||
{
|
||
try
|
||
{
|
||
if (!File.Exists(filePath))
|
||
{
|
||
System.Windows.MessageBox.Show($"文件不存在: {filePath}", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 使用 explorer.exe 的 /select 参数来打开文件资源管理器并选中文件
|
||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||
{
|
||
FileName = "explorer.exe",
|
||
Arguments = $"/select,\"{filePath}\"",
|
||
UseShellExecute = true
|
||
};
|
||
|
||
Process.Start(startInfo);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show($"打开文件资源管理器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成刀具直径列表文件
|
||
/// </summary>
|
||
/// <param name="originalFilePath">原始文件路径</param>
|
||
/// <param name="tools">刀具列表</param>
|
||
private void GenerateToolDiameterListFile(string originalFilePath, ObservableCollection<ToolItem> tools)
|
||
{
|
||
try
|
||
{
|
||
// 获取原文件的目录和文件名(不含扩展名)
|
||
string directory = Path.GetDirectoryName(originalFilePath) ?? "";
|
||
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(originalFilePath);
|
||
string outputFilePath = Path.Combine(directory, $"{fileNameWithoutExt}-sort.txt");
|
||
|
||
// 按刀具编号排序获取最终顺序
|
||
var sortedTools = tools.OrderBy(t => t.ToolNumber).ToList();
|
||
|
||
// 生成刀具直径列表内容
|
||
var diameterList = new StringBuilder();
|
||
foreach (var tool in sortedTools)
|
||
{
|
||
diameterList.AppendLine($"{tool.Diameter:F3}");
|
||
}
|
||
|
||
// 使用ANSI编码写入文件
|
||
Encoding ansiEncoding;
|
||
try
|
||
{
|
||
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
|
||
}
|
||
catch
|
||
{
|
||
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
|
||
}
|
||
|
||
File.WriteAllText(outputFilePath, diameterList.ToString(), ansiEncoding);
|
||
|
||
// 记录日志
|
||
System.Diagnostics.Debug.WriteLine($"刀具直径列表文件已生成: {outputFilePath}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 记录错误但不影响主要功能
|
||
System.Diagnostics.Debug.WriteLine($"生成刀具直径列表文件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成通用排序种子文件
|
||
/// </summary>
|
||
/// <param name="tools">刀具列表</param>
|
||
public void GenerateGeneralSortSeedFile(ObservableCollection<ToolItem> tools)
|
||
{
|
||
if (tools.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("没有刀具数据,无法生成排序种子文件");
|
||
}
|
||
|
||
try
|
||
{
|
||
// 获取原文件的目录
|
||
string directory = Path.GetDirectoryName(OriginalFilePath) ?? "";
|
||
string outputFilePath = Path.Combine(directory, "General_sort.txt");
|
||
|
||
// 检查文件是否已存在
|
||
if (File.Exists(outputFilePath))
|
||
{
|
||
var result = System.Windows.MessageBox.Show(
|
||
"General_sort.txt文件已存在,是否覆盖?\n\n" +
|
||
"是(Y):覆盖现有文件\n" +
|
||
"否(N):取消操作",
|
||
"文件已存在",
|
||
MessageBoxButton.YesNo,
|
||
MessageBoxImage.Question);
|
||
|
||
if (result != MessageBoxResult.Yes)
|
||
{
|
||
throw new OperationCanceledException("用户取消了覆盖操作");
|
||
}
|
||
}
|
||
|
||
// 按刀具编号排序获取最终顺序
|
||
var sortedTools = tools.OrderBy(t => t.ToolNumber).ToList();
|
||
|
||
// 生成刀具直径列表内容
|
||
var diameterList = new StringBuilder();
|
||
foreach (var tool in sortedTools)
|
||
{
|
||
diameterList.AppendLine($"{tool.Diameter:F3}");
|
||
}
|
||
|
||
// 使用ANSI编码写入文件
|
||
Encoding ansiEncoding;
|
||
try
|
||
{
|
||
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
|
||
}
|
||
catch
|
||
{
|
||
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
|
||
}
|
||
|
||
File.WriteAllText(outputFilePath, diameterList.ToString(), ansiEncoding);
|
||
|
||
// 记录日志
|
||
System.Diagnostics.Debug.WriteLine($"通用排序种子文件已生成: {outputFilePath}");
|
||
|
||
// 打开文件资源管理器并选中文件
|
||
OpenFileExplorerAndSelectFile(outputFilePath);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
// 重新抛出取消异常,不包装
|
||
throw;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new InvalidOperationException($"生成通用排序种子文件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查并应用-sort.txt文件中的刀具排序
|
||
/// </summary>
|
||
/// <param name="originalFilePath">原始文件路径</param>
|
||
/// <param name="tools">当前刀具列表</param>
|
||
private void CheckAndApplySortFile(string originalFilePath, ObservableCollection<ToolItem> tools)
|
||
{
|
||
try
|
||
{
|
||
// 获取-sort.txt文件路径
|
||
string directory = Path.GetDirectoryName(originalFilePath) ?? "";
|
||
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(originalFilePath);
|
||
string sortFilePath = Path.Combine(directory, $"{fileNameWithoutExt}-sort.txt");
|
||
|
||
// 获取General_sort.txt文件路径
|
||
string generalSortFilePath = Path.Combine(directory, "General_sort.txt");
|
||
|
||
// 检查是否存在排序种子文件
|
||
bool hasSpecificSortFile = File.Exists(sortFilePath);
|
||
bool hasGeneralSortFile = File.Exists(generalSortFilePath);
|
||
|
||
if (!hasSpecificSortFile && !hasGeneralSortFile)
|
||
return;
|
||
|
||
// 优先使用特定文件名的种子,如果两者都存在
|
||
string selectedFilePath = "";
|
||
string selectedFileName = "";
|
||
List<double> sortDiameters = new List<double>();
|
||
|
||
if (hasSpecificSortFile)
|
||
{
|
||
selectedFilePath = sortFilePath;
|
||
selectedFileName = Path.GetFileName(sortFilePath);
|
||
sortDiameters = ReadSortFile(sortFilePath);
|
||
}
|
||
else if (hasGeneralSortFile)
|
||
{
|
||
selectedFilePath = generalSortFilePath;
|
||
selectedFileName = Path.GetFileName(generalSortFilePath);
|
||
sortDiameters = ReadSortFile(generalSortFilePath);
|
||
}
|
||
|
||
// 检查是否成功读取到直径数据
|
||
if (sortDiameters.Count == 0)
|
||
return;
|
||
|
||
// 检查直径数量是否一致
|
||
if (sortDiameters.Count != tools.Count)
|
||
return;
|
||
|
||
// 构建提示消息
|
||
string message = hasSpecificSortFile && hasGeneralSortFile
|
||
? $"检测到两个排序种子文件:\n" +
|
||
$"1. 特定文件种子:{Path.GetFileName(sortFilePath)}\n" +
|
||
$"2. 通用排序种子:{Path.GetFileName(generalSortFilePath)}\n\n" +
|
||
$"系统将优先使用特定文件种子:{selectedFileName}\n\n" +
|
||
$"是否按该记录文件进行排序?\n\n" +
|
||
"是(Y):按记录文件排序\n" +
|
||
"否(N):保持当前顺序"
|
||
: $"检测到排序种子文件:{selectedFileName}\n\n" +
|
||
$"是否按该记录文件进行排序?\n\n" +
|
||
"是(Y):按记录文件排序\n" +
|
||
"否(N):保持当前顺序";
|
||
|
||
// 提示用户是否应用排序
|
||
var result = System.Windows.MessageBox.Show(
|
||
message,
|
||
"刀具排序提示",
|
||
MessageBoxButton.YesNo,
|
||
MessageBoxImage.Question);
|
||
|
||
if (result == MessageBoxResult.Yes)
|
||
{
|
||
// 按照排序文件中的直径顺序重新排列刀具
|
||
SortToolsByDiameterList(tools, sortDiameters);
|
||
|
||
// 执行完整的重排刀序流程,包括重新编号和更新钻带内容
|
||
try
|
||
{
|
||
string reorderedDrillTape = ReorderAndRenumberTools();
|
||
System.Diagnostics.Debug.WriteLine($"已按{selectedFileName}文件重新排序刀具并更新钻带内容");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"重排刀序失败: {ex.Message}");
|
||
// 即使重排失败,也保持列表的排序
|
||
OnPropertyChanged(nameof(Tools));
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 记录错误但不影响主要功能
|
||
System.Diagnostics.Debug.WriteLine($"检查排序种子文件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取-sort.txt文件内容并解析直径列表
|
||
/// </summary>
|
||
/// <param name="sortFilePath">-sort.txt文件路径</param>
|
||
/// <returns>直径列表</returns>
|
||
private List<double> ReadSortFile(string sortFilePath)
|
||
{
|
||
var diameters = new List<double>();
|
||
|
||
try
|
||
{
|
||
// 使用ANSI编码
|
||
Encoding ansiEncoding;
|
||
try
|
||
{
|
||
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
|
||
}
|
||
catch
|
||
{
|
||
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
|
||
}
|
||
|
||
// 使用 cmd 命令读取-sort.txt文件,参考钻带文件读取方法
|
||
var process = new Process
|
||
{
|
||
StartInfo = new ProcessStartInfo
|
||
{
|
||
FileName = "cmd.exe",
|
||
Arguments = $"/c type \"{sortFilePath}\"",
|
||
RedirectStandardOutput = true,
|
||
UseShellExecute = false,
|
||
CreateNoWindow = true,
|
||
StandardOutputEncoding = ansiEncoding
|
||
}
|
||
};
|
||
|
||
process.Start();
|
||
string fileContent = process.StandardOutput.ReadToEnd();
|
||
process.WaitForExit();
|
||
|
||
// 按行分割内容
|
||
string[] lines = fileContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
foreach (string line in lines)
|
||
{
|
||
string trimmedLine = line.Trim();
|
||
if (!string.IsNullOrEmpty(trimmedLine))
|
||
{
|
||
if (double.TryParse(trimmedLine, out double diameter))
|
||
{
|
||
diameters.Add(diameter);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"读取-sort.txt文件失败: {ex.Message}");
|
||
}
|
||
|
||
return diameters;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据直径列表排序刀具
|
||
/// </summary>
|
||
/// <param name="tools">刀具列表</param>
|
||
/// <param name="diameterList">直径列表(目标顺序)</param>
|
||
private void SortToolsByDiameterList(ObservableCollection<ToolItem> tools, List<double> diameterList)
|
||
{
|
||
// 创建直径到刀具的映射
|
||
var diameterToTools = new Dictionary<double, List<ToolItem>>();
|
||
foreach (var tool in tools)
|
||
{
|
||
double roundedDiameter = Math.Round(tool.Diameter, 3);
|
||
if (!diameterToTools.ContainsKey(roundedDiameter))
|
||
{
|
||
diameterToTools[roundedDiameter] = new List<ToolItem>();
|
||
}
|
||
diameterToTools[roundedDiameter].Add(tool);
|
||
}
|
||
|
||
// 按照直径列表的顺序重新排列刀具
|
||
var sortedTools = new List<ToolItem>();
|
||
foreach (double diameter in diameterList)
|
||
{
|
||
double roundedDiameter = Math.Round(diameter, 3);
|
||
if (diameterToTools.ContainsKey(roundedDiameter))
|
||
{
|
||
sortedTools.AddRange(diameterToTools[roundedDiameter]);
|
||
}
|
||
}
|
||
|
||
// 清空原列表并添加排序后的刀具
|
||
tools.Clear();
|
||
foreach (var tool in sortedTools)
|
||
{
|
||
tools.Add(tool);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换窗口置顶状态
|
||
/// </summary>
|
||
public void ToggleTopmost()
|
||
{
|
||
IsTopmost = !IsTopmost;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新最小直径信息
|
||
/// </summary>
|
||
private void UpdateMinDiameterInfo()
|
||
{
|
||
// 初始化为最大值,以便找到最小值
|
||
MinDrillDiameter = double.MaxValue;
|
||
MinSlotDiameter = double.MaxValue;
|
||
MinEADiameter = double.MaxValue;
|
||
|
||
foreach (var tool in Tools)
|
||
{
|
||
// 根据刀具类型分类计算最小直径
|
||
if (tool.ToolType == ToolType.Regular)
|
||
{
|
||
// 普通钻咀
|
||
if (tool.Diameter < MinDrillDiameter)
|
||
MinDrillDiameter = tool.Diameter;
|
||
}
|
||
else if (tool.ToolType == ToolType.Slot)
|
||
{
|
||
// 槽刀
|
||
if (tool.Diameter < MinSlotDiameter)
|
||
MinSlotDiameter = tool.Diameter;
|
||
|
||
// 检查是否是EA型槽刀(尾号为2或6)
|
||
if ((tool.ToolSuffixType == ToolSuffixType.EASlot || tool.ToolSuffixType == ToolSuffixType.EASlot2)
|
||
&& tool.Diameter < MinEADiameter)
|
||
MinEADiameter = tool.Diameter;
|
||
}
|
||
}
|
||
|
||
// 如果没有找到对应的刀具类型,将值设为0
|
||
if (MinDrillDiameter == double.MaxValue)
|
||
MinDrillDiameter = 0;
|
||
|
||
if (MinSlotDiameter == double.MaxValue)
|
||
MinSlotDiameter = 0;
|
||
|
||
if (MinEADiameter == double.MaxValue)
|
||
MinEADiameter = 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试使用参考钻带重排功能
|
||
/// </summary>
|
||
public void TestReorderByReferenceDrillTape()
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("=== 测试使用参考钻带重排功能 ===");
|
||
|
||
try
|
||
{
|
||
// 1. 加载测试数据
|
||
LoadSampleData();
|
||
System.Diagnostics.Debug.WriteLine($"已加载{Tools.Count}把刀具的测试数据");
|
||
|
||
// 2. 创建模拟的参考钻带内容
|
||
string mockReferenceContent = CreateMockReferenceDrillTape(Tools.ToList());
|
||
string tempReferenceFile = System.IO.Path.GetTempFileName();
|
||
System.IO.File.WriteAllText(tempReferenceFile, mockReferenceContent);
|
||
|
||
try
|
||
{
|
||
// 3. 读取参考钻带的刀序
|
||
var referenceDiameters = ReadToolOrderFromReferenceDrillTape(tempReferenceFile);
|
||
System.Diagnostics.Debug.WriteLine($"从参考钻带读取到{referenceDiameters.Count}把刀具的顺序");
|
||
System.Diagnostics.Debug.WriteLine($"参考钻带刀序:{string.Join(", ", referenceDiameters.Select(d => d.ToString("F3")))}");
|
||
|
||
// 4. 验证刀具匹配
|
||
var (isValid, message, warnings) = ValidateToolMatch(Tools, referenceDiameters);
|
||
System.Diagnostics.Debug.WriteLine($"验证结果:{(isValid ? "通过" : "失败")}");
|
||
System.Diagnostics.Debug.WriteLine($"验证消息:{message}");
|
||
|
||
if (warnings.Any())
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"警告信息:{string.Join("; ", warnings)}");
|
||
}
|
||
|
||
if (isValid)
|
||
{
|
||
// 5. 执行重排
|
||
var originalOrder = Tools.Select(t => new { t.ToolNumber, t.Diameter }).ToList();
|
||
System.Diagnostics.Debug.WriteLine($"原始刀序:{string.Join(", ", originalOrder.Select(t => $"T{t.ToolNumber:D2}({t.Diameter:F3})"))}");
|
||
|
||
SortToolsByDiameterList(Tools, referenceDiameters);
|
||
|
||
var newOrder = Tools.Select(t => new { t.ToolNumber, t.Diameter }).ToList();
|
||
System.Diagnostics.Debug.WriteLine($"重排后刀序:{string.Join(", ", newOrder.Select(t => $"T{t.ToolNumber:D2}({t.Diameter:F3})"))}");
|
||
|
||
// 验证重排是否正确
|
||
bool isCorrectOrder = true;
|
||
for (int i = 0; i < referenceDiameters.Count; i++)
|
||
{
|
||
double expectedDiameter = Math.Round(referenceDiameters[i], 3);
|
||
double actualDiameter = Math.Round(Tools[i].Diameter, 3);
|
||
if (expectedDiameter != actualDiameter)
|
||
{
|
||
isCorrectOrder = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine($"重排验证:{(isCorrectOrder ? "成功" : "失败")}");
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
// 清理临时文件
|
||
if (System.IO.File.Exists(tempReferenceFile))
|
||
{
|
||
System.IO.File.Delete(tempReferenceFile);
|
||
}
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine("测试完成!");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"测试失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建模拟的参考钻带内容
|
||
/// </summary>
|
||
/// <param name="tools">刀具列表</param>
|
||
/// <returns>模拟的钻带内容</returns>
|
||
private string CreateMockReferenceDrillTape(List<ToolItem> tools)
|
||
{
|
||
var content = new System.Text.StringBuilder();
|
||
content.AppendLine("M48");
|
||
content.AppendLine(";模拟参考钻带文件");
|
||
|
||
// 添加刀具定义
|
||
foreach (var tool in tools.OrderBy(t => t.ToolNumber))
|
||
{
|
||
content.AppendLine($"T{tool.ToolNumber:D2}C{tool.Diameter:F3}H01500Z+0.150S070.00F008.0U0800.0");
|
||
}
|
||
|
||
content.AppendLine("%");
|
||
|
||
// 添加刀具切换和坐标数据
|
||
foreach (var tool in tools.OrderBy(t => t.ToolNumber))
|
||
{
|
||
content.AppendLine($"T{tool.ToolNumber:D2}");
|
||
|
||
if (tool.HoleLocations != null && tool.HoleLocations.Any())
|
||
{
|
||
foreach (var location in tool.HoleLocations.Take(3)) // 只取前3个坐标作为示例
|
||
{
|
||
content.AppendLine(location);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
content.AppendLine("X000000Y000000"); // 默认坐标
|
||
}
|
||
}
|
||
|
||
content.AppendLine("M30");
|
||
|
||
return content.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用参考钻带的刀序重排当前刀具
|
||
/// </summary>
|
||
public void ReorderToolsByReferenceDrillTape()
|
||
{
|
||
try
|
||
{
|
||
// 前置检查
|
||
if (Tools.Count == 0)
|
||
{
|
||
System.Windows.MessageBox.Show("没有可重排的刀具,请先加载钻带文件", "提示",
|
||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 1. 选择参考钻带文件
|
||
var openFileDialog = new Microsoft.Win32.OpenFileDialog
|
||
{
|
||
Filter = "钻带文件 (*.txt;*.drl;*.dr2;*.dpin)|*.txt;*.drl;*.dr2;*.dpin|所有文件 (*.*)|*.*",
|
||
Title = "选择参考钻带文件"
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() != true)
|
||
return;
|
||
|
||
// 显示进度提示
|
||
var progressDialog = new System.Windows.Window
|
||
{
|
||
Title = "处理中",
|
||
Width = 300,
|
||
Height = 100,
|
||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||
Owner = System.Windows.Application.Current.MainWindow,
|
||
ResizeMode = ResizeMode.NoResize
|
||
};
|
||
|
||
var progressText = new System.Windows.Controls.TextBlock
|
||
{
|
||
Text = "正在读取参考钻带文件...",
|
||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||
VerticalAlignment = VerticalAlignment.Center,
|
||
Margin = new Thickness(20)
|
||
};
|
||
|
||
progressDialog.Content = progressText;
|
||
|
||
// 异步处理以避免界面冻结
|
||
var progressDialogShown = false;
|
||
|
||
System.Threading.Tasks.Task.Run(() =>
|
||
{
|
||
try
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressDialog.Show();
|
||
progressDialogShown = true;
|
||
progressText.Text = "正在读取参考钻带文件...";
|
||
});
|
||
|
||
List<double> referenceDiameters;
|
||
string referenceFileName = System.IO.Path.GetFileName(openFileDialog.FileName);
|
||
|
||
// 2. 读取参考钻带的刀序
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressText.Text = "正在解析刀具信息...";
|
||
});
|
||
|
||
referenceDiameters = ReadToolOrderFromReferenceDrillTape(openFileDialog.FileName);
|
||
|
||
if (referenceDiameters.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("参考钻带文件中没有找到有效的刀具信息");
|
||
}
|
||
|
||
// 3. 验证刀具匹配
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressText.Text = "正在验证刀具匹配...";
|
||
});
|
||
|
||
var (isValid, message, warnings) = ValidateToolMatch(Tools, referenceDiameters);
|
||
|
||
if (!isValid)
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressDialog.Close();
|
||
System.Windows.MessageBox.Show(message, "刀具匹配失败",
|
||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 显示警告信息(如果有)
|
||
if (warnings.Any())
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
var warningResult = System.Windows.MessageBox.Show(
|
||
$"{string.Join("\n", warnings)}\n\n是否继续重排操作?",
|
||
"警告",
|
||
MessageBoxButton.YesNo,
|
||
MessageBoxImage.Warning);
|
||
|
||
if (warningResult == MessageBoxResult.No)
|
||
{
|
||
progressDialog.Close();
|
||
return;
|
||
}
|
||
});
|
||
}
|
||
|
||
// 4. 保存原始刀具顺序用于对比
|
||
var originalTools = Tools.Select(t => new ToolItem
|
||
{
|
||
ToolNumber = t.ToolNumber,
|
||
Diameter = t.Diameter,
|
||
ToolType = t.ToolType,
|
||
ToolSuffixType = t.ToolSuffixType,
|
||
ToolCategory = t.ToolCategory,
|
||
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = t.MachineCodeCommand,
|
||
MachineCodeType = t.MachineCodeType
|
||
}).ToList();
|
||
|
||
// 5. 按照参考钻带的顺序重排刀具
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressText.Text = "正在重排刀具顺序...";
|
||
SortToolsByDiameterList(Tools, referenceDiameters);
|
||
});
|
||
|
||
// 6. 创建重排后的刀具列表用于确认
|
||
var reorderedTools = Tools.Select(t => new ToolItem
|
||
{
|
||
ToolNumber = t.ToolNumber,
|
||
Diameter = t.Diameter,
|
||
ToolType = t.ToolType,
|
||
ToolSuffixType = t.ToolSuffixType,
|
||
ToolCategory = t.ToolCategory,
|
||
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = t.MachineCodeCommand,
|
||
MachineCodeType = t.MachineCodeType
|
||
}).ToList();
|
||
|
||
// 7. 显示确认窗口
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressDialog.Close();
|
||
|
||
var confirmationWindow = new ToolReorderConfirmationWindow(originalTools, reorderedTools);
|
||
confirmationWindow.Owner = System.Windows.Application.Current.MainWindow;
|
||
confirmationWindow.Title = $"使用参考钻带刀序确认 - {referenceFileName}";
|
||
confirmationWindow.ShowDialog();
|
||
|
||
if (!confirmationWindow.IsConfirmed)
|
||
{
|
||
// 用户取消,恢复原始顺序
|
||
Tools.Clear();
|
||
foreach (var tool in originalTools)
|
||
{
|
||
Tools.Add(tool);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 8. 执行重排和重新编号
|
||
try
|
||
{
|
||
string reorderedDrillTape = ReorderAndRenumberTools(skipConfirmation: true);
|
||
DrillTapeContent = reorderedDrillTape;
|
||
|
||
System.Windows.MessageBox.Show(
|
||
$"已成功使用参考钻带文件重排刀具顺序\n\n参考文件:{referenceFileName}\n重排刀具数量:{Tools.Count}把",
|
||
"重排完成",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"重排过程中发生错误:{ex.Message}\n\n刀具顺序已更新,但钻带内容未能重新生成。",
|
||
"部分完成",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Warning);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
if (progressDialogShown)
|
||
progressDialog.Close();
|
||
|
||
System.Windows.MessageBox.Show(
|
||
$"使用参考钻带重排失败:{ex.Message}",
|
||
"错误",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Error);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"启动重排操作失败:{ex.Message}",
|
||
"错误",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从参考钻带文件读取刀具顺序
|
||
/// </summary>
|
||
/// <param name="referenceFilePath">参考钻带文件路径</param>
|
||
/// <returns>按顺序排列的刀具直径列表</returns>
|
||
private List<double> ReadToolOrderFromReferenceDrillTape(string referenceFilePath)
|
||
{
|
||
var diameters = new List<double>();
|
||
|
||
try
|
||
{
|
||
// 检查文件是否存在
|
||
if (!System.IO.File.Exists(referenceFilePath))
|
||
{
|
||
throw new System.IO.FileNotFoundException($"参考钻带文件不存在:{referenceFilePath}");
|
||
}
|
||
|
||
// 使用与加载钻带文件相同的方式读取文件内容
|
||
var process = new Process
|
||
{
|
||
StartInfo = new ProcessStartInfo
|
||
{
|
||
FileName = "cmd.exe",
|
||
Arguments = $"/c type \"{referenceFilePath}\"",
|
||
RedirectStandardOutput = true,
|
||
UseShellExecute = false,
|
||
CreateNoWindow = true
|
||
}
|
||
};
|
||
|
||
process.Start();
|
||
string drillTapeContent = process.StandardOutput.ReadToEnd();
|
||
process.WaitForExit();
|
||
|
||
if (process.ExitCode != 0)
|
||
{
|
||
throw new InvalidOperationException($"读取参考钻带文件失败,退出代码:{process.ExitCode}");
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(drillTapeContent))
|
||
{
|
||
throw new InvalidOperationException("参考钻带文件内容为空");
|
||
}
|
||
|
||
// 解析刀具定义部分(M48到%之间的内容)
|
||
var toolPattern = @"T(\d+)C(\d+\.?\d*)";
|
||
var matches = System.Text.RegularExpressions.Regex.Matches(drillTapeContent, toolPattern);
|
||
|
||
if (matches.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("参考钻带文件中未找到刀具定义信息(TxxCxxx格式)");
|
||
}
|
||
|
||
// 创建刀具编号到直径的映射
|
||
var toolNumberToDiameter = new Dictionary<int, double>();
|
||
foreach (System.Text.RegularExpressions.Match match in matches)
|
||
{
|
||
int toolNumber = int.Parse(match.Groups[1].Value);
|
||
double diameter = double.Parse(match.Groups[2].Value);
|
||
|
||
if (toolNumberToDiameter.ContainsKey(toolNumber))
|
||
{
|
||
throw new InvalidOperationException($"参考钻带文件中存在重复的刀具编号:T{toolNumber:D2}");
|
||
}
|
||
|
||
toolNumberToDiameter[toolNumber] = diameter;
|
||
}
|
||
|
||
// 按刀具编号顺序提取直径(T01, T02, T03...)
|
||
int maxToolNumber = toolNumberToDiameter.Keys.Max();
|
||
for (int i = 1; i <= maxToolNumber; i++)
|
||
{
|
||
if (toolNumberToDiameter.ContainsKey(i))
|
||
{
|
||
diameters.Add(toolNumberToDiameter[i]);
|
||
}
|
||
}
|
||
|
||
if (diameters.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("无法从参考钻带文件中提取有效的刀具顺序");
|
||
}
|
||
}
|
||
catch (System.IO.FileNotFoundException)
|
||
{
|
||
throw;
|
||
}
|
||
catch (InvalidOperationException)
|
||
{
|
||
throw;
|
||
}
|
||
catch (System.Text.RegularExpressions.RegexMatchTimeoutException ex)
|
||
{
|
||
throw new InvalidOperationException($"解析参考钻带文件时超时:{ex.Message}", ex);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new InvalidOperationException($"读取参考钻带文件时发生未知错误:{ex.Message}", ex);
|
||
}
|
||
|
||
return diameters;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证当前刀具与参考钻带刀具的匹配性
|
||
/// </summary>
|
||
/// <param name="currentTools">当前刀具列表</param>
|
||
/// <param name="referenceDiameters">参考钻带的直径列表</param>
|
||
/// <returns>验证结果和详细信息</returns>
|
||
private (bool IsValid, string Message, List<string> Warnings) ValidateToolMatch(
|
||
ObservableCollection<ToolItem> currentTools,
|
||
List<double> referenceDiameters)
|
||
{
|
||
var warnings = new List<string>();
|
||
|
||
// 检查刀具数量是否一致
|
||
if (currentTools.Count != referenceDiameters.Count)
|
||
{
|
||
return (false,
|
||
$"刀具数量不匹配:当前钻带有{currentTools.Count}把刀具,参考钻带有{referenceDiameters.Count}把刀具",
|
||
warnings);
|
||
}
|
||
|
||
// 创建当前刀具直径的集合(考虑浮点数精度)
|
||
var currentDiameters = currentTools.Select(t => Math.Round(t.Diameter, 3)).ToHashSet();
|
||
var referenceDiametersRounded = referenceDiameters.Select(d => Math.Round(d, 3)).ToHashSet();
|
||
|
||
// 检查直径是否完全匹配
|
||
var missingInCurrent = referenceDiametersRounded.Except(currentDiameters).ToList();
|
||
var missingInReference = currentDiameters.Except(referenceDiametersRounded).ToList();
|
||
|
||
if (missingInCurrent.Any() || missingInReference.Any())
|
||
{
|
||
var message = "刀具直径不匹配:\n";
|
||
if (missingInCurrent.Any())
|
||
{
|
||
message += $"参考钻带中存在但当前钻带中不存在的刀具:{string.Join(", ", missingInCurrent)}\n";
|
||
}
|
||
if (missingInReference.Any())
|
||
{
|
||
message += $"当前钻带中存在但参考钻带中不存在的刀具:{string.Join(", ", missingInReference)}";
|
||
}
|
||
return (false, message, warnings);
|
||
}
|
||
|
||
// 检查机台码刀具位置是否一致
|
||
var currentMachineCodeTools = currentTools.Where(t => t.ToolType == ToolType.MachineCode).ToList();
|
||
var referenceMachineCodeIndices = new List<int>();
|
||
|
||
for (int i = 0; i < referenceDiameters.Count; i++)
|
||
{
|
||
double diameter = Math.Round(referenceDiameters[i], 3);
|
||
var matchingTool = currentTools.FirstOrDefault(t => Math.Round(t.Diameter, 3) == diameter);
|
||
if (matchingTool != null && matchingTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
referenceMachineCodeIndices.Add(i);
|
||
}
|
||
}
|
||
|
||
var currentMachineCodeIndices = new List<int>();
|
||
for (int i = 0; i < currentTools.Count; i++)
|
||
{
|
||
if (currentTools[i].ToolType == ToolType.MachineCode)
|
||
{
|
||
currentMachineCodeIndices.Add(i);
|
||
}
|
||
}
|
||
|
||
if (!referenceMachineCodeIndices.SequenceEqual(currentMachineCodeIndices))
|
||
{
|
||
warnings.Add("注意:机台码刀具的位置在参考钻带和当前钻带中不一致,这可能会影响加工顺序");
|
||
}
|
||
|
||
return (true, "刀具匹配验证通过", warnings);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用指定种子文件的刀序重排当前刀具
|
||
/// </summary>
|
||
public void ReorderToolsBySortSeedFile()
|
||
{
|
||
try
|
||
{
|
||
// 前置检查
|
||
if (Tools.Count == 0)
|
||
{
|
||
System.Windows.MessageBox.Show("没有可重排的刀具,请先加载钻带文件", "提示",
|
||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 1. 选择种子文件
|
||
var openFileDialog = new Microsoft.Win32.OpenFileDialog
|
||
{
|
||
Filter = "排序种子文件 (*.txt;*-sort.txt)|*.txt;*-sort.txt|所有文件 (*.*)|*.*",
|
||
Title = "选择排序种子文件"
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() != true)
|
||
return;
|
||
|
||
// 显示进度提示
|
||
var progressDialog = new System.Windows.Window
|
||
{
|
||
Title = "处理中",
|
||
Width = 300,
|
||
Height = 100,
|
||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||
Owner = System.Windows.Application.Current.MainWindow,
|
||
ResizeMode = ResizeMode.NoResize
|
||
};
|
||
|
||
var progressText = new System.Windows.Controls.TextBlock
|
||
{
|
||
Text = "正在读取种子文件...",
|
||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||
VerticalAlignment = VerticalAlignment.Center,
|
||
Margin = new Thickness(20)
|
||
};
|
||
|
||
progressDialog.Content = progressText;
|
||
|
||
// 异步处理以避免界面冻结
|
||
var progressDialogShown = false;
|
||
|
||
System.Threading.Tasks.Task.Run(() =>
|
||
{
|
||
try
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressDialog.Show();
|
||
progressDialogShown = true;
|
||
progressText.Text = "正在读取种子文件...";
|
||
});
|
||
|
||
List<double> seedDiameters;
|
||
string seedFileName = System.IO.Path.GetFileName(openFileDialog.FileName);
|
||
|
||
// 2. 读取种子文件的刀序
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressText.Text = "正在解析种子文件...";
|
||
});
|
||
|
||
seedDiameters = ReadSortFile(openFileDialog.FileName);
|
||
|
||
if (seedDiameters.Count == 0)
|
||
{
|
||
throw new InvalidOperationException("种子文件中没有找到有效的刀具直径信息");
|
||
}
|
||
|
||
// 3. 验证刀具匹配
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressText.Text = "正在验证刀具匹配...";
|
||
});
|
||
|
||
var (isValid, message, warnings) = ValidateSortSeedMatch(Tools, seedDiameters);
|
||
|
||
if (!isValid)
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressDialog.Close();
|
||
System.Windows.MessageBox.Show(message, "刀具匹配失败",
|
||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 显示警告信息(如果有)
|
||
if (warnings.Any())
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
var warningResult = System.Windows.MessageBox.Show(
|
||
$"{string.Join("\n", warnings)}\n\n是否继续重排操作?",
|
||
"警告",
|
||
MessageBoxButton.YesNo,
|
||
MessageBoxImage.Warning);
|
||
|
||
if (warningResult == MessageBoxResult.No)
|
||
{
|
||
progressDialog.Close();
|
||
return;
|
||
}
|
||
});
|
||
}
|
||
|
||
// 4. 保存原始刀具顺序用于对比
|
||
var originalTools = Tools.Select(t => new ToolItem
|
||
{
|
||
ToolNumber = t.ToolNumber,
|
||
Diameter = t.Diameter,
|
||
ToolType = t.ToolType,
|
||
ToolSuffixType = t.ToolSuffixType,
|
||
ToolCategory = t.ToolCategory,
|
||
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = t.MachineCodeCommand,
|
||
MachineCodeType = t.MachineCodeType
|
||
}).ToList();
|
||
|
||
// 5. 按照种子文件的顺序重排刀具
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressText.Text = "正在重排刀具顺序...";
|
||
SortToolsByDiameterList(Tools, seedDiameters);
|
||
});
|
||
|
||
// 6. 创建重排后的刀具列表用于确认
|
||
var reorderedTools = Tools.Select(t => new ToolItem
|
||
{
|
||
ToolNumber = t.ToolNumber,
|
||
Diameter = t.Diameter,
|
||
ToolType = t.ToolType,
|
||
ToolSuffixType = t.ToolSuffixType,
|
||
ToolCategory = t.ToolCategory,
|
||
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
|
||
MachineCodeCommand = t.MachineCodeCommand,
|
||
MachineCodeType = t.MachineCodeType
|
||
}).ToList();
|
||
|
||
// 7. 显示确认窗口
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
progressDialog.Close();
|
||
|
||
var confirmationWindow = new ToolReorderConfirmationWindow(originalTools, reorderedTools);
|
||
confirmationWindow.Owner = System.Windows.Application.Current.MainWindow;
|
||
confirmationWindow.Title = $"使用种子文件刀序确认 - {seedFileName}";
|
||
confirmationWindow.ShowDialog();
|
||
|
||
if (!confirmationWindow.IsConfirmed)
|
||
{
|
||
// 用户取消,恢复原始顺序
|
||
Tools.Clear();
|
||
foreach (var tool in originalTools)
|
||
{
|
||
Tools.Add(tool);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 8. 执行重排和重新编号
|
||
try
|
||
{
|
||
string reorderedDrillTape = ReorderAndRenumberTools(skipConfirmation: true);
|
||
DrillTapeContent = reorderedDrillTape;
|
||
|
||
System.Windows.MessageBox.Show(
|
||
$"已成功使用种子文件重排刀具顺序\n\n种子文件:{seedFileName}\n重排刀具数量:{Tools.Count}把",
|
||
"重排完成",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"重排过程中发生错误:{ex.Message}\n\n刀具顺序已更新,但钻带内容未能重新生成。",
|
||
"部分完成",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Warning);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
if (progressDialogShown)
|
||
progressDialog.Close();
|
||
|
||
System.Windows.MessageBox.Show(
|
||
$"使用种子文件重排失败:{ex.Message}",
|
||
"错误",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Error);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"启动重排操作失败:{ex.Message}",
|
||
"错误",
|
||
MessageBoxButton.OK,
|
||
MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证当前刀具与种子文件的匹配性
|
||
/// </summary>
|
||
/// <param name="currentTools">当前刀具列表</param>
|
||
/// <param name="seedDiameters">种子文件的直径列表</param>
|
||
/// <returns>验证结果和详细信息</returns>
|
||
private (bool IsValid, string Message, List<string> Warnings) ValidateSortSeedMatch(
|
||
ObservableCollection<ToolItem> currentTools,
|
||
List<double> seedDiameters)
|
||
{
|
||
var warnings = new List<string>();
|
||
|
||
// 检查刀具数量是否一致
|
||
if (currentTools.Count != seedDiameters.Count)
|
||
{
|
||
return (false,
|
||
$"刀具数量不匹配:当前钻带有{currentTools.Count}把刀具,种子文件中有{seedDiameters.Count}把刀具",
|
||
warnings);
|
||
}
|
||
|
||
// 创建当前刀具直径的集合(考虑浮点数精度)
|
||
var currentDiameters = currentTools.Select(t => Math.Round(t.Diameter, 3)).ToHashSet();
|
||
var seedDiametersRounded = seedDiameters.Select(d => Math.Round(d, 3)).ToHashSet();
|
||
|
||
// 检查直径是否完全匹配
|
||
var missingInCurrent = seedDiametersRounded.Except(currentDiameters).ToList();
|
||
var missingInSeed = currentDiameters.Except(seedDiametersRounded).ToList();
|
||
|
||
if (missingInCurrent.Any() || missingInSeed.Any())
|
||
{
|
||
var message = "刀具直径不匹配:\n";
|
||
if (missingInCurrent.Any())
|
||
{
|
||
message += $"种子文件中存在但当前钻带中不存在的刀具:{string.Join(", ", missingInCurrent)}\n";
|
||
}
|
||
if (missingInSeed.Any())
|
||
{
|
||
message += $"当前钻带中存在但种子文件中不存在的刀具:{string.Join(", ", missingInSeed)}";
|
||
}
|
||
return (false, message, warnings);
|
||
}
|
||
|
||
// 检查机台码刀具位置是否一致
|
||
var currentMachineCodeTools = currentTools.Where(t => t.ToolType == ToolType.MachineCode).ToList();
|
||
var seedMachineCodeIndices = new List<int>();
|
||
|
||
for (int i = 0; i < seedDiameters.Count; i++)
|
||
{
|
||
double diameter = Math.Round(seedDiameters[i], 3);
|
||
var matchingTool = currentTools.FirstOrDefault(t => Math.Round(t.Diameter, 3) == diameter);
|
||
if (matchingTool != null && matchingTool.ToolType == ToolType.MachineCode)
|
||
{
|
||
seedMachineCodeIndices.Add(i);
|
||
}
|
||
}
|
||
|
||
var currentMachineCodeIndices = new List<int>();
|
||
for (int i = 0; i < currentTools.Count; i++)
|
||
{
|
||
if (currentTools[i].ToolType == ToolType.MachineCode)
|
||
{
|
||
currentMachineCodeIndices.Add(i);
|
||
}
|
||
}
|
||
|
||
if (!seedMachineCodeIndices.SequenceEqual(currentMachineCodeIndices))
|
||
{
|
||
warnings.Add("注意:机台码刀具的位置在种子文件和当前钻带中不一致,这可能会影响加工顺序");
|
||
}
|
||
|
||
return (true, "刀具匹配验证通过", warnings);
|
||
}
|
||
}
|
||
} |