Files
AohDrllTools/DrillTapeProcessor.cs
2025-12-07 20:25:27 +08:00

680 lines
28 KiB
C#
Raw Permalink 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.Linq;
using System.Text.RegularExpressions;
namespace DrillTools.Integration
{
/// <summary>
/// 钻带处理工具集成示例
/// 展示如何将槽孔计算类集成到钻带处理工具中
/// </summary>
public class DrillTapeProcessor
{
/// <summary>
/// 处理钻带数据并计算槽孔孔数
/// </summary>
/// <param name="drillTapeContent">钻带文件内容</param>
/// <returns>处理结果</returns>
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<HoleInfo>();
var locations = new List<string>();
// 统计孔数并收集孔位坐标
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;
}
/// <summary>
/// 解析刀具信息
/// </summary>
private static List<ToolInfo> ParseTools(string drillTapeContent)
{
var tools = new List<ToolInfo>();
// 解析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;
}
/// <summary>
/// 解析钻孔信息
/// </summary>
private static List<HoleInfo> ParseHoles(string drillTapeContent, List<ToolInfo> tools)
{
var holes = new List<HoleInfo>();
// 按刀具分组解析
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<string>();
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;
}
/// <summary>
/// 生成处理报告
/// </summary>
/// <param name="result">处理结果</param>
/// <returns>格式化的报告文本</returns>
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();
}
}
/// <summary>
/// 刀具信息
/// </summary>
public class ToolInfo
{
public int Number { get; set; }
public double Diameter { get; set; }
}
/// <summary>
/// 钻孔信息
/// </summary>
public class HoleInfo
{
public int ToolNumber { get; set; }
public HoleType Type { get; set; }
public Point2D Position { get; set; }
public LineSlot Slot { get; set; }
}
/// <summary>
/// 钻孔类型
/// </summary>
public enum HoleType
{
Regular, // 普通圆孔
Slot // 槽孔
}
/// <summary>
/// 钻带处理结果
/// </summary>
public class DrillTapeResult
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public List<ToolResult> ToolResults { get; set; } = new List<ToolResult>();
/// <summary>
/// 获取总孔数
/// </summary>
public int TotalHoles => ToolResults.Sum(t => t.TotalHoles);
}
/// <summary>
/// 刀具处理结果
/// </summary>
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<string> Locations { get; set; } = new(); // 位置数据
public ToolType ToolType { get; set; }
public ToolSuffixType ToolSuffixType { get; set; }
public ToolCategory ToolCategory { get; set; }
/// <summary>
/// 机台码命令 (M97或M98)
/// </summary>
public string MachineCodeCommand { get; set; } = string.Empty;
/// <summary>
/// 机台码类型 (A*或B*)
/// </summary>
public string MachineCodeType { get; set; } = string.Empty;
}
/// <summary>
/// 钻带处理扩展方法
/// </summary>
public static class DrillTapeProcessorExtensions
{
/// <summary>
/// 生成重新排序后的钻带内容(重排并重新编号)
/// </summary>
/// <param name="originalDrillTape">原始钻带内容</param>
/// <param name="reorderedTools">重新排序的刀具列表</param>
/// <returns>重新排序后的钻带内容</returns>
public static string GenerateReorderedDrillTape(string originalDrillTape, List<ToolItem> reorderedTools)
{
// 1. 创建原始刀具编号到新编号的映射
var toolNumberMapping = new Dictionary<int, int>();
var originalToNewMapping = new Dictionary<int, int>(); // 保存原始映射关系
// 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);
}
/// <summary>
/// 使用新的刀具编号更新钻带内容
/// </summary>
/// <param name="originalDrillTape">原始钻带内容</param>
/// <param name="toolNumberMapping">刀具编号映射</param>
/// <param name="reorderedTools">重新排序的刀具列表</param>
/// <returns>更新后的钻带内容</returns>
private static string UpdateDrillTapeWithNewToolNumbers(string originalDrillTape, Dictionary<int, int> toolNumberMapping, List<ToolItem> reorderedTools)
{
var lines = originalDrillTape.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
var result = new List<string>();
// 创建新编号到刀具对象的映射,以便获取对应的坐标数据
var newNumberToToolMap = new Dictionary<int, ToolItem>();
foreach (var tool in reorderedTools)
{
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 = 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);
}
/// <summary>
/// 根据刀具编号尾号判断刀具尾号类型
/// </summary>
/// <param name="toolNumber">刀具编号</param>
/// <returns>刀具尾号类型</returns>
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
};
}
/// <summary>
/// 根据刀具尾号类型获取刀具大类
/// </summary>
/// <param name="suffixType">刀具尾号类型</param>
/// <returns>刀具大类</returns>
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
};
}
}
}