using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace DrillTools.Integration { /// /// 钻带处理工具集成示例 /// 展示如何将槽孔计算类集成到钻带处理工具中 /// public class DrillTapeProcessor { /// /// 处理钻带数据并计算槽孔孔数 /// /// 钻带文件内容 /// 处理结果 public static DrillTapeResult ProcessDrillTape(string drillTapeContent) { var result = new DrillTapeResult(); try { // 解析刀具信息 var tools = ParseTools(drillTapeContent); // 解析孔位信息 var holes = ParseHoles(drillTapeContent, tools); // 按刀具分组孔位数据 var holesByTool = holes.GroupBy(h => h.ToolNumber).ToDictionary(g => g.Key, g => g.ToList()); // 处理刀具信息 foreach (var tool in tools) { // 检查是否是机台码 (0.499孔径) bool isMachineCode = Math.Abs(tool.Diameter - 0.499) < 0.001; // 根据刀具类型设置 ToolType toolType; if (isMachineCode) { toolType = ToolType.MachineCode; } else { // 简化处理,所有非机台码都设为Regular // 实际应用中可以根据需要判断是否为槽孔 toolType = ToolType.Regular; } // 计算刀具尾号类型和大类 var toolSuffixType = ToolItem.GetToolSuffixType(tool.Diameter); var ToolCategory = ToolItem.GetToolCategory(toolSuffixType); // 获取当前刀具的孔位数据 var toolHoles = holesByTool.ContainsKey(tool.Number) ? holesByTool[tool.Number] : new List(); var locations = new List(); // 统计孔数并收集孔位坐标 int regularHoles = 0; int slotHoles = 0; int slotCount = 0; // 特殊处理机台码:分离固定孔数和实际坐标 if (isMachineCode) { // 机台码:先添加固定孔数的虚拟坐标(X0Y0) int machineCodeHoleCount = 0; foreach (var hole in toolHoles) { if (hole.Type == HoleType.Regular && hole.Position.X == 0 && hole.Position.Y == 0) { regularHoles++; machineCodeHoleCount++; // 不添加 X0Y0 到 locations,这些只是用于计数的虚拟孔 } else if (hole.Type == HoleType.Regular && (hole.Position.X != 0 || hole.Position.Y != 0)) { // 这是实际的坐标行,使用原始字符串保持格式一致 string location = !string.IsNullOrEmpty(hole.Position.OriginalString) ? hole.Position.OriginalString : $"X{hole.Position.X:F0}Y{hole.Position.Y:F0}"; locations.Add(location); } } } else { // 普通刀具和槽刀:正常处理 foreach (var hole in toolHoles) { if (hole.Type == HoleType.Regular) { regularHoles++; // Point2D 是 struct,不需要检查 null // 使用原始字符串保持格式一致 string location = !string.IsNullOrEmpty(hole.Position.OriginalString) ? hole.Position.OriginalString : $"X{hole.Position.X:F0}Y{hole.Position.Y:F0}"; locations.Add(location); } else if (hole.Type == HoleType.Slot) { slotHoles++; slotCount++; // LineSlot 是 struct,不需要检查 null,使用正确的属性名 // 使用原始字符串保持格式一致 string startLocation = !string.IsNullOrEmpty(hole.Slot.StartPoint.OriginalString) ? hole.Slot.StartPoint.OriginalString : $"X{hole.Slot.StartPoint.X:F0}Y{hole.Slot.StartPoint.Y:F0}"; string endLocation = !string.IsNullOrEmpty(hole.Slot.EndPoint.OriginalString) ? hole.Slot.EndPoint.OriginalString : $"X{hole.Slot.EndPoint.X:F0}Y{hole.Slot.EndPoint.Y:F0}"; // 直接组合成G85格式,保持原始坐标格式 string location = $"{startLocation}G85{endLocation}"; locations.Add(location); } } } // 为机台码刀具提取机台码命令和类型 string machineCodeCommand = string.Empty; string machineCodeType = string.Empty; if (isMachineCode) { // 从钻带内容中提取机台码信息 var toolPattern = $@"%.+?T{tool.Number:D2}(.*?)(?=T\d{{2}}|M30)"; var match = Regex.Match(drillTapeContent, toolPattern, RegexOptions.Singleline); if (match.Success) { string holeSection = match.Groups[1].Value; // 查找机台码命令 var machineCodePattern = @"(M97|M98),(A\*|B\*),\$S \$N"; var machineCodeMatch = Regex.Match(holeSection, machineCodePattern); if (machineCodeMatch.Success) { machineCodeCommand = machineCodeMatch.Groups[1].Value; // M97或M98 machineCodeType = machineCodeMatch.Groups[2].Value; // A*或B* // 添加日志验证机台码解析 System.Diagnostics.Debug.WriteLine($"[机台码解析] T{tool.Number:D2}: 命令={machineCodeCommand}, 类型={machineCodeType}"); } else { System.Diagnostics.Debug.WriteLine($"[机台码解析] T{tool.Number:D2}: 未找到机台码命令"); } } else { System.Diagnostics.Debug.WriteLine($"[机台码解析] T{tool.Number:D2}: 未找到机台码部分"); } } result.ToolResults.Add(new ToolResult { ToolNumber = tool.Number, Diameter = tool.Diameter, TotalHoles = regularHoles + slotHoles, RegularHoles = regularHoles, SlotHoles = slotHoles, ToolType = toolType, SlotCount = slotCount, Locations = locations, ToolSuffixType = toolSuffixType, ToolCategory = ToolCategory, MachineCodeCommand = machineCodeCommand, MachineCodeType = machineCodeType }); } result.Success = true; result.Message = "钻带处理成功"; } catch (Exception ex) { result.Success = false; result.Message = $"钻带处理失败: {ex.Message}"; } return result; } /// /// 解析刀具信息 /// private static List ParseTools(string drillTapeContent) { var tools = new List(); // 解析M48到%之间的刀具信息 var toolPattern = @"T(\d+)C(\d+\.?\d*)"; var matches = Regex.Matches(drillTapeContent, toolPattern); foreach (Match match in matches) { int toolNumber = int.Parse(match.Groups[1].Value); double diameter = double.Parse(match.Groups[2].Value); tools.Add(new ToolInfo { Number = toolNumber, Diameter = diameter }); } return tools; } /// /// 解析钻孔信息 /// private static List ParseHoles(string drillTapeContent, List tools) { var holes = new List(); // 按刀具分组解析 foreach (var tool in tools) { // 检查是否是机台码 (0.499孔径) if (Math.Abs(tool.Diameter - 0.499) < 0.001) { // 解析机台码部分(在%符号之后查找) var toolPattern = $@"%.+?T{tool.Number:D2}(.*?)(?=T\d{{2}}|M30)"; var match = Regex.Match(drillTapeContent, toolPattern, RegexOptions.Singleline); if (match.Success) { string holeSection = match.Groups[1].Value; // 查找机台码命令 var machineCodePattern = @"(M97|M98),(A\*|B\*),\$S \$N"; var machineCodeMatch = Regex.Match(holeSection, machineCodePattern); if (machineCodeMatch.Success) { string command = machineCodeMatch.Groups[1].Value; // M97或M98 string codeType = machineCodeMatch.Groups[2].Value; // A*或B* // 根据机台码类型确定孔数 int holeCount = (codeType == "A*") ? 57 : 58; // 查找机台码坐标行 var coordinatePattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)"; var coordinateMatches = Regex.Matches(holeSection, coordinatePattern); // 添加机台码孔信息 for (int i = 0; i < holeCount; i++) { holes.Add(new HoleInfo { ToolNumber = tool.Number, Type = HoleType.Regular, Position = new Point2D(0, 0) // 机台码不需要实际坐标 }); } // 这样可以确保坐标数据被正确保存到 ToolResult.Locations 中 foreach (Match coordMatch in coordinateMatches) { // 解析坐标 double x = double.Parse(coordMatch.Groups[1].Value); double y = double.Parse(coordMatch.Groups[2].Value); // 保存原始坐标字符串 string originalCoordString = coordMatch.Value; // 添加一个特殊的孔位信息来保存实际坐标 holes.Add(new HoleInfo { ToolNumber = tool.Number, Type = HoleType.Regular, Position = new Point2D(x, y, originalCoordString) }); } } } } else { // 查找当前刀具的钻孔部分(只在%符号之后查找) var toolPattern = $@"%.+?T{tool.Number:D2}(.*?)(?=T\d{{2}}|M30)"; var match = Regex.Match(drillTapeContent, toolPattern, RegexOptions.Singleline); if (match.Success) { string holeSection = match.Groups[1].Value; // 先解析槽孔,并记录槽孔的起始坐标 var slotPattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)G85X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)"; var slotMatches = Regex.Matches(holeSection, slotPattern); var slotStartPositions = new System.Collections.Generic.HashSet(); foreach (Match slotMatch in slotMatches) { var slot = SlotHoleCalculator.ParseLineSlotFromG85(slotMatch.Value, tool.Diameter); holes.Add(new HoleInfo { ToolNumber = tool.Number, Type = HoleType.Slot, Slot = slot }); // 记录槽孔的起始坐标,以便后续排除 string startPos = $"X{slotMatch.Groups[1].Value}Y{slotMatch.Groups[2].Value}"; slotStartPositions.Add(startPos); } // 解析普通圆孔(排除槽孔的起始坐标) var regularHolePattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)"; var regularMatches = Regex.Matches(holeSection, regularHolePattern); foreach (Match regularMatch in regularMatches) { // 排除槽孔的起始坐标 if (!slotStartPositions.Contains(regularMatch.Value)) { holes.Add(new HoleInfo { ToolNumber = tool.Number, Type = HoleType.Regular, Position = Point2D.ParseFromDrillString(regularMatch.Value) }); } } // 解析槽孔(已在上面处理) } } } return holes; } /// /// 生成处理报告 /// /// 处理结果 /// 格式化的报告文本 public static string GenerateReport(DrillTapeResult result) { var report = new System.Text.StringBuilder(); report.AppendLine("钻带处理报告"); report.AppendLine("============"); report.AppendLine($"处理状态: {(result.Success ? "成功" : "失败")}"); report.AppendLine($"处理信息: {result.Message}"); report.AppendLine(); if (result.Success) { report.AppendLine("刀具统计:"); report.AppendLine("刀具\t孔径(mm)\t类型\t\t尾号类型\t大类\t\t普通孔数\t槽孔数\t槽孔个数\t总孔数"); report.AppendLine("================================================================================"); foreach (var tool in result.ToolResults) { string toolTypeDisplay = tool.ToolType switch { ToolType.Regular => "圆孔", ToolType.Slot => "槽孔", ToolType.MachineCode => "机台码", _ => "未知" }; string suffixTypeDisplay = tool.ToolSuffixType switch { ToolSuffixType.Drill => "钻针", ToolSuffixType.Slot => "槽刀", ToolSuffixType.EASlot => "EA型槽刀", ToolSuffixType.DustSlot => "粉尘刀", ToolSuffixType.DeburrSlot => "去毛刺刀", ToolSuffixType.NonStandard => "非标刀", ToolSuffixType.EASlot2 => "EA型槽刀", ToolSuffixType.Special => "特殊刀具", _ => "未知" }; string categoryDisplay = tool.ToolCategory switch { ToolCategory.Drill => "钻针", ToolCategory.Slot => "槽刀", ToolCategory.EA => "EA刀", ToolCategory.NonStandard => "非标刀", ToolCategory.Special => "特殊刀", _ => "未知" }; report.AppendLine($"T{tool.ToolNumber:D2}\t{tool.Diameter:F3}\t\t{toolTypeDisplay}\t{suffixTypeDisplay}\t{categoryDisplay}\t{tool.RegularHoles}\t\t{tool.SlotHoles}\t{tool.SlotCount}\t\t{tool.TotalHoles}"); } report.AppendLine(); report.AppendLine($"总计: {result.ToolResults.Count} 把刀具"); report.AppendLine($"总孔数: {result.TotalHoles}"); } return report.ToString(); } } /// /// 刀具信息 /// public class ToolInfo { public int Number { get; set; } public double Diameter { get; set; } } /// /// 钻孔信息 /// public class HoleInfo { public int ToolNumber { get; set; } public HoleType Type { get; set; } public Point2D Position { get; set; } public LineSlot Slot { get; set; } } /// /// 钻孔类型 /// public enum HoleType { Regular, // 普通圆孔 Slot // 槽孔 } /// /// 钻带处理结果 /// public class DrillTapeResult { public bool Success { get; set; } public string Message { get; set; } = string.Empty; public List ToolResults { get; set; } = new List(); /// /// 获取总孔数 /// public int TotalHoles => ToolResults.Sum(t => t.TotalHoles); } /// /// 刀具处理结果 /// public class ToolResult { public int ToolNumber { get; set; } public double Diameter { get; set; } public int TotalHoles { get; set; } public int RegularHoles { get; set; } public int SlotHoles { get; set; } public int SlotCount { get; set; } // 槽孔个数(不是钻孔数量) public List Locations { get; set; } = new(); // 位置数据 public ToolType ToolType { get; set; } public ToolSuffixType ToolSuffixType { get; set; } public ToolCategory ToolCategory { get; set; } /// /// 机台码命令 (M97或M98) /// public string MachineCodeCommand { get; set; } = string.Empty; /// /// 机台码类型 (A*或B*) /// public string MachineCodeType { get; set; } = string.Empty; } /// /// 钻带处理扩展方法 /// public static class DrillTapeProcessorExtensions { /// /// 生成重新排序后的钻带内容(重排并重新编号) /// /// 原始钻带内容 /// 重新排序的刀具列表 /// 重新排序后的钻带内容 public static string GenerateReorderedDrillTape(string originalDrillTape, List reorderedTools) { // 1. 创建原始刀具编号到新编号的映射 var toolNumberMapping = new Dictionary(); var originalToNewMapping = new Dictionary(); // 保存原始映射关系 // 2. 按当前列表顺序重新编号(T01, T02, T03...) int newToolNumber = 1; foreach (var tool in reorderedTools.ToList()) { // 机台码刀具不允许重新编号 if (tool.ToolType != ToolType.MachineCode) { originalToNewMapping[tool.ToolNumber] = newToolNumber; toolNumberMapping[tool.ToolNumber] = newToolNumber; tool.ToolNumber = newToolNumber++; } else { // 机台码刀具保持原始编号,但也要加入映射 toolNumberMapping[tool.ToolNumber] = tool.ToolNumber; } } // 3. 使用新的刀具编号更新钻带内容 return UpdateDrillTapeWithNewToolNumbers(originalDrillTape, toolNumberMapping, reorderedTools); } /// /// 使用新的刀具编号更新钻带内容 /// /// 原始钻带内容 /// 刀具编号映射 /// 重新排序的刀具列表 /// 更新后的钻带内容 private static string UpdateDrillTapeWithNewToolNumbers(string originalDrillTape, Dictionary toolNumberMapping, List reorderedTools) { var lines = originalDrillTape.Split(new[] { '\r', '\n' }, StringSplitOptions.None); var result = new List(); // 创建新编号到刀具对象的映射,以便获取对应的坐标数据 var newNumberToToolMap = new Dictionary(); foreach (var tool in reorderedTools) { 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 = reorderedTools.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 = reorderedTools.OrderBy(t => t.ToolNumber).ToList(); foreach (var tool in sortedToolsForData) { // 添加刀具切换行 result.Add($"T{tool.ToolNumber:D2}"); // 添加该刀具对应的所有坐标数据 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"); // 添加机台码的坐标数据 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); } /// /// 根据刀具编号尾号判断刀具尾号类型 /// /// 刀具编号 /// 刀具尾号类型 private static ToolSuffixType GetToolSuffixType(int toolNumber) { int suffix = toolNumber % 10; return suffix switch { 0 => ToolSuffixType.Drill, 1 => ToolSuffixType.Slot, 2 => ToolSuffixType.EASlot, 3 => ToolSuffixType.DustSlot, 4 => ToolSuffixType.DeburrSlot, 5 => ToolSuffixType.NonStandard, 6 => ToolSuffixType.EASlot2, 7 => ToolSuffixType.Special, _ => ToolSuffixType.NonStandard }; } /// /// 根据刀具尾号类型获取刀具大类 /// /// 刀具尾号类型 /// 刀具大类 private static ToolCategory GetToolCategory(ToolSuffixType suffixType) { return suffixType switch { ToolSuffixType.Drill => ToolCategory.Drill, ToolSuffixType.Slot or ToolSuffixType.DustSlot or ToolSuffixType.DeburrSlot => ToolCategory.Slot, ToolSuffixType.EASlot or ToolSuffixType.EASlot2 => ToolCategory.EA, ToolSuffixType.NonStandard => ToolCategory.NonStandard, ToolSuffixType.Special => ToolCategory.Special, _ => ToolCategory.NonStandard }; } } }