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
};
}
}
}