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 { /// /// 主窗口视图模型 /// public class MainWindowViewModel : INotifyPropertyChanged { private ObservableCollection _tools = new(); private ObservableCollection _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; /// /// 刀具列表 /// public ObservableCollection Tools { get => _tools; set => SetProperty(ref _tools, value); } /// /// 选中的刀具 /// public ToolItem? SelectedTool { get => _selectedTool; set { if (SetProperty(ref _selectedTool, value)) { UpdateMoveButtonsState(); } } } /// /// 钻带内容 /// public string DrillTapeContent { get => _drillTapeContent; set => SetProperty(ref _drillTapeContent, value); } /// /// 是否可以上移 /// public bool CanMoveUp { get => _canMoveUp; set => SetProperty(ref _canMoveUp, value); } /// /// 是否可以下移 /// public bool CanMoveDown { get => _canMoveDown; set => SetProperty(ref _canMoveDown, value); } /// /// 原始文件路径 /// 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; } } } } /// /// 原始刀具顺序集合(按文件中的出现顺序) /// public ObservableCollection OriginalTools { get => _originalTools; set => SetProperty(ref _originalTools, value); } /// /// 是否有原始文件 /// public bool HasOriginalFile => !string.IsNullOrEmpty(OriginalFilePath); /// /// 窗口是否置顶 /// public bool IsTopmost { get => _isTopmost; set { if (SetProperty(ref _isTopmost, value)) { // 当IsTopmost改变时,通知TopmostButtonText属性也已更改 OnPropertyChanged(nameof(TopmostButtonText)); } } } /// /// 置顶按钮显示文本 /// public string TopmostButtonText => IsTopmost ? "取消置顶" : "窗口置顶"; /// /// 文件名(不包含后缀) /// public string FileNameWithoutExtension { get => _fileNameWithoutExtension; set => SetProperty(ref _fileNameWithoutExtension, value); } /// /// 最小钻咀直径 /// public double MinDrillDiameter { get => _minDrillDiameter; set => SetProperty(ref _minDrillDiameter, value); } /// /// 最小槽刀直径 /// public double MinSlotDiameter { get => _minSlotDiameter; set => SetProperty(ref _minSlotDiameter, value); } /// /// 最小EA刀直径 /// public double MinEADiameter { get => _minEADiameter; set => SetProperty(ref _minEADiameter, value); } /// /// 从钻带内容加载刀具信息 /// /// 钻带内容 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() }; // 如果是机台码,使用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(); // 按原始顺序处理坐标行 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(), 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); } } /// /// 重新排序刀具 /// /// 原索引 /// 新索引 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); } /// /// 上移选中的刀具 /// 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); } } /// /// 下移选中的刀具 /// 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); } } /// /// 更新上移/下移按钮的状态 /// 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}"); } /// /// 保存刀具顺序并应用到钻带处理 /// /// 重新排序后的钻带内容 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); } } /// /// 重排刀序并重新编号 /// /// 是否直接应用到文件 /// 是否跳过确认窗口 /// 重排后的钻带内容 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(t.HoleLocations ?? new List()), MachineCodeCommand = t.MachineCodeCommand, MachineCodeType = t.MachineCodeType }).ToList(); // 1. 创建原始刀具编号到新编号的映射 // 创建重排后的刀具列表(基于用户已调整的顺序) // 注意:Tools 列表已经反映了用户通过拖放操作调整后的顺序 var reorderedTools = new List(); reorderedTools = Tools.Select(t => new ToolItem { ToolNumber = t.ToolNumber, Diameter = t.Diameter, ToolType = t.ToolType, ToolSuffixType = t.ToolSuffixType, ToolCategory = t.ToolCategory, HoleLocations = new List(t.HoleLocations ?? new List()), 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(); var originalToNewMapping = new Dictionary(); // 保存原始映射关系 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; } /// /// 使用新的刀具编号更新钻带内容 /// /// 原始钻带内容 /// 刀具编号映射 /// 更新后的钻带内容 private string UpdateDrillTapeWithNewToolNumbers(string originalDrillTape, Dictionary toolNumberMapping) { var lines = originalDrillTape.Split(new[] { '\r', '\n' }, StringSplitOptions.None); var result = new List(); // 创建新编号到刀具对象的映射,以便获取对应的坐标数据 var newNumberToToolMap = new Dictionary(); foreach (var tool in Tools) { if (toolNumberMapping.ContainsValue(tool.ToolNumber)) { newNumberToToolMap[tool.ToolNumber] = tool; } } // 首先解析原始钻带中的刀具定义行,保存参数信息 var originalToolDefinitions = new Dictionary(); var headerLines = new List(); 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); } /// /// 加载示例数据 /// 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 { "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 { "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 { "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 { "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 { "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 { "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(tool.HoleLocations ?? new List()), 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(ref T field, T value, [CallerMemberName] string? propertyName = null) { if (EqualityComparer.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一致" : "✗ 存在不一致的结果")}"); } /// /// 显示刀具详情窗口 /// /// 要显示详情的刀具 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); } } /// /// 测试重排刀序功能 /// 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(), reorderedOrder.ToList()); Console.WriteLine("\n重排刀序功能测试完成!"); } /// /// 验证坐标绑定关系 /// private void VerifyCoordinateBinding(List originalOrder, List reorderedOrder) { Console.WriteLine("\n=== 验证坐标绑定关系 ==="); // 创建直径到坐标的映射 var originalDiameterToLocation = new Dictionary(); 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 ? "✓ 全部正确" : "✗ 存在问题")}"); } /// /// 打开文件资源管理器并选中指定文件 /// /// 要选中的文件路径 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); } } /// /// 生成刀具直径列表文件 /// /// 原始文件路径 /// 刀具列表 private void GenerateToolDiameterListFile(string originalFilePath, ObservableCollection 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}"); } } /// /// 生成通用排序种子文件 /// /// 刀具列表 public void GenerateGeneralSortSeedFile(ObservableCollection 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); } } /// /// 检查并应用-sort.txt文件中的刀具排序 /// /// 原始文件路径 /// 当前刀具列表 private void CheckAndApplySortFile(string originalFilePath, ObservableCollection 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 sortDiameters = new List(); 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}"); } } /// /// 读取-sort.txt文件内容并解析直径列表 /// /// -sort.txt文件路径 /// 直径列表 private List ReadSortFile(string sortFilePath) { var diameters = new List(); 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; } /// /// 根据直径列表排序刀具 /// /// 刀具列表 /// 直径列表(目标顺序) private void SortToolsByDiameterList(ObservableCollection tools, List diameterList) { // 创建直径到刀具的映射 var diameterToTools = new Dictionary>(); foreach (var tool in tools) { double roundedDiameter = Math.Round(tool.Diameter, 3); if (!diameterToTools.ContainsKey(roundedDiameter)) { diameterToTools[roundedDiameter] = new List(); } diameterToTools[roundedDiameter].Add(tool); } // 按照直径列表的顺序重新排列刀具 var sortedTools = new List(); 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); } } /// /// 切换窗口置顶状态 /// public void ToggleTopmost() { IsTopmost = !IsTopmost; } /// /// 更新最小直径信息 /// private void UpdateMinDiameterInfo() { // 初始化为最大值,以便找到最小值 MinDrillDiameter = double.MaxValue; MinSlotDiameter = double.MaxValue; MinEADiameter = double.MaxValue; // 添加调试日志 System.Diagnostics.Debug.WriteLine("=== 开始计算最小直径信息 ==="); foreach (var tool in Tools) { // 添加详细的刀具信息日志 System.Diagnostics.Debug.WriteLine($"[刀具分析] T{tool.ToolNumber:D2}: 直径={tool.Diameter:F3}, 类型={tool.ToolType}, 尾号类型={tool.ToolSuffixType}, 大类={tool.ToolCategory}"); // 根据刀具类型分类计算最小直径 if (tool.ToolType == ToolType.Regular) { // 普通钻咀 if (tool.Diameter < MinDrillDiameter) MinDrillDiameter = tool.Diameter; System.Diagnostics.Debug.WriteLine($"[钻咀] 当前最小钻咀直径: {MinDrillDiameter:F3}"); } else if (tool.ToolType == ToolType.Slot) { // 槽刀 - 修复:只有真正的槽刀(非EA型槽刀)才计入最小槽刀直径 // EA型槽刀不应该计入最小槽刀直径的计算 if (tool.ToolSuffixType != ToolSuffixType.EASlot && tool.ToolSuffixType != ToolSuffixType.EASlot2) { if (tool.Diameter < MinSlotDiameter) MinSlotDiameter = tool.Diameter; System.Diagnostics.Debug.WriteLine($"[槽刀] T{tool.ToolNumber:D2}是真正的槽刀,当前最小槽刀直径: {MinSlotDiameter:F3}"); } else { System.Diagnostics.Debug.WriteLine($"[EA刀] T{tool.ToolNumber:D2}是EA型槽刀,不计入最小槽刀直径计算"); } // 检查是否是EA型槽刀(尾号为2或6) if ((tool.ToolSuffixType == ToolSuffixType.EASlot || tool.ToolSuffixType == ToolSuffixType.EASlot2) && tool.Diameter < MinEADiameter) MinEADiameter = tool.Diameter; // 特别标记EA型槽刀 if (tool.ToolSuffixType == ToolSuffixType.EASlot || tool.ToolSuffixType == ToolSuffixType.EASlot2) { System.Diagnostics.Debug.WriteLine($"[EA刀] T{tool.ToolNumber:D2}是EA型槽刀,直径={tool.Diameter:F3}"); } } } // 如果没有找到对应的刀具类型,将值设为0 if (MinDrillDiameter == double.MaxValue) MinDrillDiameter = 0; if (MinSlotDiameter == double.MaxValue) MinSlotDiameter = 0; if (MinEADiameter == double.MaxValue) MinEADiameter = 0; System.Diagnostics.Debug.WriteLine($"=== 最终结果 ==="); System.Diagnostics.Debug.WriteLine($"最小钻咀: {MinDrillDiameter:F3}"); System.Diagnostics.Debug.WriteLine($"最小槽刀: {MinSlotDiameter:F3}"); System.Diagnostics.Debug.WriteLine($"最小EA刀: {MinEADiameter:F3}"); System.Diagnostics.Debug.WriteLine($"=== 计算完成 ==="); } /// /// 测试使用参考钻带重排功能 /// 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}"); } } /// /// 创建模拟的参考钻带内容 /// /// 刀具列表 /// 模拟的钻带内容 private string CreateMockReferenceDrillTape(List 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(); } /// /// 使用参考钻带的刀序重排当前刀具 /// 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 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(t.HoleLocations ?? new List()), 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(t.HoleLocations ?? new List()), 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); } } /// /// 从参考钻带文件读取刀具顺序 /// /// 参考钻带文件路径 /// 按顺序排列的刀具直径列表 private List ReadToolOrderFromReferenceDrillTape(string referenceFilePath) { var diameters = new List(); 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(); 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; } /// /// 验证当前刀具与参考钻带刀具的匹配性 /// /// 当前刀具列表 /// 参考钻带的直径列表 /// 验证结果和详细信息 private (bool IsValid, string Message, List Warnings) ValidateToolMatch( ObservableCollection currentTools, List referenceDiameters) { var warnings = new List(); // 检查刀具数量是否一致 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(); 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(); 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); } /// /// 使用指定种子文件的刀序重排当前刀具 /// 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 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(t.HoleLocations ?? new List()), 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(t.HoleLocations ?? new List()), 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); } } /// /// 验证当前刀具与种子文件的匹配性 /// /// 当前刀具列表 /// 种子文件的直径列表 /// 验证结果和详细信息 private (bool IsValid, string Message, List Warnings) ValidateSortSeedMatch( ObservableCollection currentTools, List seedDiameters) { var warnings = new List(); // 检查刀具数量是否一致 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(); 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(); 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); } } }