Files
AohDrllTools/MainWindowViewModel.cs
Mr.Xia 5e8b989add 刀具日志增强与PCB钻孔数据大规模更新
本次提交包含以下主要变更:

- 增强了刀具类型、尾号类型及最小直径计算的调试日志,便于问题追踪和数据分析。
- 修正了最小槽刀直径的计算逻辑,EA型槽刀单独处理,提升了判定准确性。
- 针对特殊直径的刀具类型判定增加了专门处理和日志输出。
- 删除了 General_sort.txt 和 s40024079g0-a2-cs-jp-sort.txt 文件内容。
- n40032386g0-a2-cs-jp.drl 文件新增了大量PCB钻孔、槽孔(G85)、外形轮廓等数控加工数据,完善了刀具分组与程序控制指令,满足PCB生产自动化需求。
2025-12-31 15:09:14 +08:00

2403 lines
98 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
// 添加调试日志
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($"=== 计算完成 ===");
}
/// <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);
}
}
}