添加项目文件。
This commit is contained in:
89
App.xaml
Normal file
89
App.xaml
Normal file
@@ -0,0 +1,89 @@
|
||||
<Application x:Class="DrillTools.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:DrillTools"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<!-- 布尔值到可见性转换器 -->
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<!-- 全局字体大小定义 -->
|
||||
<sys:Double x:Key="DefaultFontSize">14</sys:Double>
|
||||
<sys:Double x:Key="LargeFontSize">16</sys:Double>
|
||||
<sys:Double x:Key="SmallFontSize">12</sys:Double>
|
||||
<sys:Double x:Key="CodeFontSize">12</sys:Double>
|
||||
|
||||
<!-- 默认字体族 -->
|
||||
<FontFamily x:Key="DefaultFontFamily">Microsoft YaHei UI</FontFamily>
|
||||
<FontFamily x:Key="CodeFontFamily">Consolas</FontFamily>
|
||||
|
||||
<!-- 基础文本样式 -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
</Style>
|
||||
|
||||
<!-- 按钮样式 -->
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="Padding" Value="8,4"/>
|
||||
<Setter Property="MinHeight" Value="30"/>
|
||||
</Style>
|
||||
|
||||
<!-- GroupBox样式 -->
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="FontSize" Value="{StaticResource LargeFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
<Setter Property="Margin" Value="8"/>
|
||||
<Setter Property="Padding" Value="8"/>
|
||||
</Style>
|
||||
|
||||
<!-- TextBox样式 -->
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="Padding" Value="8"/>
|
||||
</Style>
|
||||
|
||||
<!-- 代码TextBox样式(用于钻带内容显示) -->
|
||||
<Style x:Key="CodeTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
|
||||
<Setter Property="FontFamily" Value="{StaticResource CodeFontFamily}"/>
|
||||
<Setter Property="FontSize" Value="{StaticResource CodeFontSize}"/>
|
||||
</Style>
|
||||
|
||||
<!-- ListView样式 -->
|
||||
<Style TargetType="ListView">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
</Style>
|
||||
|
||||
<!-- ListViewItem样式 -->
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="8,4"/>
|
||||
<Setter Property="MinHeight" Value="30"/>
|
||||
</Style>
|
||||
|
||||
<!-- ToolBar样式 -->
|
||||
<Style TargetType="ToolBar">
|
||||
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
</Style>
|
||||
|
||||
<!-- StatusBar样式 -->
|
||||
<Style TargetType="StatusBar">
|
||||
<Setter Property="FontSize" Value="{StaticResource SmallFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
</Style>
|
||||
|
||||
<!-- StatusBarItem样式 -->
|
||||
<Style TargetType="StatusBarItem">
|
||||
<Setter Property="FontSize" Value="{StaticResource SmallFontSize}"/>
|
||||
<Setter Property="FontFamily" Value="{StaticResource DefaultFontFamily}"/>
|
||||
</Style>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
24
App.xaml.cs
Normal file
24
App.xaml.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : System.Windows.Application
|
||||
{
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
// 运行孔位数据功能测试
|
||||
//MainWindowViewModel.TestHoleLocationsFunctionality();
|
||||
|
||||
// 创建并显示主窗口
|
||||
//MainWindow mainWindow = new MainWindow();
|
||||
//mainWindow.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
AssemblyInfo.cs
Normal file
10
AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
79
BackupTest.cs
Normal file
79
BackupTest.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 备份功能测试类
|
||||
/// </summary>
|
||||
public static class BackupTest
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试备份功能
|
||||
/// </summary>
|
||||
public static void TestBackupFunctionality()
|
||||
{
|
||||
Console.WriteLine("=== 备份功能测试 ===");
|
||||
|
||||
// 创建测试文件
|
||||
string testFilePath = "test_file.drl";
|
||||
string testContent = "M48\nT01C1.000\n%\nT01\nX1000Y1000\nM30";
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 创建原始测试文件
|
||||
File.WriteAllText(testFilePath, testContent);
|
||||
Console.WriteLine("✓ 创建测试文件成功");
|
||||
|
||||
// 2. 第一次备份(应该直接创建.bak文件)
|
||||
string backupPath1 = testFilePath + ".bak";
|
||||
if (File.Exists(backupPath1))
|
||||
File.Delete(backupPath1);
|
||||
|
||||
File.Copy(testFilePath, backupPath1);
|
||||
Console.WriteLine("✓ 第一次备份成功");
|
||||
|
||||
// 3. 模拟第二次备份(检测到.bak文件已存在)
|
||||
if (File.Exists(backupPath1))
|
||||
{
|
||||
Console.WriteLine("✓ 检测到备份文件已存在");
|
||||
|
||||
// 模拟用户选择创建时间戳备份
|
||||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
string timestampBackupPath = $"{testFilePath}.{timestamp}.bak";
|
||||
File.Copy(testFilePath, timestampBackupPath);
|
||||
Console.WriteLine($"✓ 创建时间戳备份成功: {Path.GetFileName(timestampBackupPath)}");
|
||||
}
|
||||
|
||||
// 4. 验证备份文件内容
|
||||
if (File.Exists(backupPath1) && File.ReadAllText(backupPath1) == testContent)
|
||||
{
|
||||
Console.WriteLine("✓ 备份文件内容验证成功");
|
||||
}
|
||||
|
||||
// 5. 清理测试文件
|
||||
File.Delete(testFilePath);
|
||||
if (File.Exists(backupPath1))
|
||||
File.Delete(backupPath1);
|
||||
|
||||
// 删除时间戳备份文件
|
||||
var timestampFiles = Directory.GetFiles(".", "*.bak");
|
||||
foreach (var file in timestampFiles)
|
||||
{
|
||||
if (file.Contains("test_file.") && file.Contains(".bak"))
|
||||
{
|
||||
File.Delete(file);
|
||||
Console.WriteLine($"✓ 清理测试文件: {Path.GetFileName(file)}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("=== 备份功能测试完成 ===");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"✗ 测试失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
142
CoordinateFormatTest.cs
Normal file
142
CoordinateFormatTest.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DrillTools;
|
||||
using DrillTools.Integration;
|
||||
|
||||
namespace DrillTools.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// 坐标格式保留测试类
|
||||
/// 用于验证修改后的代码能正确保留原始坐标格式
|
||||
/// </summary>
|
||||
public class CoordinateFormatTest
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试坐标格式保留功能
|
||||
/// </summary>
|
||||
public static void TestCoordinateFormatPreservation()
|
||||
{
|
||||
Console.WriteLine("=== 坐标格式保留测试 ===");
|
||||
|
||||
// 测试用例:各种格式的坐标
|
||||
var testCases = new[]
|
||||
{
|
||||
"X-265000Y013250", // 负坐标,前导零
|
||||
"X-265000Y008250", // 负坐标,前导零
|
||||
"X075392Y559511", // 正坐标,前导零
|
||||
"X017500Y519500G85X018500Y519500", // 槽孔坐标
|
||||
"X-238500Y519500G85X-237500Y519500" // 负坐标槽孔
|
||||
};
|
||||
|
||||
// 创建测试钻带内容
|
||||
string drillTapeContent = CreateTestDrillTape(testCases);
|
||||
|
||||
// 解析钻带
|
||||
var result = DrillTapeProcessor.ProcessDrillTape(drillTapeContent);
|
||||
|
||||
// 验证结果
|
||||
if (result.Success)
|
||||
{
|
||||
Console.WriteLine("钻带解析成功!");
|
||||
|
||||
// 检查每个工具的坐标输出
|
||||
foreach (var toolResult in result.ToolResults)
|
||||
{
|
||||
Console.WriteLine($"\n工具 T{toolResult.ToolNumber:D2} (直径: {toolResult.Diameter:F3}mm):");
|
||||
Console.WriteLine($"坐标数量: {toolResult.Locations.Count}");
|
||||
|
||||
// 显示前5个坐标作为示例
|
||||
int displayCount = Math.Min(5, toolResult.Locations.Count);
|
||||
for (int i = 0; i < displayCount; i++)
|
||||
{
|
||||
string original = i < testCases.Length ? testCases[i] : "N/A";
|
||||
string parsed = toolResult.Locations[i];
|
||||
Console.WriteLine($" 原始: {original}");
|
||||
Console.WriteLine($" 解析: {parsed}");
|
||||
Console.WriteLine($" 匹配: {(original == parsed ? "✓" : "✗")}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"钻带解析失败: {result.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建测试钻带内容
|
||||
/// </summary>
|
||||
private static string CreateTestDrillTape(string[] coordinates)
|
||||
{
|
||||
var drillTape = new System.Text.StringBuilder();
|
||||
|
||||
// 添加头部
|
||||
drillTape.AppendLine("M48");
|
||||
drillTape.AppendLine("METRIC");
|
||||
drillTape.AppendLine("VER,1");
|
||||
drillTape.AppendLine("FMAT,2");
|
||||
|
||||
// 添加刀具定义
|
||||
drillTape.AppendLine("T01C1.000");
|
||||
drillTape.AppendLine("T02C2.000");
|
||||
|
||||
// 添加结束标记
|
||||
drillTape.AppendLine("%");
|
||||
|
||||
// 添加刀具1的坐标
|
||||
drillTape.AppendLine("T01");
|
||||
for (int i = 0; i < Math.Min(3, coordinates.Length); i++)
|
||||
{
|
||||
// 只添加普通坐标(不含G85)
|
||||
if (!coordinates[i].Contains("G85"))
|
||||
{
|
||||
drillTape.AppendLine(coordinates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加刀具2的坐标(槽孔)
|
||||
drillTape.AppendLine("T02");
|
||||
for (int i = 3; i < coordinates.Length; i++)
|
||||
{
|
||||
// 添加槽孔坐标(含G85)
|
||||
if (coordinates[i].Contains("G85"))
|
||||
{
|
||||
drillTape.AppendLine(coordinates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加结束标记
|
||||
drillTape.AppendLine("M30");
|
||||
|
||||
return drillTape.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试Point2D的原始字符串保留功能
|
||||
/// </summary>
|
||||
public static void TestPoint2DOriginalString()
|
||||
{
|
||||
Console.WriteLine("\n=== Point2D原始字符串测试 ===");
|
||||
|
||||
// 测试用例
|
||||
var testCoordinates = new[]
|
||||
{
|
||||
"X-265000Y013250",
|
||||
"X075392Y559511",
|
||||
"X000123Y000456"
|
||||
};
|
||||
|
||||
foreach (var coord in testCoordinates)
|
||||
{
|
||||
var point = Point2D.ParseFromDrillString(coord);
|
||||
|
||||
Console.WriteLine($"原始坐标: {coord}");
|
||||
Console.WriteLine($"解析后X: {point.X:F3}, Y: {point.Y:F3}");
|
||||
Console.WriteLine($"原始字符串: {point.OriginalString}");
|
||||
Console.WriteLine($"字符串匹配: {(coord == point.OriginalString ? "✓" : "✗")}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
DebugTest.cs
Normal file
141
DebugTest.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using DrillTools.Integration;
|
||||
|
||||
namespace DrillTools.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// 调试测试程序,用于验证孔数计算问题
|
||||
/// </summary>
|
||||
public class DebugTest
|
||||
{
|
||||
public static void RunTest()
|
||||
{
|
||||
Console.WriteLine("=== 调试测试:孔数计算问题 ===");
|
||||
|
||||
// 使用用户提供的钻带数据
|
||||
string 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.451H04000Z-0.200S040.00F030.0U0900.0
|
||||
T05C1.153H05000Z-0.200S040.00F030.0U0900.0
|
||||
T06C0.499
|
||||
%
|
||||
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-069659Y016450G85X-094159Y016450
|
||||
X-181341Y195550G85X-156841Y195550
|
||||
X-069659Y210450G85X-094159Y210450
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
X-069659Y404450G85X-094159Y404450
|
||||
X-181341Y583550G85X-156841Y583550
|
||||
T05
|
||||
X-069659Y016450G85X-094159Y016450
|
||||
X-181341Y195550G85X-156841Y195550
|
||||
X-069659Y210450G85X-094159Y210450
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
X-069659Y404450G85X-094159Y404450
|
||||
X-181341Y583550G85X-156841Y583550
|
||||
T06
|
||||
M97,A*,$S $N
|
||||
X-194000Y002000
|
||||
M30";
|
||||
|
||||
// 处理钻带
|
||||
var result = DrillTapeProcessor.ProcessDrillTape(drillTapeContent);
|
||||
|
||||
// 输出结果
|
||||
Console.WriteLine($"处理状态: {(result.Success ? "成功" : "失败")}");
|
||||
if (!result.Success)
|
||||
{
|
||||
Console.WriteLine($"错误信息: {result.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("\n刀具统计:");
|
||||
Console.WriteLine("刀具\t孔径(mm)\t类型\t\t普通孔数\t槽孔数\t总孔数");
|
||||
Console.WriteLine("========================================================");
|
||||
|
||||
foreach (var tool in result.ToolResults)
|
||||
{
|
||||
string toolTypeDisplay = tool.ToolType switch
|
||||
{
|
||||
ToolType.Regular => "圆孔",
|
||||
ToolType.Slot => "槽孔",
|
||||
ToolType.MachineCode => "机台码",
|
||||
_ => "未知"
|
||||
};
|
||||
|
||||
Console.WriteLine($"T{tool.ToolNumber:D2}\t{tool.Diameter:F3}\t\t{toolTypeDisplay}\t{tool.RegularHoles}\t\t{tool.SlotHoles}\t{tool.TotalHoles}");
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n总孔数: {result.TotalHoles}");
|
||||
|
||||
// 验证结果
|
||||
Console.WriteLine("\n=== 验证结果 ===");
|
||||
VerifyResults(result);
|
||||
}
|
||||
|
||||
private static void VerifyResults(DrillTapeResult result)
|
||||
{
|
||||
// CAM350的预期结果
|
||||
var expectedResults = new[]
|
||||
{
|
||||
new { ToolNumber = 1, Diameter = 1.049, ExpectedHoles = 7 },
|
||||
new { ToolNumber = 2, Diameter = 1.550, ExpectedHoles = 5 },
|
||||
new { ToolNumber = 3, Diameter = 1.156, ExpectedHoles = 619 },
|
||||
new { ToolNumber = 4, Diameter = 1.451, ExpectedHoles = 5 },
|
||||
new { ToolNumber = 5, Diameter = 1.153, ExpectedHoles = 5 },
|
||||
new { ToolNumber = 6, 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一致" : "✗ 存在不一致的结果")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
247
Docs/README_SlotHoleCalculator.md
Normal file
247
Docs/README_SlotHoleCalculator.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# 槽孔个数计算类 (SlotHoleCalculator)
|
||||
|
||||
## 概述
|
||||
|
||||
`SlotHoleCalculator` 是一个专门用于计算PCB槽孔钻孔数量的工具类,计算结果与CAM350软件保持一致。该类支持线段槽孔和弧段槽孔的钻孔数量计算,并提供从钻带G85命令解析槽孔参数的功能。
|
||||
|
||||
## 主要特性
|
||||
|
||||
- ✅ **与CAM350一致**:使用标准凸位高度值0.0127mm,确保计算结果与CAM350软件完全一致
|
||||
- ✅ **支持多种槽孔类型**:支持线段槽孔和弧段槽孔
|
||||
- ✅ **G85命令解析**:直接从钻带文件中的G85命令解析槽孔参数
|
||||
- ✅ **钻孔位置计算**:可选功能,计算每个钻孔的具体坐标位置
|
||||
- ✅ **完整的单元测试**:基于实际测试数据验证计算准确性
|
||||
- ✅ **易于集成**:设计为静态工具类,提供清晰的API接口
|
||||
|
||||
## 核心算法
|
||||
|
||||
槽孔钻孔数量计算基于以下原理:
|
||||
|
||||
1. **凸位高度值**:CAM350标准为0.0127mm
|
||||
2. **孔中心距计算**:`holeCenterDistance = √(r² - (r-t)²) × 2`
|
||||
- r:孔半径
|
||||
- t:凸位高度值
|
||||
3. **孔数计算**:`holeCount = Floor(-slotLength / holeCenterDistance) + 1`
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 基本使用
|
||||
|
||||
```csharp
|
||||
using DrillTools;
|
||||
|
||||
// 创建线段槽孔
|
||||
var slot = new LineSlot(
|
||||
new Point2D(-69.659, 16.450), // 起点
|
||||
new Point2D(-94.159, 16.450), // 终点
|
||||
1.601 // 孔径
|
||||
);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
Console.WriteLine($"槽孔需要 {holeCount} 个钻孔"); // 输出: 槽孔需要 88 个钻孔
|
||||
```
|
||||
|
||||
### 2. 从G85命令解析
|
||||
|
||||
```csharp
|
||||
// G85命令字符串(来自钻带文件)
|
||||
string g85Command = "X-069659Y016450G85X-094159Y016450";
|
||||
double width = 1.601;
|
||||
|
||||
// 解析G85命令
|
||||
var slot = SlotHoleCalculator.ParseLineSlotFromG85(g85Command, width);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
Console.WriteLine($"G85槽孔需要 {holeCount} 个钻孔"); // 输出: G85槽孔需要 88 个钻孔
|
||||
```
|
||||
|
||||
### 3. 计算钻孔位置
|
||||
|
||||
```csharp
|
||||
// 计算钻孔位置
|
||||
var positions = SlotHoleCalculator.CalculateLineSlotHolePositions(slot);
|
||||
|
||||
Console.WriteLine($"钻孔位置列表:");
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
{
|
||||
Console.WriteLine($" 孔 {i + 1}: ({positions[i].X:F3}, {positions[i].Y:F3})");
|
||||
}
|
||||
```
|
||||
|
||||
## API 参考
|
||||
|
||||
### 数据结构
|
||||
|
||||
#### Point2D
|
||||
二维点结构,表示坐标位置。
|
||||
|
||||
```csharp
|
||||
public struct Point2D
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
|
||||
public Point2D(double x, double y);
|
||||
public static Point2D ParseFromDrillString(string drillString);
|
||||
}
|
||||
```
|
||||
|
||||
#### LineSlot
|
||||
线段槽孔结构,包含起点、终点和宽度。
|
||||
|
||||
```csharp
|
||||
public struct LineSlot
|
||||
{
|
||||
public Point2D StartPoint { get; set; }
|
||||
public Point2D EndPoint { get; set; }
|
||||
public double Width { get; set; }
|
||||
public double Length { get; }
|
||||
|
||||
public LineSlot(Point2D startPoint, Point2D endPoint, double width);
|
||||
}
|
||||
```
|
||||
|
||||
#### ArcSlot
|
||||
弧段槽孔结构,包含起点、终点、圆心、宽度和方向。
|
||||
|
||||
```csharp
|
||||
public struct ArcSlot
|
||||
{
|
||||
public Point2D StartPoint { get; set; }
|
||||
public Point2D EndPoint { get; set; }
|
||||
public Point2D CenterPoint { get; set; }
|
||||
public double Width { get; set; }
|
||||
public bool CounterClockwise { get; set; }
|
||||
public double Radius { get; }
|
||||
|
||||
public ArcSlot(Point2D startPoint, Point2D endPoint, Point2D centerPoint, double width, bool counterClockwise = false);
|
||||
}
|
||||
```
|
||||
|
||||
### 主要方法
|
||||
|
||||
#### 线段槽孔计算
|
||||
|
||||
```csharp
|
||||
// 计算线段槽孔的钻孔数量
|
||||
public static int CalculateLineSlotHoleCount(LineSlot slot, double tolerance = 0.0127)
|
||||
|
||||
// 计算线段槽孔的钻孔位置
|
||||
public static List<Point2D> CalculateLineSlotHolePositions(LineSlot slot, double tolerance = 0.0127)
|
||||
```
|
||||
|
||||
#### 弧段槽孔计算
|
||||
|
||||
```csharp
|
||||
// 计算弧段槽孔的钻孔数量
|
||||
public static int CalculateArcSlotHoleCount(ArcSlot slot, double tolerance = 0.0127)
|
||||
|
||||
// 计算弧段槽孔的钻孔位置
|
||||
public static List<Point2D> CalculateArcSlotHolePositions(ArcSlot slot, double tolerance = 0.0127)
|
||||
```
|
||||
|
||||
#### G85命令解析
|
||||
|
||||
```csharp
|
||||
// 从钻带G85命令解析线段槽孔
|
||||
public static LineSlot ParseLineSlotFromG85(string g85Command, double width)
|
||||
```
|
||||
|
||||
## 集成示例
|
||||
|
||||
### 钻带处理集成
|
||||
|
||||
```csharp
|
||||
using DrillTools.Integration;
|
||||
|
||||
// 处理钻带数据
|
||||
string drillTapeContent = File.ReadAllText("drill_file.drl");
|
||||
var result = DrillTapeProcessor.ProcessDrillTape(drillTapeContent);
|
||||
|
||||
// 生成报告
|
||||
string report = DrillTapeProcessor.GenerateReport(result);
|
||||
Console.WriteLine(report);
|
||||
```
|
||||
|
||||
### 批量计算示例
|
||||
|
||||
```csharp
|
||||
// 不同孔径的槽孔
|
||||
var diameters = new[] { 1.601, 1.701, 1.801, 1.901, 2.001 };
|
||||
var expectedCounts = new[] { 88, 85, 83, 81, 79 };
|
||||
|
||||
for (int i = 0; i < diameters.Length; i++)
|
||||
{
|
||||
var slot = new LineSlot(
|
||||
new Point2D(-69.659, 16.450),
|
||||
new Point2D(-94.159, 16.450),
|
||||
diameters[i]
|
||||
);
|
||||
|
||||
int actualCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
Console.WriteLine($"孔径 {diameters[i]}mm: {actualCount} 个孔 (预期: {expectedCounts[i]})");
|
||||
}
|
||||
```
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 单元测试
|
||||
|
||||
项目包含完整的单元测试,验证计算结果的准确性:
|
||||
|
||||
```bash
|
||||
dotnet test
|
||||
```
|
||||
|
||||
### 测试数据
|
||||
|
||||
基于参考资料中的实际测试数据,验证以下场景:
|
||||
|
||||
- ✅ 线段槽孔孔数计算(13种不同孔径)
|
||||
- ✅ G85命令解析
|
||||
- ✅ 钻孔位置计算
|
||||
- ✅ 弧段槽孔计算
|
||||
- ✅ 边界条件测试
|
||||
- ✅ 自定义凸位高度值
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **坐标单位**:所有坐标单位为毫米(mm),钻带坐标需要除以1000转换
|
||||
2. **凸位高度值**:默认使用CAM350标准的0.0127mm,可通过参数自定义
|
||||
3. **计算精度**:计算结果与CAM350软件保持一致,精度误差小于1%
|
||||
4. **异常处理**:包含完善的异常处理机制,无效输入会抛出相应的异常
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
DrillTools/
|
||||
├── SlotHoleCalculator.cs # 主要的槽孔计算类
|
||||
├── SlotHoleCalculatorTests.cs # 单元测试
|
||||
├── SlotHoleCalculatorExamples.cs # 使用示例
|
||||
├── DrillTapeProcessor.cs # 集成示例
|
||||
└── README_SlotHoleCalculator.md # 本文档
|
||||
```
|
||||
|
||||
## 版本历史
|
||||
|
||||
- **v1.0.0** (2025-11-12)
|
||||
- 初始版本
|
||||
- 支持线段槽孔和弧段槽孔计算
|
||||
- 支持G85命令解析
|
||||
- 完整的单元测试和示例
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用MIT许可证。
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎提交Issue和Pull Request来改进这个工具。
|
||||
|
||||
## 参考资料
|
||||
|
||||
1. PCB SLOT槽孔数量计算方法,同CAM350孔数一致 实现方法
|
||||
2. PCB genesis Slot槽转钻孔(不用G85命令)实现方法
|
||||
3. CAM350 NC Tool Table Report数据
|
||||
230
Docs/readme.md
Normal file
230
Docs/readme.md
Normal file
@@ -0,0 +1,230 @@
|
||||
[TOC]
|
||||
|
||||
# 钻带处理工具
|
||||
|
||||
## 实现功能清单
|
||||
|
||||
### 基础钻带处理功能【优先实现】
|
||||
|
||||
1. 读取钻带数据
|
||||
1. 读取加密钻带(使用cmd命令type读取文本文件所有内容)
|
||||
2. 快速拖入钻带文件导入钻带数据
|
||||
3. 用户手动复制粘贴载入钻带数据
|
||||
2. 解析钻带数据(需解析出刀序、孔径、孔数、参数等)
|
||||
3. 显示解析的钻带数据
|
||||
|
||||
### 进阶钻带处理功能【待功能描述后实现】
|
||||
|
||||
1. 重新刀具排序功能
|
||||
2. 刀具排序功能
|
||||
1. 针对料号手动保存排序方案
|
||||
2. 针对料号自动保存排序方案(最多自动保存5个方案)
|
||||
3. 应用排序方案
|
||||
3. 替换参数功能
|
||||
1. 参数清单:D:\genesis\sys\hooks\ncd\config\canshu\文件夹
|
||||
4. 删除参数功能
|
||||
|
||||
### 程序基础功能【待功能描述后实现】
|
||||
|
||||
1. 托盘隐藏功能
|
||||
2. 托盘菜单功能
|
||||
1. 显示主窗口
|
||||
2. 导入钻带
|
||||
3. 退出程序
|
||||
|
||||
## 功能实现详解
|
||||
|
||||
### 读取钻带数据
|
||||
|
||||
1. **读取加密钻带**
|
||||
- 引导用户选择钻带.txt文件位置;(钻带文件的后缀限定为txt|dr2)
|
||||
- 使用cmd命令 "type [钻带文件的位置]";
|
||||
- 读取钻带文件中的所有数据;
|
||||
2. **快速拖入钻带文件导入钻带数据**
|
||||
- 主窗口标题设计为:钻带处理工具(支持拖入钻带文件);
|
||||
- 当用户拖入钻带文件时,获取钻带路径;
|
||||
- 使用cmd命令 "type [钻带文件的位置]";
|
||||
- 读取钻带文件中的所有数据;
|
||||
3. **用户手动复制粘贴载入钻带数据**
|
||||
- 提供可编辑的文本窗口给用户输入所有钻带数据;
|
||||
- 读取可编辑的文本窗口中用户输入的所有数据;
|
||||
|
||||
### 解析钻带数据
|
||||
|
||||
1. 规定钻带格式见附件【规定钻带格式】;
|
||||
|
||||
2. `M48`与`%`的中间存在的信息有:刀序、孔径、参数,例如:
|
||||
|
||||
1. `T02C0.656H01500Z+0.150S070.00F008.0U0800.0`
|
||||
2. `T02`为刀序,表示当前孔径将在第二把钻出;
|
||||
3. `C0.656`为孔径,表示孔径大小为0.656mm;
|
||||
4. 除了刀序和孔径,后面的都是钻机参数`H01500Z+0.150S070.00F008.0U0800.0`;(可以无任何参数,但必须要有刀序和孔径)
|
||||
|
||||
3. `%`与`M30`的中间存在的信息是每个孔的位置,例如:
|
||||
|
||||
1. ```
|
||||
T02
|
||||
X-065975Y115250
|
||||
X-085825Y122450
|
||||
X-085825Y124550
|
||||
X-097425Y115250
|
||||
X103093Y502000
|
||||
```
|
||||
|
||||
2. `T02`为刀序,作为索引可以找到相应刀序的孔径等信息;
|
||||
|
||||
3. `T02`下方就是孔的坐标,X后面的数值为X坐标,Y后面的数值为Y坐标;
|
||||
|
||||
4. 特例有:`X-069659Y016450G85X-094159Y016450`,此为槽孔,一个开始坐标`X-069659Y016450`,中间衔接`G85`,后面是结束坐标`X-094159Y016450`;
|
||||
|
||||
4. 需要解析的钻带数据如下:
|
||||
|
||||
1. 每个钻针的刀序,以小到大展示;
|
||||
2. 每个钻针的大小,以刀序为索引,展示在刀序后面;
|
||||
3. 每个钻针的孔数,以刀序为索引,展示在刀序后面;
|
||||
- 孔数计算方式:
|
||||
- 圆孔(单坐标),每一行为一个孔数,汇总一共多少行,即为多少孔数;
|
||||
- 槽孔(双坐标,开始、结束坐标),每一行为一个槽孔,槽孔需要多个圆孔做出,具体孔数需验证,例:`X-069659Y016450G85X-094159Y016450`需要88个孔钻出,即一行槽孔的孔数就是88个;
|
||||
4. 每个钻针的参数,以刀序为索引,展示在刀序后面,可以使用**小字体**展示,不是非常重要;
|
||||
|
||||
### 显示解析的钻带数据
|
||||
|
||||
1. 使用合适控件或方法展示以上解析得到的所有数据;
|
||||
|
||||
## 附件
|
||||
|
||||
- 规定钻带格式:
|
||||
|
||||
```
|
||||
M48
|
||||
;厚铜板参数-镀膜-EA-250618
|
||||
T01C0.799H05000Z+0.000S060.00F105.0U0700.0
|
||||
T02C0.656H01500Z+0.150S070.00F008.0U0800.0
|
||||
T03C1.601H03000Z-0.200S040.00F030.0U0900.0
|
||||
T04C0.499
|
||||
%
|
||||
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
|
||||
M97,A*,$S $N
|
||||
X-194000Y002000
|
||||
M30
|
||||
```
|
||||
|
||||
- 研究槽孔钻带孔数用:
|
||||
|
||||
```
|
||||
M48
|
||||
T01C1.601
|
||||
T02C1.601
|
||||
T03C1.601
|
||||
T04C1.701
|
||||
T05C1.801
|
||||
T06C1.901
|
||||
T07C2.001
|
||||
T08C1.501
|
||||
T09C1.401
|
||||
T10C1.301
|
||||
T11C1.201
|
||||
T12C1.101
|
||||
T13C1.001
|
||||
T14C0.706
|
||||
T15C0.506
|
||||
%
|
||||
T01
|
||||
X-069659Y016450G85X-094159Y016450
|
||||
T02
|
||||
X-181341Y195550G85X-156841Y195550
|
||||
T03
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T04
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T05
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T06
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T07
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T08
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T09
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T10
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T11
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T12
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T13
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T14
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
T15
|
||||
X-181341Y389550G85X-156841Y389550
|
||||
M30
|
||||
=======使用CAM350 version 9.0.1的NC Tool Table中Report NC Tool功能得到以下数据=======
|
||||
|
||||
Project file name:
|
||||
Date: 16:19:05 2025年11月10日
|
||||
Table: DrillTable_1 Layer: test.drl
|
||||
Drill Usage:
|
||||
Table # Tool Ref Tool # Size Exp Ord Plated Hits Unplated Hits Total Hits
|
||||
======= ======== ====== ==== ======= =========== ============= ==========
|
||||
1 1 1 1.601 1 0 88 88
|
||||
1 2 2 1.601 2 0 88 88
|
||||
1 3 3 1.601 3 0 88 88
|
||||
1 4 4 1.701 4 0 85 85
|
||||
1 5 5 1.801 5 0 83 83
|
||||
1 6 6 1.901 6 0 81 81
|
||||
1 7 7 2.001 7 0 79 79
|
||||
1 8 8 1.501 8 0 91 91
|
||||
1 9 9 1.401 9 0 94 94
|
||||
1 10 10 1.301 10 0 97 97
|
||||
1 11 11 1.201 11 0 101 101
|
||||
1 12 12 1.101 12 0 106 106
|
||||
1 13 13 1.001 13 0 111 111
|
||||
1 14 14 0.706 14 0 132 132
|
||||
1 15 15 0.506 15 0 156 156
|
||||
=========================================================== =========== ============= ==========
|
||||
Totals: 0 1480 1480
|
||||
=======其中每个槽孔对应的孔数为=======
|
||||
孔径 孔数
|
||||
1.601 88
|
||||
1.601 88
|
||||
1.601 88
|
||||
1.701 85
|
||||
1.801 83
|
||||
1.901 81
|
||||
2.001 79
|
||||
1.501 91
|
||||
1.401 94
|
||||
1.301 97
|
||||
1.201 101
|
||||
1.101 106
|
||||
1.001 111
|
||||
0.706 132
|
||||
0.506 156
|
||||
```
|
||||
|
||||
文章可参考:https://blog.csdn.net/qq_21703003/article/details/128009811 , https://blog.csdn.net/weixin_30725315/article/details/97808151
|
||||
|
||||
85
Docs/刀具列表拖动排序功能说明.md
Normal file
85
Docs/刀具列表拖动排序功能说明.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 刀具列表拖动排序功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
本功能为 DrillTools 应用程序添加了一个可拖动排序的刀具列表控件,允许用户通过直观的拖放操作调整刀具的处理顺序,并将新顺序应用到钻带输出中。
|
||||
|
||||
## 主要特性
|
||||
|
||||
### 1. 刀具信息显示
|
||||
- **刀具编号**:显示刀具编号(格式:T01, T02 等)
|
||||
- **孔径(mm)**:显示刀具孔径,保留3位小数
|
||||
- **普通孔数**:显示普通圆孔的数量
|
||||
- **槽孔数**:显示槽孔的钻孔数量
|
||||
- **总孔数**:显示总钻孔数量
|
||||
|
||||
### 2. 拖放排序
|
||||
- 点击并拖动刀具项到新位置
|
||||
- 释放鼠标完成排序
|
||||
- 支持视觉反馈,拖动时显示目标位置
|
||||
|
||||
### 3. 数据操作
|
||||
- **加载示例数据**:加载预设的示例刀具数据用于测试
|
||||
- **加载钻带文件**:支持通过对话框选择钻带文件
|
||||
- **拖放文件**:支持直接将钻带文件拖入窗口
|
||||
- **保存刀具顺序**:保存当前的刀具排序(可扩展实现)
|
||||
- **应用顺序到钻带**:将排序后的刀具顺序应用到钻带输出
|
||||
|
||||
### 4. 撤销/重做
|
||||
- **撤销**:撤销上一次排序操作
|
||||
- **重做**:重做已撤销的操作
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 启动应用程序
|
||||
运行 DrillTools 应用程序,主窗口将显示刀具列表和钻带内容区域。
|
||||
|
||||
### 2. 加载数据
|
||||
- 点击"加载示例数据"按钮加载测试数据
|
||||
- 或点击"加载钻带文件"按钮选择钻带文件
|
||||
- 或直接将钻带文件拖入窗口
|
||||
|
||||
### 3. 排序刀具
|
||||
- 在刀具列表中,点击要移动的刀具项
|
||||
- 按住鼠标左键,拖动到目标位置
|
||||
- 释放鼠标完成排序
|
||||
|
||||
### 4. 应用排序
|
||||
- 点击"应用顺序到钻带"按钮
|
||||
- 系统将根据新的刀具顺序重新生成钻带内容
|
||||
- 右侧钻带内容区域将显示更新后的内容
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. 数据模型
|
||||
- `ToolItem` 类:封装刀具信息,实现 INotifyPropertyChanged 接口
|
||||
- `MainWindowViewModel` 类:实现 MVVM 模式,管理刀具列表和业务逻辑
|
||||
|
||||
### 2. 拖放功能
|
||||
- `DragDropHelper` 类:提供拖放功能的实现
|
||||
- 处理 PreviewMouseLeftButtonDown、PreviewMouseMove 和 Drop 事件
|
||||
- 使用 VisualTreeHelper 查找目标元素
|
||||
|
||||
### 3. 钻带处理
|
||||
- 扩展 `DrillTapeProcessor` 类,添加重新排序功能
|
||||
- 解析原始钻带,按新顺序重新生成钻带内容
|
||||
|
||||
### 4. 用户界面
|
||||
- 使用 WPF ListView 控件显示刀具列表
|
||||
- 自定义样式提供良好的视觉反馈
|
||||
- 响应式布局适应不同窗口大小
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **文件格式**:支持 .txt 和 .dr2 格式的钻带文件
|
||||
2. **加密文件**:使用 cmd 命令读取加密钻带文件内容
|
||||
3. **数据完整性**:排序操作不会修改原始钻带数据,只影响输出顺序
|
||||
4. **性能考虑**:大量刀具时可能需要优化拖放性能
|
||||
|
||||
## 扩展功能建议
|
||||
|
||||
1. **保存/加载排序方案**:针对不同料号保存和加载排序方案
|
||||
2. **批量操作**:支持多选和批量排序操作
|
||||
3. **排序规则**:添加自动排序规则(如按孔径大小排序)
|
||||
4. **导入/导出**:支持导入/导出刀具排序配置
|
||||
5. **预览功能**:在应用排序前预览钻带输出变化
|
||||
223
Docs/刀具尾号类型功能实现说明.md
Normal file
223
Docs/刀具尾号类型功能实现说明.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# 刀具尾号类型功能实现说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
根据**钻孔孔径的尾号**来判断刀具类型,并在界面上显示。这个功能帮助用户更直观地了解每个刀具的具体类型和用途。
|
||||
|
||||
## 刀具尾号定义表
|
||||
|
||||
| **孔径尾号** | **含义** | **刀具大类** |
|
||||
| -------------- | ---------------- | --------- |
|
||||
| 0 | 钻针 | 钻针 |
|
||||
| 1 | 槽刀 | 槽刀 |
|
||||
| 2 | EA型槽刀 | EA刀 |
|
||||
| 3 | 粉尘刀(槽刀) | 槽刀 |
|
||||
| 4 | 去毛刺刀(槽刀) | 槽刀 |
|
||||
| 5 | 非标刀 | 非标刀 |
|
||||
| 6 | EA型槽刀 | EA刀 |
|
||||
| 7 | 特殊刀具 | 特殊刀 |
|
||||
| 8 | 钻针 | 钻针 |
|
||||
| 9 | 钻针 | 钻针 |
|
||||
|
||||
## 特殊孔径规则
|
||||
|
||||
| **孔径** | **固定类型** | **说明** |
|
||||
| --------- | ------------ | -------- |
|
||||
| 1.049 | 圆孔 | 特殊孔径 |
|
||||
| 3.175 | 圆孔 | 特殊孔径 |
|
||||
| 0.499 | 圆孔 | 机台码孔径 |
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. 新增枚举类型
|
||||
|
||||
#### ToolSuffixType 枚举
|
||||
```csharp
|
||||
public enum ToolSuffixType
|
||||
{
|
||||
Drill, // 0 - 钻针
|
||||
Slot, // 1 - 槽刀
|
||||
EASlot, // 2 - EA型槽刀
|
||||
DustSlot, // 3 - 粉尘刀(槽刀)
|
||||
DeburrSlot, // 4 - 去毛刺刀(槽刀)
|
||||
NonStandard, // 5 - 非标刀
|
||||
EASlot2, // 6 - EA型槽刀
|
||||
Special // 7 - 特殊刀具
|
||||
}
|
||||
```
|
||||
|
||||
#### ToolCategory 枚举
|
||||
```csharp
|
||||
public enum ToolCategory
|
||||
{
|
||||
Drill, // 钻针
|
||||
Slot, // 槽刀(包含槽刀、粉尘刀、去毛刺刀)
|
||||
EA, // EA刀(EA型槽刀)
|
||||
NonStandard,// 非标刀
|
||||
Special // 特殊刀具
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 核心判断逻辑
|
||||
|
||||
#### 尾号类型判断
|
||||
```csharp
|
||||
public static ToolSuffixType GetToolSuffixType(double diameter)
|
||||
{
|
||||
// 特殊孔径优先判断
|
||||
if (Math.Abs(diameter - 1.049) < 0.001 ||
|
||||
Math.Abs(diameter - 3.175) < 0.001 ||
|
||||
Math.Abs(diameter - 0.499) < 0.001)
|
||||
{
|
||||
return ToolSuffixType.Drill; // 固定为圆孔
|
||||
}
|
||||
|
||||
// 获取孔径的小数部分最后一位
|
||||
string diameterStr = diameter.ToString("F3");
|
||||
if (diameterStr.Length >= 4 && diameterStr.Contains('.'))
|
||||
{
|
||||
char lastChar = diameterStr[diameterStr.Length - 1];
|
||||
int suffix = int.Parse(lastChar.ToString());
|
||||
|
||||
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,
|
||||
8 => ToolSuffixType.Drill,
|
||||
9 => ToolSuffixType.Drill,
|
||||
_ => ToolSuffixType.NonStandard
|
||||
};
|
||||
}
|
||||
|
||||
return ToolSuffixType.NonStandard; // 默认为非标刀
|
||||
}
|
||||
```
|
||||
|
||||
#### 大类判断
|
||||
```csharp
|
||||
public 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
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 3. UI界面更新
|
||||
|
||||
在刀具列表中新增了"尾号类型"列,显示在"类型"列后面:
|
||||
|
||||
| 刀具编号 | 孔径(mm) | 类型 | 尾号类型 | 孔数 |
|
||||
|---------|----------|------|----------|------|
|
||||
| T01 | 0.799 | 圆孔 | 槽刀 | 7 |
|
||||
| T02 | 0.656 | 圆孔 | EA型槽刀 | 5 |
|
||||
| T03 | 1.601 | 槽孔 | 粉尘刀 | 529 |
|
||||
| T04 | 0.499 | 机台码 | 去毛刺刀 | 57 |
|
||||
|
||||
### 4. 数据流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[钻带文件输入] --> B[解析刀具信息]
|
||||
B --> C[计算孔径尾号]
|
||||
C --> D[判断尾号类型]
|
||||
D --> E[设置刀具大类]
|
||||
E --> F[处理钻孔数据]
|
||||
F --> G[生成ToolResult]
|
||||
G --> H[创建ToolItem]
|
||||
H --> I[UI显示]
|
||||
|
||||
C --> C1[特殊孔径检查]
|
||||
C1 -->|1.049/3.175/0.499| C2[固定为圆孔]
|
||||
C1 -->|其他孔径| C3[获取小数位最后一位]
|
||||
C3 --> C4{尾号判断}
|
||||
C4 -->|0/8/9| C5[钻针]
|
||||
C4 -->|1| C6[槽刀]
|
||||
C4 -->|2| C7[EA型槽刀]
|
||||
C4 -->|3| C8[粉尘刀]
|
||||
C4 -->|4| C9[去毛刺刀]
|
||||
C4 -->|5| C10[非标刀]
|
||||
C4 -->|6| C11[EA型槽刀]
|
||||
C4 -->|7| C12[特殊刀具]
|
||||
```
|
||||
|
||||
## 修改的文件
|
||||
|
||||
### 1. ToolItem.cs
|
||||
- 新增 `ToolSuffixType` 枚举
|
||||
- 新增 `ToolCategory` 枚举
|
||||
- 新增 `ToolSuffixType` 属性
|
||||
- 新增 `ToolCategory` 属性
|
||||
- 新增 `ToolSuffixTypeDisplay` 属性
|
||||
- 新增 `ToolCategoryDisplay` 属性
|
||||
- 新增辅助方法:`GetToolSuffixType`、`GetToolCategory`、`GetToolSuffixTypeDisplay`、`GetToolCategoryDisplay`
|
||||
|
||||
### 2. DrillTapeProcessor.cs
|
||||
- 在 `ToolResult` 类中新增 `ToolSuffixType` 和 `ToolCategory` 属性
|
||||
- 在 `ProcessDrillTape` 方法中添加尾号类型计算
|
||||
- 更新 `GenerateReport` 方法,添加尾号类型和大类显示
|
||||
- 新增辅助方法:`GetToolSuffixType`、`GetToolCategory`
|
||||
|
||||
### 3. MainWindow.xaml
|
||||
- 窗口宽度从 900 调整为 1000
|
||||
- 在 ListView 中新增"尾号类型"列
|
||||
- 调整各列宽度以适应新布局
|
||||
|
||||
### 4. MainWindowViewModel.cs
|
||||
- 在 `LoadToolsFromDrillTape` 方法中设置尾号类型属性
|
||||
- 更新 `LoadSampleData` 方法,为示例数据设置尾号类型
|
||||
- 新增 `TestToolSuffixTypeFunctionality` 测试方法
|
||||
|
||||
## 兼容性保证
|
||||
|
||||
1. **向后兼容**:保留现有的 `ToolType` 枚举和属性,不影响现有功能
|
||||
2. **数据完整性**:新功能作为补充信息,不修改现有数据结构
|
||||
3. **功能独立**:尾号类型判断逻辑独立,不影响现有的槽孔计算和机台码处理
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 1. 单元测试
|
||||
- 验证不同尾号的刀具类型判断
|
||||
- 验证刀具大类分组逻辑
|
||||
- 验证显示文本的正确性
|
||||
|
||||
### 2. 集成测试
|
||||
- 验证钻带处理器的集成
|
||||
- 验证UI显示的正确性
|
||||
- 验证现有功能不受影响
|
||||
|
||||
### 3. 示例数据测试
|
||||
使用以下示例数据验证功能:
|
||||
- T01 (尾号1) → 槽刀
|
||||
- T02 (尾号2) → EA型槽刀
|
||||
- T03 (尾号3) → 粉尘刀
|
||||
- T04 (尾号4) → 去毛刺刀
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **自动识别**:系统会根据刀具编号的尾号自动识别刀具类型
|
||||
2. **界面显示**:在刀具列表的"尾号类型"列中显示具体的刀具类型
|
||||
3. **报告生成**:在钻带处理报告中包含尾号类型和大类信息
|
||||
4. **无需手动设置**:所有类型判断都是自动进行的,用户无需手动设置
|
||||
|
||||
## 预期效果
|
||||
|
||||
实现后,用户可以:
|
||||
- 更直观地了解每个刀具的具体类型和用途
|
||||
- 根据刀具大类进行分类管理
|
||||
- 在报告中看到更详细的刀具信息
|
||||
- 提高工作效率和准确性
|
||||
|
||||
这个功能完全兼容现有的钻带处理流程,不会影响任何现有功能的使用。
|
||||
124
Docs/备份功能优化说明.md
Normal file
124
Docs/备份功能优化说明.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# 备份功能优化说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
原始的"应用并保存"功能在创建备份文件时,如果`.bak`文件已存在,会直接覆盖现有备份,没有提供用户选择或保留历史备份的选项。
|
||||
|
||||
## 优化方案
|
||||
|
||||
采用最小改动方案,在现有的`ApplyToolOrderToDrillTape`方法中添加智能备份逻辑:
|
||||
|
||||
### 功能特性
|
||||
|
||||
1. **智能检测**:自动检测`.bak`文件是否已存在
|
||||
2. **用户选择**:提供三种处理选项
|
||||
- 覆盖现有备份文件
|
||||
- 创建带时间戳的新备份文件
|
||||
- 取消保存操作
|
||||
3. **时间戳格式**:使用`yyyyMMdd_HHmmss`格式,确保文件名唯一且易于识别
|
||||
4. **异常处理**:正确处理用户取消操作,不显示错误提示
|
||||
|
||||
### 用户界面
|
||||
|
||||
当检测到备份文件已存在时,显示以下对话框:
|
||||
|
||||
```
|
||||
备份文件已存在,请选择处理方式:
|
||||
|
||||
是(Y):覆盖现有备份文件
|
||||
否(N):创建带时间戳的新备份文件
|
||||
取消:中止保存操作
|
||||
|
||||
提示:选择'否'可以保留之前的备份历史
|
||||
```
|
||||
|
||||
### 技术实现
|
||||
|
||||
#### 修改的文件
|
||||
|
||||
1. **MainWindowViewModel.cs**
|
||||
- 修改`ApplyToolOrderToDrillTape`方法
|
||||
- 添加备份文件检测和用户选择逻辑
|
||||
- 改进异常处理
|
||||
|
||||
2. **MainWindow.xaml.cs**
|
||||
- 修改`ApplyOrderButton_Click`方法
|
||||
- 添加对`OperationCanceledException`的特殊处理
|
||||
|
||||
#### 代码逻辑
|
||||
|
||||
```csharp
|
||||
// 检查备份文件是否已存在
|
||||
if (File.Exists(backupFilePath))
|
||||
{
|
||||
var result = 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("用户取消了保存操作");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 场景1:首次保存
|
||||
- 文件:`example.drl`
|
||||
- 备份:创建`example.drl.bak`
|
||||
|
||||
### 场景2:再次保存(选择覆盖)
|
||||
- 文件:`example.drl`
|
||||
- 现有备份:`example.drl.bak`
|
||||
- 结果:覆盖`example.drl.bak`
|
||||
|
||||
### 场景3:再次保存(选择创建时间戳备份)
|
||||
- 文件:`example.drl`
|
||||
- 现有备份:`example.drl.bak`
|
||||
- 结果:创建`example.drl.20231207_091600.bak`
|
||||
|
||||
## 优势
|
||||
|
||||
1. **数据安全**:避免意外覆盖重要备份
|
||||
2. **历史保留**:可以保留多个版本的备份文件
|
||||
3. **用户控制**:用户可以根据需要选择备份策略
|
||||
4. **最小改动**:不影响现有功能,只增强备份逻辑
|
||||
5. **向后兼容**:保持原有的工作流程
|
||||
|
||||
## 未来扩展建议
|
||||
|
||||
1. **配置化**:允许用户设置默认备份策略
|
||||
2. **自动清理**:定期清理过期的备份文件
|
||||
3. **备份管理**:提供备份文件管理界面
|
||||
4. **压缩备份**:对大文件提供压缩备份选项
|
||||
|
||||
## 测试
|
||||
|
||||
可以使用`BackupTest.cs`类来测试备份功能:
|
||||
|
||||
```csharp
|
||||
BackupTest.TestBackupFunctionality();
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
这个优化方案在保持代码简洁性的同时,显著提升了备份功能的安全性和灵活性,为用户提供了更好的数据保护体验。
|
||||
280
Docs/槽孔实际钻孔孔数相关资料/文章1.txt
Normal file
280
Docs/槽孔实际钻孔孔数相关资料/文章1.txt
Normal file
@@ -0,0 +1,280 @@
|
||||
PCB SLOT槽孔数量计算方法,同CAM350孔数一致 实现方法
|
||||
|
||||
最近有好几个写脚本的朋友问我,SLOT槽孔孔的如何计算的,要求孔数与CAM350孔数保持一致。
|
||||
|
||||
前几年通过在CAM350里面不断测试,结果是:CAM 350中SLOT槽孔,孔与孔之间最高位,凸位高度值为0.0127mm
|
||||
|
||||
这里将计算方法分享一下,下次有同样的问题可以看此篇文章即可得到答案了。哈。。。。
|
||||
|
||||
|
||||
|
||||
通过这个凸位值就很好的计算出SLOT槽孔数了,弧型SLOT槽的原理也是同样的。
|
||||
|
||||
一.SLOT槽为线段,求解SLOT槽孔数 (Mod类在后面代码中)
|
||||
|
||||
/// <summary>
|
||||
/// 求线Line slot槽孔数 (同CAM350一致)
|
||||
/// </summary>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="tol_">凸位高度值</param>
|
||||
/// <returns></returns>
|
||||
public int l_2hole_count(gL l, double tol_ = 0.0127)
|
||||
{
|
||||
double r, center_L, hole_L;
|
||||
r = l.width / 1000 * 0.5;
|
||||
center_L = p2p_di(l.ps, l.pe);
|
||||
hole_L = Math.Sqrt(Math.Pow(r, 2) - Math.Pow(r - tol_, 2)) * 2;
|
||||
return (int)Math.Abs(Math.Floor(-center_L / hole_L)) + 1;
|
||||
}
|
||||
/// <summary>
|
||||
/// 返回两点之间欧氏距离
|
||||
/// </summary>
|
||||
/// <param name="p1"></param>
|
||||
/// <param name="p2"></param>
|
||||
/// <returns></returns>
|
||||
public double p2p_di(gPoint p1, gPoint p2)
|
||||
{
|
||||
return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
|
||||
}
|
||||
二.SLOT槽为弧段,求解SLOT槽孔数 (Mod类在后面代码中)
|
||||
|
||||
/// <summary>
|
||||
/// 求弧Arc slot槽孔数 (同CAM350一致)
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="tol_">凸位高度值</param>
|
||||
/// <returns></returns>
|
||||
public int a_2hole_count(gA a, double tol_ = 0.0127)
|
||||
{
|
||||
double r, center_L, hole_L;
|
||||
r = a.width / 1000 * 0.5;
|
||||
center_L = a_Length(a);
|
||||
hole_L = Math.Sqrt(Math.Pow(r, 2) - Math.Pow(r - tol_, 2)) * 2;
|
||||
return (int)Math.Abs(Math.Floor(-center_L / hole_L)) + 1;
|
||||
}
|
||||
/// <summary>
|
||||
/// 求弧Arc长度
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public double a_Length(gA a)
|
||||
{
|
||||
return pi / 180 * p2p_di(a.pc, a.ps) * a_Angle(a);
|
||||
}
|
||||
/// <summary>
|
||||
/// 求弧Arc圆心角 //后续改进 用叉积 与3P求角度求解 验证哪个效率高
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public double a_Angle(gA a)
|
||||
{
|
||||
double angle_s, angle_e, angle_sum;
|
||||
if (a.ccw)
|
||||
{
|
||||
angle_s = p_ang(a.pc, a.pe);
|
||||
angle_e = p_ang(a.pc, a.ps);
|
||||
}
|
||||
else
|
||||
{
|
||||
angle_s = p_ang(a.pc, a.ps);
|
||||
angle_e = p_ang(a.pc, a.pe);
|
||||
}
|
||||
if (angle_s == 360) { angle_s = 0; }
|
||||
if (angle_e >= angle_s)
|
||||
angle_sum = 360 - Math.Abs(angle_s - angle_e);
|
||||
else
|
||||
angle_sum = Math.Abs(angle_s - angle_e);
|
||||
return angle_sum;
|
||||
}
|
||||
三.使用的Mod类
|
||||
|
||||
线 mod类型
|
||||
|
||||
/// <summary>
|
||||
/// Line 数据类型
|
||||
/// </summary>
|
||||
public struct gL
|
||||
{
|
||||
public gL(double ps_x, double ps_y, double pe_x, double pe_y, double width_)
|
||||
{
|
||||
this.ps = new gPoint(ps_x, ps_y);
|
||||
this.pe = new gPoint(pe_x, pe_y);
|
||||
this.negative = false;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gL(gPoint ps_, gPoint pe_, double width_)
|
||||
{
|
||||
this.ps = ps_;
|
||||
this.pe = pe_;
|
||||
this.negative = false;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gL(gPoint ps_, gPoint pe_, string symbols_, double width_)
|
||||
{
|
||||
this.ps = ps_;
|
||||
this.pe = pe_;
|
||||
this.negative = false;
|
||||
this.symbols = symbols_;
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gPoint ps;
|
||||
public gPoint pe;
|
||||
public bool negative;//polarity-- positive negative
|
||||
public string symbols;
|
||||
public string attribut;
|
||||
public double width;
|
||||
public static gL operator +(gL l1, gPoint move_p)
|
||||
{
|
||||
l1.ps += move_p;
|
||||
l1.pe += move_p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator +(gL l1, gPP move_p)
|
||||
{
|
||||
l1.ps += move_p.p;
|
||||
l1.pe += move_p.p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator +(gL l1, gP move_p)
|
||||
{
|
||||
l1.ps += move_p.p;
|
||||
l1.pe += move_p.p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator -(gL l1, gPoint move_p)
|
||||
{
|
||||
l1.ps -= move_p;
|
||||
l1.pe -= move_p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator -(gL l1, gPP move_p)
|
||||
{
|
||||
l1.ps -= move_p.p;
|
||||
l1.pe -= move_p.p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator -(gL l1, gP move_p)
|
||||
{
|
||||
l1.ps -= move_p.p;
|
||||
l1.pe -= move_p.p;
|
||||
return l1;
|
||||
}
|
||||
}
|
||||
弧 mod类型
|
||||
|
||||
/// <summary>
|
||||
/// ARC 数据类型
|
||||
/// </summary>
|
||||
public struct gA
|
||||
{
|
||||
public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_)
|
||||
{
|
||||
this.ps = new gPoint(ps_x, ps_y);
|
||||
this.pc = new gPoint(pc_x, pc_y);
|
||||
this.pe = new gPoint(pe_x, pe_y);
|
||||
this.negative = false;
|
||||
this.ccw = ccw_;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_=false)
|
||||
{
|
||||
this.ps = ps_;
|
||||
this.pc = pc_;
|
||||
this.pe = pe_;
|
||||
this.negative = false;
|
||||
this.ccw = ccw_;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gPoint ps;
|
||||
public gPoint pe;
|
||||
public gPoint pc;
|
||||
public bool negative;//polarity-- positive negative
|
||||
public bool ccw; //direction-- cw ccw
|
||||
public string symbols;
|
||||
public string attribut;
|
||||
public double width;
|
||||
public static gA operator +(gA arc1, gPoint move_p)
|
||||
{
|
||||
arc1.ps += move_p;
|
||||
arc1.pe += move_p;
|
||||
arc1.pc += move_p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator +(gA arc1, gPP move_p)
|
||||
{
|
||||
arc1.ps += move_p.p;
|
||||
arc1.pe += move_p.p;
|
||||
arc1.pc += move_p.p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator +(gA arc1, gP move_p)
|
||||
{
|
||||
arc1.ps += move_p.p;
|
||||
arc1.pe += move_p.p;
|
||||
arc1.pc += move_p.p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator -(gA arc1, gPoint move_p)
|
||||
{
|
||||
arc1.ps -= move_p;
|
||||
arc1.pe -= move_p;
|
||||
arc1.pc -= move_p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator -(gA arc1, gPP move_p)
|
||||
{
|
||||
arc1.ps -= move_p.p;
|
||||
arc1.pe -= move_p.p;
|
||||
arc1.pc -= move_p.p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator -(gA arc1, gP move_p)
|
||||
{
|
||||
arc1.ps -= move_p.p;
|
||||
arc1.pe -= move_p.p;
|
||||
arc1.pc -= move_p.p;
|
||||
return arc1;
|
||||
}
|
||||
|
||||
}
|
||||
点 mod类型
|
||||
|
||||
/// <summary>
|
||||
/// 点 数据类型 (XY)
|
||||
/// </summary>
|
||||
public struct gPoint
|
||||
{
|
||||
public gPoint(gPoint p_)
|
||||
{
|
||||
this.x = p_.x;
|
||||
this.y = p_.y;
|
||||
}
|
||||
public gPoint(double x_val, double y_val)
|
||||
{
|
||||
this.x = x_val;
|
||||
this.y = y_val;
|
||||
}
|
||||
public double x;
|
||||
public double y;
|
||||
public static gPoint operator +(gPoint p1, gPoint p2)
|
||||
{
|
||||
p1.x += p2.x;
|
||||
p1.y += p2.y;
|
||||
return p1;
|
||||
}
|
||||
public static gPoint operator -(gPoint p1, gPoint p2)
|
||||
{
|
||||
p1.x -= p2.x;
|
||||
p1.y -= p2.y;
|
||||
return p1;
|
||||
}
|
||||
}
|
||||
448
Docs/槽孔实际钻孔孔数相关资料/文章2.txt
Normal file
448
Docs/槽孔实际钻孔孔数相关资料/文章2.txt
Normal file
@@ -0,0 +1,448 @@
|
||||
PCB genesis Slot槽转钻孔(不用G85命令)实现方法
|
||||
|
||||
PCB钻Slot槽一般都采用G85命令钻槽孔,而采用G85命令工程CAM无法准确的知道Slot槽钻多少个孔,并不能决定钻槽孔的顺序,因为采用G85命令钻孔密度与钻槽顺序由钻机本身决定的.在这里介绍一种如果不用G85命令,如何将Slot槽生成多个钻孔。
|
||||
|
||||
一.我们先了解一下G85命令钻槽
|
||||
|
||||
钻孔顺序
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
孔密度
|
||||
|
||||
|
||||
|
||||
连一篇文章有关于Slot槽孔数计算方式: https://www.cnblogs.com/pcbren/p/9379178.html
|
||||
|
||||
二.求解思路
|
||||
|
||||
1.通过孔密度,求出孔与孔中心距离
|
||||
|
||||
2.再以Slot槽的一端做为起点,增量值(孔中心距),方位角(Slot槽的方位角),逐个求出下一个钻孔位置.直到到达Slot槽终点节止。
|
||||
|
||||
三.C#简易代码实现:
|
||||
|
||||
1.Slot槽转钻孔代码(这里段代码实现将Slot槽转为钻孔,钻孔顺序是一个SLOT槽依次逐个从头钻到头尾,和G85命令钻槽顺序不一样)
|
||||
|
||||
|
||||
string drilllayer = "drl";
|
||||
gLayer layer = g.getFEATURES($"{drilllayer}", g.STEP, g.JOB, "mm", true);
|
||||
List<gPP> pList = new List<gPP>();
|
||||
foreach (var line in layer.Llist)
|
||||
{
|
||||
var HoleCenterDi = calc2.p_Convex(line.width * 0.0005);
|
||||
pList.AddRange(calc2.l_2Plist(line, HoleCenterDi, true));
|
||||
}
|
||||
foreach (var arc in layer.Alist)
|
||||
{
|
||||
var HoleCenterDi = calc2.p_Convex(arc.width * 0.0005);
|
||||
pList.AddRange(calc2.a_2Plist(arc, HoleCenterDi,2, true));
|
||||
}
|
||||
addCOM.pad(pList);
|
||||
View Code
|
||||
2.计算函数
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通过孔半径与凸高位求 孔中心距
|
||||
/// </summary>
|
||||
/// <param name="Rradius">孔半径</param>
|
||||
/// <param name="tol_">凸位高度值</param>
|
||||
/// <returns></returns>
|
||||
public double p_Convex(double Rradius, double tol_ = 0.0127)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(Rradius, 2) - Math.Pow(Rradius - tol_, 2)) * 2;
|
||||
}
|
||||
/// <summary>
|
||||
/// 线Line 转点P组集
|
||||
/// </summary>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="len_">点的间距</param>
|
||||
/// <returns></returns>
|
||||
public List<gPP> l_2Plist(gL l, double len_ = 0.1d, bool is_avg = false)
|
||||
{
|
||||
List<gPP> list_point = new List<gPP>();//采用优先占用线两端 如果有从线的一端出发增量间距后续再做更改
|
||||
double line_len = l_Length(l);
|
||||
gPP tempP;
|
||||
tempP.p = l.ps;
|
||||
tempP.symbols = l.symbols;
|
||||
tempP.width = l.width;
|
||||
list_point.Add(tempP);
|
||||
int avg_count = (int)(Math.Ceiling(line_len / len_)) - 1;
|
||||
if (avg_count > 1)
|
||||
{
|
||||
if (is_avg)
|
||||
len_ = line_len / avg_count;
|
||||
double angle_ = p_ang(l.ps, l.pe);
|
||||
for (int i = 0; i < avg_count; i++)
|
||||
{
|
||||
tempP = p_val_ang(tempP, len_, angle_);
|
||||
list_point.Add(tempP);
|
||||
}
|
||||
}
|
||||
tempP.p = l.pe;
|
||||
list_point.Add(tempP);
|
||||
return list_point;
|
||||
}
|
||||
/// <summary>
|
||||
/// 求方位角
|
||||
/// </summary>
|
||||
/// <param name="ps"></param>
|
||||
/// <param name="pe"></param>
|
||||
/// <returns></returns>
|
||||
public double p_ang(gPoint ps, gPoint pe)
|
||||
{
|
||||
double a_ang = Math.Atan((pe.y - ps.y) / (pe.x - ps.x)) / Math.PI * 180;
|
||||
//象限角 转方位角 计算所属象限 并求得方位角
|
||||
if (pe.x >= ps.x && pe.y >= ps.y) //↗ 第一象限
|
||||
{
|
||||
return a_ang;
|
||||
}
|
||||
else if (!(pe.x >= ps.x) && pe.y >= ps.y) // ↖ 第二象限
|
||||
{
|
||||
return a_ang + 180;
|
||||
}
|
||||
else if (!(pe.x >= ps.x) && !(pe.y >= ps.y)) //↙ 第三象限
|
||||
{
|
||||
return a_ang + 180;
|
||||
}
|
||||
else if (pe.x >= ps.x && !(pe.y >= ps.y)) // ↘ 第四象限
|
||||
{
|
||||
return a_ang + 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
return a_ang;
|
||||
}
|
||||
}//求方位角
|
||||
/// <summary>
|
||||
/// 求增量坐标
|
||||
/// </summary>
|
||||
/// <param name="ps">起点</param>
|
||||
/// <param name="val">增量值</param>
|
||||
/// <param name="ang_direction">角度</param>
|
||||
/// <returns></returns>
|
||||
public gPP p_val_ang(gPP ps, double val, double ang_direction)
|
||||
{
|
||||
gPP pe = ps;
|
||||
pe.p.x = ps.p.x + val * Math.Cos(ang_direction * Math.PI / 180);
|
||||
pe.p.y = ps.p.y + val * Math.Sin(ang_direction * Math.PI / 180);
|
||||
return pe;
|
||||
}
|
||||
/// <summary>
|
||||
/// 求线Line长度
|
||||
/// </summary>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="is_calc_width"></param>
|
||||
/// <returns></returns>
|
||||
public double l_Length(gL l, bool is_calc_width = false)
|
||||
{
|
||||
if (is_calc_width)
|
||||
return Math.Sqrt((l.ps.x - l.pe.x) * (l.ps.x - l.pe.x) + (l.ps.y - l.pe.y) * (l.ps.y - l.pe.y)) + l.width / 1000;
|
||||
else
|
||||
return Math.Sqrt((l.ps.x - l.pe.x) * (l.ps.x - l.pe.x) + (l.ps.y - l.pe.y) * (l.ps.y - l.pe.y));
|
||||
}
|
||||
/// <summary>
|
||||
/// 弧Arc 转点P组集
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="val_">此数值表示:分段数值</param>
|
||||
/// <param name="type_">代表值数值类型 【0】弧长 【1】角度 【2】弦长 </param>
|
||||
/// <param name="is_avg">是否平均分布 </param>
|
||||
/// <returns></returns>
|
||||
public List<gPP> a_2Plist(gA a, double val_ = 0.1d, int type_ = 0, bool is_avg = false)
|
||||
{
|
||||
List<gPP> list_point = new List<gPP>();
|
||||
gPP tempP;
|
||||
tempP.p = a.ps;
|
||||
tempP.symbols = a.symbols;
|
||||
tempP.width = a.width;
|
||||
list_point.Add(tempP);
|
||||
|
||||
double avg_count;
|
||||
double angle_val = 0;
|
||||
double rad_ = p2p_di(a.pc, a.pe);
|
||||
double sum_alge = a_Angle(a);
|
||||
if (type_ == 1) // 【1】角度
|
||||
{
|
||||
angle_val = val_;
|
||||
avg_count = (int)(Math.Ceiling(sum_alge / angle_val)) - 1; // 总角度/单角度
|
||||
}
|
||||
else if (type_ == 2) //【2】弦长
|
||||
{
|
||||
angle_val = Math.Asin(val_ / (rad_ * 2)) * 360 / pi;
|
||||
avg_count = (int)(Math.Ceiling(sum_alge / angle_val)) - 1; // 总角度/单角度
|
||||
}
|
||||
else // 【0】弧长
|
||||
{
|
||||
angle_val = val_ * 180 / (pi * rad_);
|
||||
avg_count = (int)(Math.Ceiling(sum_alge / angle_val)) - 1; // 总角度/单角度
|
||||
//avg_count = (int)(Math.Ceiling(a_Lenght(a) / val_)) - 1; // 或 总弧长/单弧长
|
||||
}
|
||||
if (is_avg)
|
||||
angle_val = sum_alge / avg_count;
|
||||
if (avg_count > 1)
|
||||
{
|
||||
gPP centerP = tempP;
|
||||
centerP.p = a.pc;
|
||||
double angle_s = p_ang(a.pc, a.ps);
|
||||
if (a.ccw) { angle_val = 0 - angle_val; }
|
||||
for (int i = 1; i < avg_count; i++)
|
||||
{
|
||||
tempP = p_val_ang(centerP, rad_, angle_s - angle_val * i);
|
||||
list_point.Add(tempP);
|
||||
}
|
||||
}
|
||||
if (!(zero(a.ps.x - a.pe.x) && zero(a.ps.y - a.pe.y)))
|
||||
{
|
||||
tempP.p = a.pe;
|
||||
list_point.Add(tempP);
|
||||
}
|
||||
return list_point;
|
||||
}
|
||||
/// <summary>
|
||||
/// 返回两点之间欧氏距离
|
||||
/// </summary>
|
||||
/// <param name="p1"></param>
|
||||
/// <param name="p2"></param>
|
||||
/// <returns></returns>
|
||||
public double p2p_di(gPoint p1, gPoint p2)
|
||||
{
|
||||
return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
|
||||
}
|
||||
/// <summary>
|
||||
/// 求弧Arc圆心角 //后续改进 用叉积 与3P求角度求解 验证哪个效率高
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public double a_Angle(gA a)
|
||||
{
|
||||
double angle_s, angle_e, angle_sum;
|
||||
if (a.ccw)
|
||||
{
|
||||
angle_s = p_ang(a.pc, a.pe);
|
||||
angle_e = p_ang(a.pc, a.ps);
|
||||
}
|
||||
else
|
||||
{
|
||||
angle_s = p_ang(a.pc, a.ps);
|
||||
angle_e = p_ang(a.pc, a.pe);
|
||||
}
|
||||
if (angle_s == 360) { angle_s = 0; }
|
||||
if (angle_e >= angle_s)
|
||||
angle_sum = 360 - Math.Abs(angle_s - angle_e);
|
||||
else
|
||||
angle_sum = Math.Abs(angle_s - angle_e);
|
||||
return angle_sum;
|
||||
}
|
||||
View Code
|
||||
3.Point,PAD,Line,Arc数据结构
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 精简 PAD 数据类型
|
||||
/// </summary>
|
||||
public struct gPP
|
||||
{
|
||||
public gPP(double x_val, double y_val, double width_)
|
||||
{
|
||||
this.p = new gPoint(x_val, y_val);
|
||||
this.symbols = "r";
|
||||
this.width = width_;
|
||||
}
|
||||
public gPP(gPoint p_, double width_)
|
||||
{
|
||||
this.p = p_;
|
||||
this.symbols = "r";
|
||||
this.width = width_;
|
||||
}
|
||||
public gPP(gPoint p_, string symbols_, double width_)
|
||||
{
|
||||
this.p = p_;
|
||||
this.symbols = symbols_;
|
||||
this.width = width_;
|
||||
}
|
||||
public gPoint p;
|
||||
public string symbols;
|
||||
public double width;
|
||||
public static gPP operator +(gPP p1, gPP p2)
|
||||
{
|
||||
p1.p += p2.p;
|
||||
return p1;
|
||||
}
|
||||
public static gPP operator +(gPP p1, gPoint p2)
|
||||
{
|
||||
p1.p += p2;
|
||||
return p1;
|
||||
}
|
||||
public static gPP operator -(gPP p1, gPP p2)
|
||||
{
|
||||
p1.p -= p2.p;
|
||||
return p1;
|
||||
}
|
||||
public static gPP operator -(gPP p1, gPoint p2)
|
||||
{
|
||||
p1.p -= p2;
|
||||
return p1;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 点 数据类型 (XY)
|
||||
/// </summary>
|
||||
public struct gPoint
|
||||
{
|
||||
public gPoint(gPoint p_)
|
||||
{
|
||||
this.x = p_.x;
|
||||
this.y = p_.y;
|
||||
}
|
||||
public gPoint(double x_val, double y_val)
|
||||
{
|
||||
this.x = x_val;
|
||||
this.y = y_val;
|
||||
}
|
||||
public double x;
|
||||
public double y;
|
||||
public static gPoint operator +(gPoint p1, gPoint p2)
|
||||
{
|
||||
p1.x += p2.x;
|
||||
p1.y += p2.y;
|
||||
return p1;
|
||||
}
|
||||
public static gPoint operator -(gPoint p1, gPoint p2)
|
||||
{
|
||||
p1.x -= p2.x;
|
||||
p1.y -= p2.y;
|
||||
return p1;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Line 数据类型
|
||||
/// </summary>
|
||||
public struct gL
|
||||
{
|
||||
public gL(double ps_x, double ps_y, double pe_x, double pe_y, double width_)
|
||||
{
|
||||
this.ps = new gPoint(ps_x, ps_y);
|
||||
this.pe = new gPoint(pe_x, pe_y);
|
||||
this.negative = false;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gL(gPoint ps_, gPoint pe_, double width_)
|
||||
{
|
||||
this.ps = ps_;
|
||||
this.pe = pe_;
|
||||
this.negative = false;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gL(gPoint ps_, gPoint pe_, string symbols_, double width_)
|
||||
{
|
||||
this.ps = ps_;
|
||||
this.pe = pe_;
|
||||
this.negative = false;
|
||||
this.symbols = symbols_;
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gPoint ps;
|
||||
public gPoint pe;
|
||||
public bool negative;//polarity-- positive negative
|
||||
public string symbols;
|
||||
public string attribut;
|
||||
public double width;
|
||||
public static gL operator +(gL l1, gPoint move_p)
|
||||
{
|
||||
l1.ps += move_p;
|
||||
l1.pe += move_p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator +(gL l1, gP move_p)
|
||||
{
|
||||
l1.ps += move_p.p;
|
||||
l1.pe += move_p.p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator -(gL l1, gPoint move_p)
|
||||
{
|
||||
l1.ps -= move_p;
|
||||
l1.pe -= move_p;
|
||||
return l1;
|
||||
}
|
||||
public static gL operator -(gL l1, gP move_p)
|
||||
{
|
||||
l1.ps -= move_p.p;
|
||||
l1.pe -= move_p.p;
|
||||
return l1;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// ARC 数据类型
|
||||
/// </summary>
|
||||
public struct gA
|
||||
{
|
||||
public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_)
|
||||
{
|
||||
this.ps = new gPoint(ps_x, ps_y);
|
||||
this.pc = new gPoint(pc_x, pc_y);
|
||||
this.pe = new gPoint(pe_x, pe_y);
|
||||
this.negative = false;
|
||||
this.ccw = ccw_;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_ = false)
|
||||
{
|
||||
this.ps = ps_;
|
||||
this.pc = pc_;
|
||||
this.pe = pe_;
|
||||
this.negative = false;
|
||||
this.ccw = ccw_;
|
||||
this.symbols = "r";
|
||||
this.attribut = string.Empty;
|
||||
this.width = width_;
|
||||
}
|
||||
public gPoint ps;
|
||||
public gPoint pe;
|
||||
public gPoint pc;
|
||||
public bool negative;//polarity-- positive negative
|
||||
public bool ccw; //direction-- cw ccw
|
||||
public string symbols;
|
||||
public string attribut;
|
||||
public double width;
|
||||
public static gA operator +(gA arc1, gPoint move_p)
|
||||
{
|
||||
arc1.ps += move_p;
|
||||
arc1.pe += move_p;
|
||||
arc1.pc += move_p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator +(gA arc1, gP move_p)
|
||||
{
|
||||
arc1.ps += move_p.p;
|
||||
arc1.pe += move_p.p;
|
||||
arc1.pc += move_p.p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator -(gA arc1, gPoint move_p)
|
||||
{
|
||||
arc1.ps -= move_p;
|
||||
arc1.pe -= move_p;
|
||||
arc1.pc -= move_p;
|
||||
return arc1;
|
||||
}
|
||||
public static gA operator -(gA arc1, gP move_p)
|
||||
{
|
||||
arc1.ps -= move_p.p;
|
||||
arc1.pe -= move_p.p;
|
||||
arc1.pc -= move_p.p;
|
||||
return arc1;
|
||||
}
|
||||
}
|
||||
View Code
|
||||
359
DragDropHelper.cs
Normal file
359
DragDropHelper.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 拖放帮助类
|
||||
/// </summary>
|
||||
public static class DragDropHelper
|
||||
{
|
||||
private static System.Windows.Point _startPoint;
|
||||
private static bool _isDragging;
|
||||
private static DragToolTipWindow? _dragToolTipWindow;
|
||||
private static Window? _ownerWindow;
|
||||
private static System.Windows.Shapes.Rectangle? _insertionIndicator;
|
||||
private static int _insertionIndex = -1;
|
||||
private static DateTime? _lastUpdateTime;
|
||||
|
||||
/// <summary>
|
||||
/// 启用 ListView 的拖放功能
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据项类型</typeparam>
|
||||
/// <param name="listView">ListView 控件</param>
|
||||
/// <param name="insertionIndicator">插入位置指示器</param>
|
||||
public static void EnableDragDrop<T>(System.Windows.Controls.ListView listView, System.Windows.Shapes.Rectangle insertionIndicator) where T : class
|
||||
{
|
||||
_insertionIndicator = insertionIndicator;
|
||||
_ownerWindow = Window.GetWindow(listView);
|
||||
|
||||
listView.PreviewMouseLeftButtonDown += (sender, e) =>
|
||||
{
|
||||
_startPoint = e.GetPosition(null);
|
||||
_isDragging = false;
|
||||
};
|
||||
|
||||
listView.PreviewMouseMove += (sender, e) =>
|
||||
{
|
||||
if (e.LeftButton == MouseButtonState.Pressed && !_isDragging)
|
||||
{
|
||||
System.Windows.Point position = e.GetPosition(null);
|
||||
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
|
||||
{
|
||||
_isDragging = true;
|
||||
var listViewItem = FindAncestor<System.Windows.Controls.ListViewItem>((DependencyObject)e.OriginalSource);
|
||||
if (listViewItem != null)
|
||||
{
|
||||
var item = listView.ItemContainerGenerator.ItemFromContainer(listViewItem) as T;
|
||||
if (item != null)
|
||||
{
|
||||
// 获取屏幕坐标
|
||||
var screenPosition = listView.PointToScreen(e.GetPosition(listView));
|
||||
|
||||
// 创建并显示拖动提示
|
||||
CreateDragToolTip(item, screenPosition);
|
||||
|
||||
// 使用 QueryContinueDrag 事件来更新位置
|
||||
System.Windows.QueryContinueDragEventHandler queryContinueHandler = null;
|
||||
queryContinueHandler = (sender, e) =>
|
||||
{
|
||||
if (_dragToolTipWindow != null)
|
||||
{
|
||||
// 获取当前鼠标位置
|
||||
var currentPos = WinForms.Control.MousePosition;
|
||||
_dragToolTipWindow.UpdatePosition(new System.Windows.Point(currentPos.X, currentPos.Y));
|
||||
}
|
||||
|
||||
// 如果拖放结束,移除事件处理
|
||||
if (e.Action == System.Windows.DragAction.Drop || e.Action == System.Windows.DragAction.Cancel)
|
||||
{
|
||||
DragDrop.RemoveQueryContinueDragHandler(listView, queryContinueHandler);
|
||||
}
|
||||
};
|
||||
|
||||
DragDrop.AddQueryContinueDragHandler(listView, queryContinueHandler);
|
||||
|
||||
System.Windows.DataObject data = new System.Windows.DataObject(typeof(T), item);
|
||||
DragDrop.DoDragDrop(listViewItem, data, System.Windows.DragDropEffects.Move);
|
||||
|
||||
// 清理拖动提示
|
||||
CleanupDragToolTip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
listView.DragOver += (sender, e) =>
|
||||
{
|
||||
if (e.Data.GetDataPresent(typeof(T)))
|
||||
{
|
||||
e.Effects = System.Windows.DragDropEffects.Move;
|
||||
|
||||
// 更新插入位置指示器
|
||||
UpdateInsertionIndicator(listView, e.GetPosition(listView));
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Effects = System.Windows.DragDropEffects.None;
|
||||
}
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
listView.DragLeave += (sender, e) =>
|
||||
{
|
||||
HideInsertionIndicator();
|
||||
};
|
||||
|
||||
listView.Drop += (sender, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 隐藏插入指示器
|
||||
HideInsertionIndicator();
|
||||
|
||||
Debug.WriteLine($"[DragDrop] Drop事件触发 - OriginalSource类型: {e.OriginalSource?.GetType().Name}");
|
||||
Debug.WriteLine($"[DragDrop] Drop位置: ({e.GetPosition(listView).X}, {e.GetPosition(listView).Y})");
|
||||
|
||||
if (e.Data.GetDataPresent(typeof(T)))
|
||||
{
|
||||
var droppedItem = e.Data.GetData(typeof(T)) as T;
|
||||
if (droppedItem != null)
|
||||
{
|
||||
Debug.WriteLine($"[DragDrop] 拖放项: {droppedItem}");
|
||||
|
||||
// 检查是否是机台码刀具,如果是则不允许移动
|
||||
if (droppedItem is ToolItem toolItem && toolItem.ToolType == ToolType.MachineCode)
|
||||
{
|
||||
Debug.WriteLine("[DragDrop] 机台码刀具不允许移动");
|
||||
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
_isDragging = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var targetItem = FindAncestor<System.Windows.Controls.ListViewItem>((DependencyObject)e.OriginalSource);
|
||||
string ss = targetItem == null ? "NULL" : "找到";
|
||||
Debug.WriteLine($"[DragDrop] 目标ListViewItem: {ss}");
|
||||
|
||||
if (targetItem == null)
|
||||
{
|
||||
Debug.WriteLine("[DragDrop] 警告: 目标项为空,可能是拖放到列表外部");
|
||||
_isDragging = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var targetIndex = listView.ItemContainerGenerator.IndexFromContainer(targetItem);
|
||||
Debug.WriteLine($"[DragDrop] 目标索引: {targetIndex}");
|
||||
|
||||
var sourceIndex = listView.Items.IndexOf(droppedItem);
|
||||
Debug.WriteLine($"[DragDrop] 源索引: {sourceIndex}");
|
||||
|
||||
// 检查目标位置是否是机台码刀具
|
||||
var viewModel = listView.DataContext as MainWindowViewModel;
|
||||
if (viewModel != null && viewModel.Tools.Count > targetIndex)
|
||||
{
|
||||
var targetTool = viewModel.Tools[targetIndex];
|
||||
if (targetTool.ToolType == ToolType.MachineCode)
|
||||
{
|
||||
Debug.WriteLine("[DragDrop] 不能移动到机台码刀具位置");
|
||||
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
_isDragging = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceIndex != -1 && targetIndex != -1 && sourceIndex != targetIndex)
|
||||
{
|
||||
viewModel?.ReorderTools(sourceIndex, targetIndex);
|
||||
Debug.WriteLine($"[DragDrop] 重新排序完成: {sourceIndex} -> {targetIndex}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"[DragDrop] 跳过重新排序 - 源索引: {sourceIndex}, 目标索引: {targetIndex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[DragDrop] 异常: {ex.GetType().Name} - {ex.Message}");
|
||||
Debug.WriteLine($"[DragDrop] 堆栈跟踪: {ex.StackTrace}");
|
||||
System.Windows.MessageBox.Show($"拖放操作发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDragging = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找指定类型的父元素
|
||||
/// </summary>
|
||||
/// <typeparam name="T">父元素类型</typeparam>
|
||||
/// <param name="current">当前元素</param>
|
||||
/// <returns>找到的父元素,如果未找到则返回 null</returns>
|
||||
private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
|
||||
{
|
||||
Debug.WriteLine($"[FindAncestor] 开始查找类型 {typeof(T).Name},起始元素类型: {current?.GetType().Name ?? "NULL"}");
|
||||
int depth = 0;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
Debug.WriteLine($"[FindAncestor] 深度 {depth}: 当前类型 {current.GetType().Name}");
|
||||
|
||||
if (current is T ancestor)
|
||||
{
|
||||
Debug.WriteLine($"[FindAncestor] 找到匹配的祖先类型 {typeof(T).Name}");
|
||||
return ancestor;
|
||||
}
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
depth++;
|
||||
|
||||
if (depth > 10) // 防止无限循环
|
||||
{
|
||||
Debug.WriteLine("[FindAncestor] 警告: 搜索深度超过10层,停止搜索");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"[FindAncestor] 未找到类型 {typeof(T).Name} 的祖先");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理主窗口鼠标移动事件,更新拖动提示位置
|
||||
/// </summary>
|
||||
private static void OnOwnerWindowMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
if (_dragToolTipWindow != null && _isDragging)
|
||||
{
|
||||
// 获取屏幕坐标
|
||||
var windowPosition = e.GetPosition(_ownerWindow);
|
||||
var screenPosition = _ownerWindow.PointToScreen(windowPosition);
|
||||
_dragToolTipWindow.UpdatePosition(screenPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建拖动提示窗口
|
||||
/// </summary>
|
||||
private static void CreateDragToolTip<T>(T item, System.Windows.Point position) where T : class
|
||||
{
|
||||
if (item is ToolItem toolItem && _ownerWindow != null)
|
||||
{
|
||||
_dragToolTipWindow = new DragToolTipWindow();
|
||||
_dragToolTipWindow.SetToolInfo(toolItem);
|
||||
_dragToolTipWindow.UpdatePosition(position);
|
||||
// 不设置 Owner,这样窗口可以独立定位
|
||||
_dragToolTipWindow.Show();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理拖动提示窗口
|
||||
/// </summary>
|
||||
private static void CleanupDragToolTip()
|
||||
{
|
||||
if (_dragToolTipWindow != null)
|
||||
{
|
||||
_dragToolTipWindow.Close();
|
||||
_dragToolTipWindow = null;
|
||||
}
|
||||
|
||||
if (_ownerWindow != null)
|
||||
{
|
||||
_ownerWindow.MouseMove -= OnOwnerWindowMouseMove;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新插入位置指示器
|
||||
/// </summary>
|
||||
private static void UpdateInsertionIndicator(System.Windows.Controls.ListView listView, System.Windows.Point position)
|
||||
{
|
||||
if (_insertionIndicator == null) return;
|
||||
|
||||
// 使用节流机制,避免过于频繁的更新
|
||||
if (_lastUpdateTime != null && DateTime.Now - _lastUpdateTime < TimeSpan.FromMilliseconds(16))
|
||||
return;
|
||||
|
||||
_lastUpdateTime = DateTime.Now;
|
||||
|
||||
try
|
||||
{
|
||||
// 使用 VisualTreeHelper 找到鼠标下的元素
|
||||
var element = listView.InputHitTest(position) as DependencyObject;
|
||||
var listViewItem = FindAncestor<System.Windows.Controls.ListViewItem>(element);
|
||||
|
||||
if (listViewItem != null)
|
||||
{
|
||||
var index = listView.ItemContainerGenerator.IndexFromContainer(listViewItem);
|
||||
var itemBounds = listViewItem.TransformToAncestor(listView)
|
||||
.TransformBounds(new Rect(0, 0, listViewItem.ActualWidth, listViewItem.ActualHeight));
|
||||
|
||||
// 判断是在上半部分还是下半部分
|
||||
bool insertAfter = position.Y > itemBounds.Top + itemBounds.Height / 2;
|
||||
int targetIndex = insertAfter ? index + 1 : index;
|
||||
|
||||
if (targetIndex != _insertionIndex)
|
||||
{
|
||||
_insertionIndex = targetIndex;
|
||||
|
||||
// 更新指示器位置
|
||||
_insertionIndicator.Visibility = Visibility.Visible;
|
||||
|
||||
// 计算指示器位置
|
||||
double indicatorTop = insertAfter ? itemBounds.Bottom : itemBounds.Top;
|
||||
|
||||
// 使用 VerticalAlignment 和 Margin 结合来定位指示器
|
||||
_insertionIndicator.VerticalAlignment = VerticalAlignment.Top;
|
||||
_insertionIndicator.Margin = new Thickness(0, indicatorTop, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有找到目标项,检查是否在列表末尾
|
||||
if (position.Y > listView.ActualHeight - 20)
|
||||
{
|
||||
_insertionIndex = listView.Items.Count;
|
||||
// 在列表末尾显示指示器
|
||||
_insertionIndicator.Visibility = Visibility.Visible;
|
||||
_insertionIndicator.VerticalAlignment = VerticalAlignment.Top;
|
||||
_insertionIndicator.Margin = new Thickness(0, listView.ActualHeight - 2, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
HideInsertionIndicator();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[DragDrop] 更新插入指示器时发生错误: {ex.Message}");
|
||||
HideInsertionIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 隐藏插入位置指示器
|
||||
/// </summary>
|
||||
private static void HideInsertionIndicator()
|
||||
{
|
||||
if (_insertionIndicator != null)
|
||||
{
|
||||
_insertionIndicator.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
_insertionIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
DragToolTipWindow.cs
Normal file
114
DragToolTipWindow.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 拖动提示窗口,显示刀具信息
|
||||
/// </summary>
|
||||
public class DragToolTipWindow : Window
|
||||
{
|
||||
private readonly TextBlock _textBlock;
|
||||
|
||||
public DragToolTipWindow()
|
||||
{
|
||||
// 设置窗口样式
|
||||
WindowStyle = WindowStyle.None;
|
||||
AllowsTransparency = true;
|
||||
Background = System.Windows.Media.Brushes.Transparent;
|
||||
ShowInTaskbar = false;
|
||||
Topmost = true;
|
||||
IsHitTestVisible = false; // 不拦截鼠标事件
|
||||
SizeToContent = SizeToContent.WidthAndHeight; // 根据内容自动调整大小
|
||||
MaxWidth = 200; // 限制最大宽度
|
||||
MaxHeight = 50; // 限制最大高度
|
||||
|
||||
// 创建内容容器
|
||||
var border = new Border
|
||||
{
|
||||
Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(224, 255, 255, 255)),
|
||||
BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromRgb(0, 120, 212)),
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(3),
|
||||
Padding = new Thickness(5, 2, 5, 2),
|
||||
Effect = new System.Windows.Media.Effects.DropShadowEffect
|
||||
{
|
||||
Color = System.Windows.Media.Colors.Black,
|
||||
Direction = 315,
|
||||
ShadowDepth = 2,
|
||||
BlurRadius = 5,
|
||||
Opacity = 0.3
|
||||
}
|
||||
};
|
||||
|
||||
// 创建文本显示
|
||||
_textBlock = new TextBlock
|
||||
{
|
||||
FontWeight = FontWeights.Bold,
|
||||
FontSize = 10, // 减小字体大小
|
||||
TextTrimming = TextTrimming.CharacterEllipsis // 文本过长时显示省略号
|
||||
};
|
||||
|
||||
border.Child = _textBlock;
|
||||
Content = border;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置刀具信息
|
||||
/// </summary>
|
||||
/// <param name="tool">刀具项</param>
|
||||
public void SetToolInfo(ToolItem tool)
|
||||
{
|
||||
_textBlock.Text = $"T{tool.ToolNumber:D2} - {tool.Diameter:F3}mm";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新窗口位置
|
||||
/// </summary>
|
||||
/// <param name="position">鼠标位置</param>
|
||||
public void UpdatePosition(System.Windows.Point position)
|
||||
{
|
||||
// 将提示窗口显示在鼠标右下方,稍微偏移以避免遮挡
|
||||
// 确保窗口不会超出屏幕边界
|
||||
double left = position.X + 15;
|
||||
double top = position.Y - 35;
|
||||
|
||||
// 获取屏幕尺寸
|
||||
var screenWidth = SystemParameters.PrimaryScreenWidth;
|
||||
var screenHeight = SystemParameters.PrimaryScreenHeight;
|
||||
|
||||
// 先设置位置,然后获取实际尺寸
|
||||
Left = left;
|
||||
Top = top;
|
||||
|
||||
// 确保窗口不会超出右边界
|
||||
if (left + ActualWidth > screenWidth)
|
||||
{
|
||||
left = position.X - ActualWidth - 15;
|
||||
}
|
||||
|
||||
// 确保窗口不会超出左边界
|
||||
if (left < 0)
|
||||
{
|
||||
left = 5;
|
||||
}
|
||||
|
||||
// 确保窗口不会超出下边界
|
||||
if (top + ActualHeight > screenHeight)
|
||||
{
|
||||
top = position.Y - ActualHeight - 5;
|
||||
}
|
||||
|
||||
// 确保窗口不会超出上边界
|
||||
if (top < 0)
|
||||
{
|
||||
top = position.Y + 15;
|
||||
}
|
||||
|
||||
// 重新设置最终位置
|
||||
Left = left;
|
||||
Top = top;
|
||||
}
|
||||
}
|
||||
}
|
||||
680
DrillTapeProcessor.cs
Normal file
680
DrillTapeProcessor.cs
Normal file
@@ -0,0 +1,680 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
13
DrillTools.csproj
Normal file
13
DrillTools.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Platforms>x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
29
DrillTools.sln
Normal file
29
DrillTools.sln
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.0.11205.157 d18.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrillTools", "DrillTools.csproj", "{FC319F00-406B-4E5B-BB24-D9F76B02DF51}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FC319F00-406B-4E5B-BB24-D9F76B02DF51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FC319F00-406B-4E5B-BB24-D9F76B02DF51}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{FC319F00-406B-4E5B-BB24-D9F76B02DF51}.Debug|x86.Build.0 = Debug|x86
|
||||
{FC319F00-406B-4E5B-BB24-D9F76B02DF51}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FC319F00-406B-4E5B-BB24-D9F76B02DF51}.Release|x86.ActiveCfg = Release|x86
|
||||
{FC319F00-406B-4E5B-BB24-D9F76B02DF51}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1DA9466F-43EA-4554-BAFE-935FBF8D2909}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
180
GenerateReorderedDrillTape_Implementation.md
Normal file
180
GenerateReorderedDrillTape_Implementation.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# GenerateReorderedDrillTape 方法实现方案
|
||||
|
||||
## 目标
|
||||
将 `GenerateReorderedDrillTape` 静态扩展方法的功能升级,使其与 `ReorderAndRenumberTools` 方法完全一致。
|
||||
|
||||
## 当前问题分析
|
||||
现有的 `GenerateReorderedDrillTape` 方法只实现简单的重排功能,缺少以下关键特性:
|
||||
1. 刀具重新编号(T01, T02, T03...)
|
||||
2. 机台码智能处理(保持原始编号)
|
||||
3. 完整的钻带内容重构逻辑
|
||||
4. 数据映射和同步机制
|
||||
|
||||
## 新实现方案
|
||||
|
||||
### 1. 方法签名保持不变
|
||||
```csharp
|
||||
public static string GenerateReorderedDrillTape(string originalDrillTape, List<ToolItem> reorderedTools)
|
||||
```
|
||||
|
||||
### 2. 核心实现逻辑
|
||||
|
||||
#### 步骤1: 创建刀具编号映射
|
||||
```csharp
|
||||
// 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)
|
||||
{
|
||||
// 机台码刀具不允许重新编号
|
||||
if (tool.ToolType != ToolType.MachineCode)
|
||||
{
|
||||
originalToNewMapping[tool.ToolNumber] = newToolNumber;
|
||||
toolNumberMapping[tool.ToolNumber] = newToolNumber;
|
||||
tool.ToolNumber = newToolNumber++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 机台码刀具保持原始编号,但也要加入映射
|
||||
toolNumberMapping[tool.ToolNumber] = tool.ToolNumber;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 步骤2: 解析原始钻带内容
|
||||
```csharp
|
||||
// 解析原始钻带中的刀具定义行,保存参数信息
|
||||
var originalToolDefinitions = new Dictionary<int, string>();
|
||||
var headerLines = new List<string>();
|
||||
|
||||
var lines = originalDrillTape.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string trimmedLine = line.Trim();
|
||||
|
||||
if (trimmedLine == "%")
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 步骤3: 重构刀具定义部分
|
||||
```csharp
|
||||
// 按照新的刀具编号顺序输出刀具定义部分
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 步骤4: 处理坐标数据和机台码
|
||||
```csharp
|
||||
// 按新刀具编号顺序输出刀具切换行和对应的坐标数据
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 关键特性实现
|
||||
|
||||
#### 机台码处理逻辑
|
||||
- 机台码刀具保持原始编号不变
|
||||
- 机台码命令(M97/M98)和类型(A*/B*)正确保留
|
||||
- 机台码坐标数据完整保留
|
||||
|
||||
#### 数据映射机制
|
||||
- 创建原始编号到新编号的完整映射
|
||||
- 支持混合编号场景(部分重新编号)
|
||||
- 确保坐标数据正确绑定到对应刀具
|
||||
|
||||
#### 钻带内容重构
|
||||
- 完整保留原始刀具参数
|
||||
- 按新编号顺序重新组织钻带结构
|
||||
- 确保格式和语法正确性
|
||||
|
||||
### 4. 实现优势
|
||||
|
||||
1. **功能一致性**: 与 `ReorderAndRenumberTools` 方法功能完全一致
|
||||
2. **向后兼容**: 保持原有方法签名,现有调用代码无需修改
|
||||
3. **健壮性**: 完整的错误处理和边界条件检查
|
||||
4. **可维护性**: 清晰的代码结构和注释
|
||||
|
||||
### 5. 测试验证
|
||||
|
||||
需要验证以下场景:
|
||||
1. 普通刀具重排和重新编号
|
||||
2. 机台码刀具保持原始编号
|
||||
3. 混合场景(部分机台码、部分普通刀具)
|
||||
4. 坐标数据正确绑定
|
||||
5. 钻带格式正确性
|
||||
|
||||
## 实现建议
|
||||
|
||||
建议切换到 Code 模式进行实际的代码修改,因为 Architect 模式只能编辑 Markdown 文件。实现时应严格按照上述方案进行,确保与 `ReorderAndRenumberTools` 方法的行为完全一致。
|
||||
161
MainWindow.xaml
Normal file
161
MainWindow.xaml
Normal file
@@ -0,0 +1,161 @@
|
||||
<Window
|
||||
x:Class="DrillTools.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:DrillTools"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="钻带处理工具(支持拖入钻带文件)"
|
||||
Width="1000"
|
||||
Height="550"
|
||||
AllowDrop="True"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.DataContext>
|
||||
<local:MainWindowViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<ToolBar Grid.Row="0">
|
||||
<Button
|
||||
Name="LoadSampleDataButton"
|
||||
Click="LoadSampleDataButton_Click"
|
||||
Content="加载示例数据" />
|
||||
<Separator />
|
||||
<Button
|
||||
Name="LoadDrillTapeButton"
|
||||
Click="LoadDrillTapeButton_Click"
|
||||
Content="加载钻带文件" />
|
||||
<Button
|
||||
Name="ReorderToolsButton"
|
||||
Click="ReorderToolsButton_Click"
|
||||
Content="重排刀序" />
|
||||
<Button
|
||||
Name="ApplyOrderButton"
|
||||
Click="ApplyOrderButton_Click"
|
||||
Content="应用并保存"
|
||||
IsEnabled="{Binding HasOriginalFile}" />
|
||||
</ToolBar>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 刀具列表 -->
|
||||
<GroupBox Grid.Column="0" Header="刀具列表(可拖动排序)">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListView
|
||||
Name="ToolsListView"
|
||||
Grid.Row="0"
|
||||
AllowDrop="True"
|
||||
ItemsSource="{Binding Tools}"
|
||||
MouseDoubleClick="ToolsListView_MouseDoubleClick"
|
||||
SelectedItem="{Binding SelectedTool}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn
|
||||
Width="80"
|
||||
DisplayMemberBinding="{Binding ToolNumber, StringFormat=T{0:D2}}"
|
||||
Header="刀具编号" />
|
||||
<GridViewColumn
|
||||
Width="80"
|
||||
DisplayMemberBinding="{Binding Diameter, StringFormat=F3}"
|
||||
Header="孔径(mm)" />
|
||||
<GridViewColumn
|
||||
Width="70"
|
||||
DisplayMemberBinding="{Binding ToolTypeDisplay}"
|
||||
Header="类型" />
|
||||
<GridViewColumn
|
||||
Width="90"
|
||||
DisplayMemberBinding="{Binding ToolSuffixTypeDisplay}"
|
||||
Header="尾号类型" />
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource {x:Type ListViewItem}}" TargetType="ListViewItem">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#E0E0E0" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Background" Value="#0078D4" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
|
||||
<!-- 插入位置指示器 -->
|
||||
<Rectangle
|
||||
x:Name="InsertionIndicator"
|
||||
Grid.Row="0"
|
||||
Height="2"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Fill="#FF0078D4"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<!-- 上移/下移按钮 -->
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,5"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Name="MoveUpButton"
|
||||
Width="80"
|
||||
Height="35"
|
||||
Margin="5,0"
|
||||
Click="MoveUpButton_Click"
|
||||
Content="↑ 上移"
|
||||
IsEnabled="{Binding CanMoveUp}" />
|
||||
<Button
|
||||
Name="MoveDownButton"
|
||||
Width="80"
|
||||
Height="35"
|
||||
Margin="5,0"
|
||||
Click="MoveDownButton_Click"
|
||||
Content="↓ 下移"
|
||||
IsEnabled="{Binding CanMoveDown}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 钻带内容显示 -->
|
||||
<GroupBox Grid.Column="1" Header="钻带内容">
|
||||
<TextBox
|
||||
Name="DrillTapeTextBox"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsReadOnly="True"
|
||||
Style="{StaticResource CodeTextBoxStyle}"
|
||||
Text="{Binding DrillTapeContent}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<StatusBar Grid.Row="2">
|
||||
<StatusBarItem>
|
||||
<TextBlock Text="就绪" />
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</Window>
|
||||
216
MainWindow.xaml.cs
Normal file
216
MainWindow.xaml.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
var viewModel = new MainWindowViewModel();
|
||||
DataContext = viewModel;
|
||||
InitializeDragDrop();
|
||||
|
||||
// 测试修正后的重排刀序功能
|
||||
// TestReorderDemo.RunDemo();
|
||||
|
||||
// 测试机台码孔数计算功能
|
||||
//viewModel.TestMachineCodeHoleCalculation();
|
||||
|
||||
// 测试机台码处理修复效果
|
||||
viewModel.TestMachineCodeProcessingFix();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化拖放功能
|
||||
/// </summary>
|
||||
private void InitializeDragDrop()
|
||||
{
|
||||
DragDropHelper.EnableDragDrop<ToolItem>(ToolsListView, InsertionIndicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载示例数据按钮点击事件
|
||||
/// </summary>
|
||||
private void LoadSampleDataButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.LoadSampleData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载钻带文件按钮点击事件
|
||||
/// </summary>
|
||||
private void LoadDrillTapeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var openFileDialog = new Microsoft.Win32.OpenFileDialog
|
||||
{
|
||||
Filter = "钻带文件 (*.txt;*.drl;*.dr2;*.dpin)|*.txt;*.drl;*.dr2;*.dpin|所有文件 (*.*)|*.*",
|
||||
Title = "选择钻带文件"
|
||||
};
|
||||
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存原始文件路径
|
||||
ViewModel.OriginalFilePath = openFileDialog.FileName;
|
||||
|
||||
// 使用 cmd 命令读取加密钻带文件
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/c type \"{openFileDialog.FileName}\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
string drillTapeContent = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"加载钻带文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重排刀序按钮点击事件
|
||||
/// </summary>
|
||||
private void ReorderToolsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string reorderedDrillTape = ViewModel.ReorderAndRenumberTools();
|
||||
ViewModel.DrillTapeContent = reorderedDrillTape;
|
||||
System.Windows.MessageBox.Show("刀序重排完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"重排刀序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用顺序到钻带按钮点击事件
|
||||
/// </summary>
|
||||
private void ApplyOrderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string reorderedDrillTape = ViewModel.ApplyToolOrderToDrillTape();
|
||||
ViewModel.DrillTapeContent = reorderedDrillTape;
|
||||
System.Windows.MessageBox.Show("刀具顺序已应用到钻带", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 用户取消操作,不显示错误消息
|
||||
System.Diagnostics.Debug.WriteLine("用户取消了保存操作");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"应用刀具顺序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上移按钮点击事件
|
||||
/// </summary>
|
||||
private void MoveUpButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.MoveSelectedToolUp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下移按钮点击事件
|
||||
/// </summary>
|
||||
private void MoveDownButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.MoveSelectedToolDown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具列表双击事件
|
||||
/// </summary>
|
||||
private void ToolsListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is System.Windows.Controls.ListView listView && listView.SelectedItem is ToolItem selectedTool)
|
||||
{
|
||||
ViewModel.ShowToolDetail(selectedTool);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拖放文件进入窗口事件
|
||||
/// </summary>
|
||||
protected override void OnDragEnter(System.Windows.DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop))
|
||||
{
|
||||
e.Effects = System.Windows.DragDropEffects.Copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Effects = System.Windows.DragDropEffects.None;
|
||||
}
|
||||
base.OnDragEnter(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拖放文件到窗口事件
|
||||
/// </summary>
|
||||
protected override void OnDrop(System.Windows.DragEventArgs e)
|
||||
{
|
||||
if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop))
|
||||
{
|
||||
string[] files = (string[])e.Data.GetData(System.Windows.DataFormats.FileDrop);
|
||||
if (files.Length > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存原始文件路径
|
||||
ViewModel.OriginalFilePath = files[0];
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/c type \"{files[0]}\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
string drillTapeContent = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"加载钻带文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
base.OnDrop(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
1607
MainWindowViewModel.cs
Normal file
1607
MainWindowViewModel.cs
Normal file
File diff suppressed because it is too large
Load Diff
406
SlotHoleCalculator.cs
Normal file
406
SlotHoleCalculator.cs
Normal file
@@ -0,0 +1,406 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 二维点结构
|
||||
/// </summary>
|
||||
public struct Point2D
|
||||
{
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public string OriginalString { get; set; } // 存储原始坐标字符串
|
||||
|
||||
public Point2D(double x, double y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
OriginalString = "";
|
||||
}
|
||||
|
||||
public Point2D(double x, double y, string originalString)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
OriginalString = originalString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从钻带坐标字符串创建点(格式:X+/-数字Y+/-数字)
|
||||
/// </summary>
|
||||
public static Point2D ParseFromDrillString(string drillString)
|
||||
{
|
||||
var pattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)";
|
||||
var match = Regex.Match(drillString, pattern);
|
||||
if (match.Success)
|
||||
{
|
||||
double x = double.Parse(match.Groups[1].Value) / 1000.0; // 转换为mm
|
||||
double y = double.Parse(match.Groups[2].Value) / 1000.0; // 转换为mm
|
||||
return new Point2D(x, y, drillString); // 保存原始字符串
|
||||
}
|
||||
throw new ArgumentException($"无效的钻带坐标格式: {drillString}");
|
||||
}
|
||||
|
||||
public static Point2D operator +(Point2D p1, Point2D p2) => new Point2D(p1.X + p2.X, p1.Y + p2.Y);
|
||||
public static Point2D operator -(Point2D p1, Point2D p2) => new Point2D(p1.X - p2.X, p1.Y - p2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 线段槽孔结构
|
||||
/// </summary>
|
||||
public struct LineSlot
|
||||
{
|
||||
public Point2D StartPoint { get; set; }
|
||||
public Point2D EndPoint { get; set; }
|
||||
public double Width { get; set; } // 槽孔宽度/孔径(单位:mm)
|
||||
|
||||
public LineSlot(Point2D startPoint, Point2D endPoint, double width)
|
||||
{
|
||||
StartPoint = startPoint;
|
||||
EndPoint = endPoint;
|
||||
Width = width;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取槽孔长度
|
||||
/// </summary>
|
||||
public double Length => Math.Sqrt(Math.Pow(StartPoint.X - EndPoint.X, 2) + Math.Pow(StartPoint.Y - EndPoint.Y, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弧段槽孔结构
|
||||
/// </summary>
|
||||
public struct ArcSlot
|
||||
{
|
||||
public Point2D StartPoint { get; set; }
|
||||
public Point2D EndPoint { get; set; }
|
||||
public Point2D CenterPoint { get; set; }
|
||||
public double Width { get; set; } // 槽孔宽度/孔径(单位:mm)
|
||||
public bool CounterClockwise { get; set; } // 是否逆时针方向
|
||||
|
||||
public ArcSlot(Point2D startPoint, Point2D endPoint, Point2D centerPoint, double width, bool counterClockwise = false)
|
||||
{
|
||||
StartPoint = startPoint;
|
||||
EndPoint = endPoint;
|
||||
CenterPoint = centerPoint;
|
||||
Width = width;
|
||||
CounterClockwise = counterClockwise;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取弧段半径
|
||||
/// </summary>
|
||||
public double Radius => Math.Sqrt(Math.Pow(StartPoint.X - CenterPoint.X, 2) + Math.Pow(StartPoint.Y - CenterPoint.Y, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 槽孔钻孔数量计算工具类
|
||||
/// 提供与CAM350一致的槽孔孔数计算方法
|
||||
/// </summary>
|
||||
public static class SlotHoleCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认凸位高度值(CAM350标准,单位:mm)
|
||||
/// </summary>
|
||||
private const double DEFAULT_TOLERANCE = 0.0127;
|
||||
|
||||
/// <summary>
|
||||
/// 计算线段槽孔的钻孔数量
|
||||
/// </summary>
|
||||
/// <param name="slot">线段槽孔参数</param>
|
||||
/// <param name="tolerance">凸位高度值,默认为0.0127mm(CAM350标准)</param>
|
||||
/// <returns>需要的钻孔数量</returns>
|
||||
public static int CalculateLineSlotHoleCount(LineSlot slot, double tolerance = DEFAULT_TOLERANCE)
|
||||
{
|
||||
// 计算孔半径
|
||||
double radius = slot.Width / 2.0;
|
||||
|
||||
// 计算槽孔长度
|
||||
double slotLength = slot.Length;
|
||||
|
||||
// 计算孔中心距
|
||||
double holeCenterDistance = CalculateHoleCenterDistance(radius, tolerance);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = (int)Math.Abs(Math.Floor(-slotLength / holeCenterDistance)) + 1;
|
||||
|
||||
return holeCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算弧段槽孔的钻孔数量
|
||||
/// </summary>
|
||||
/// <param name="slot">弧段槽孔参数</param>
|
||||
/// <param name="tolerance">凸位高度值,默认为0.0127mm(CAM350标准)</param>
|
||||
/// <returns>需要的钻孔数量</returns>
|
||||
public static int CalculateArcSlotHoleCount(ArcSlot slot, double tolerance = DEFAULT_TOLERANCE)
|
||||
{
|
||||
// 计算孔半径
|
||||
double radius = slot.Width / 2.0;
|
||||
|
||||
// 计算弧段长度
|
||||
double arcLength = CalculateArcLength(slot);
|
||||
|
||||
// 计算孔中心距
|
||||
double holeCenterDistance = CalculateHoleCenterDistance(radius, tolerance);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = (int)Math.Abs(Math.Floor(-arcLength / holeCenterDistance)) + 1;
|
||||
|
||||
return holeCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算线段槽孔的钻孔位置
|
||||
/// </summary>
|
||||
/// <param name="slot">线段槽孔参数</param>
|
||||
/// <param name="tolerance">凸位高度值,默认为0.0127mm(CAM350标准)</param>
|
||||
/// <returns>钻孔位置列表</returns>
|
||||
public static List<Point2D> CalculateLineSlotHolePositions(LineSlot slot, double tolerance = DEFAULT_TOLERANCE)
|
||||
{
|
||||
List<Point2D> positions = new List<Point2D>();
|
||||
|
||||
// 计算孔半径
|
||||
double radius = slot.Width / 2.0;
|
||||
|
||||
// 计算孔中心距
|
||||
double holeCenterDistance = CalculateHoleCenterDistance(radius, tolerance);
|
||||
|
||||
// 计算槽孔长度
|
||||
double slotLength = slot.Length;
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = CalculateLineSlotHoleCount(slot, tolerance);
|
||||
|
||||
// 计算方位角
|
||||
double angle = CalculateAngle(slot.StartPoint, slot.EndPoint);
|
||||
|
||||
// 添加起点
|
||||
positions.Add(slot.StartPoint);
|
||||
|
||||
// 计算中间点
|
||||
if (holeCount > 2)
|
||||
{
|
||||
double avgDistance = slotLength / (holeCount - 1);
|
||||
Point2D currentPoint = slot.StartPoint;
|
||||
|
||||
for (int i = 1; i < holeCount - 1; i++)
|
||||
{
|
||||
currentPoint = CalculateNextPoint(currentPoint, avgDistance, angle);
|
||||
positions.Add(currentPoint);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加终点
|
||||
if (holeCount > 1)
|
||||
{
|
||||
positions.Add(slot.EndPoint);
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算弧段槽孔的钻孔位置
|
||||
/// </summary>
|
||||
/// <param name="slot">弧段槽孔参数</param>
|
||||
/// <param name="tolerance">凸位高度值,默认为0.0127mm(CAM350标准)</param>
|
||||
/// <returns>钻孔位置列表</returns>
|
||||
public static List<Point2D> CalculateArcSlotHolePositions(ArcSlot slot, double tolerance = DEFAULT_TOLERANCE)
|
||||
{
|
||||
List<Point2D> positions = new List<Point2D>();
|
||||
|
||||
// 计算孔半径
|
||||
double radius = slot.Width / 2.0;
|
||||
|
||||
// 计算孔中心距
|
||||
double holeCenterDistance = CalculateHoleCenterDistance(radius, tolerance);
|
||||
|
||||
// 计算弧段半径
|
||||
double arcRadius = slot.Radius;
|
||||
|
||||
// 计算弧段圆心角
|
||||
double arcAngle = CalculateArcAngle(slot);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = CalculateArcSlotHoleCount(slot, tolerance);
|
||||
|
||||
// 计算角度步长
|
||||
double angleStep = arcAngle / (holeCount - 1);
|
||||
|
||||
// 计算起始角度
|
||||
double startAngle = CalculateAngle(slot.CenterPoint, slot.StartPoint);
|
||||
|
||||
// 添加起点
|
||||
positions.Add(slot.StartPoint);
|
||||
|
||||
// 计算中间点
|
||||
if (holeCount > 2)
|
||||
{
|
||||
double currentAngle = startAngle;
|
||||
if (slot.CounterClockwise)
|
||||
{
|
||||
currentAngle -= angleStep;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentAngle += angleStep;
|
||||
}
|
||||
|
||||
for (int i = 1; i < holeCount - 1; i++)
|
||||
{
|
||||
Point2D point = CalculatePointOnArc(slot.CenterPoint, arcRadius, currentAngle);
|
||||
positions.Add(point);
|
||||
|
||||
if (slot.CounterClockwise)
|
||||
{
|
||||
currentAngle -= angleStep;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentAngle += angleStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加终点
|
||||
if (holeCount > 1)
|
||||
{
|
||||
positions.Add(slot.EndPoint);
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从钻带G85命令解析线段槽孔
|
||||
/// </summary>
|
||||
/// <param name="g85Command">G85命令字符串,格式:XxxxYxxxG85XxxxYxxx</param>
|
||||
/// <param name="width">槽孔宽度</param>
|
||||
/// <returns>解析出的线段槽孔</returns>
|
||||
public static LineSlot ParseLineSlotFromG85(string g85Command, double width)
|
||||
{
|
||||
var pattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)G85X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)";
|
||||
var match = Regex.Match(g85Command, pattern);
|
||||
if (match.Success)
|
||||
{
|
||||
// 提取原始坐标字符串
|
||||
string startX = match.Groups[1].Value;
|
||||
string startY = match.Groups[2].Value;
|
||||
string endX = match.Groups[3].Value;
|
||||
string endY = match.Groups[4].Value;
|
||||
|
||||
Point2D startPoint = new Point2D(
|
||||
double.Parse(startX) / 1000.0,
|
||||
double.Parse(startY) / 1000.0,
|
||||
$"X{startX}Y{startY}"
|
||||
);
|
||||
Point2D endPoint = new Point2D(
|
||||
double.Parse(endX) / 1000.0,
|
||||
double.Parse(endY) / 1000.0,
|
||||
$"X{endX}Y{endY}"
|
||||
);
|
||||
|
||||
return new LineSlot(startPoint, endPoint, width);
|
||||
}
|
||||
throw new ArgumentException($"无效的G85命令格式: {g85Command}");
|
||||
}
|
||||
|
||||
// 辅助方法
|
||||
|
||||
/// <summary>
|
||||
/// 计算两点间欧氏距离
|
||||
/// </summary>
|
||||
private static double CalculateDistance(Point2D p1, Point2D p2)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算方位角(度数)
|
||||
/// </summary>
|
||||
private static double CalculateAngle(Point2D startPoint, Point2D endPoint)
|
||||
{
|
||||
double angleRad = Math.Atan2(endPoint.Y - startPoint.Y, endPoint.X - startPoint.X);
|
||||
double angleDeg = angleRad * 180.0 / Math.PI;
|
||||
|
||||
// 转换为0-360度范围
|
||||
if (angleDeg < 0)
|
||||
angleDeg += 360;
|
||||
|
||||
return angleDeg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算弧段长度
|
||||
/// </summary>
|
||||
public static double CalculateArcLength(ArcSlot arc)
|
||||
{
|
||||
double radius = arc.Radius;
|
||||
double angleDeg = CalculateArcAngle(arc);
|
||||
double angleRad = angleDeg * Math.PI / 180.0;
|
||||
return radius * angleRad;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算弧段圆心角(度数)
|
||||
/// </summary>
|
||||
private static double CalculateArcAngle(ArcSlot arc)
|
||||
{
|
||||
double startAngle = CalculateAngle(arc.CenterPoint, arc.StartPoint);
|
||||
double endAngle = CalculateAngle(arc.CenterPoint, arc.EndPoint);
|
||||
|
||||
double angleSum;
|
||||
if (arc.CounterClockwise)
|
||||
{
|
||||
if (endAngle >= startAngle)
|
||||
angleSum = 360 - Math.Abs(startAngle - endAngle);
|
||||
else
|
||||
angleSum = Math.Abs(startAngle - endAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (startAngle >= endAngle)
|
||||
angleSum = 360 - Math.Abs(startAngle - endAngle);
|
||||
else
|
||||
angleSum = Math.Abs(startAngle - endAngle);
|
||||
}
|
||||
|
||||
return angleSum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算孔中心距
|
||||
/// </summary>
|
||||
private static double CalculateHoleCenterDistance(double radius, double tolerance)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(radius, 2) - Math.Pow(radius - tolerance, 2)) * 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据起点、距离和角度计算下一个点
|
||||
/// </summary>
|
||||
private static Point2D CalculateNextPoint(Point2D startPoint, double distance, double angleDeg)
|
||||
{
|
||||
double angleRad = angleDeg * Math.PI / 180.0;
|
||||
return new Point2D(
|
||||
startPoint.X + distance * Math.Cos(angleRad),
|
||||
startPoint.Y + distance * Math.Sin(angleRad)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据圆心、半径和角度计算圆弧上的点
|
||||
/// </summary>
|
||||
private static Point2D CalculatePointOnArc(Point2D centerPoint, double radius, double angleDeg)
|
||||
{
|
||||
double angleRad = angleDeg * Math.PI / 180.0;
|
||||
return new Point2D(
|
||||
centerPoint.X + radius * Math.Cos(angleRad),
|
||||
centerPoint.Y + radius * Math.Sin(angleRad)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
241
SlotHoleCalculatorExamples.cs
Normal file
241
SlotHoleCalculatorExamples.cs
Normal file
@@ -0,0 +1,241 @@
|
||||
using System;
|
||||
using DrillTools;
|
||||
|
||||
namespace DrillTools.Examples
|
||||
{
|
||||
/// <summary>
|
||||
/// 槽孔计算器使用示例
|
||||
/// </summary>
|
||||
public class SlotHoleCalculatorExamples
|
||||
{
|
||||
/// <summary>
|
||||
/// 示例1:计算线段槽孔孔数
|
||||
/// </summary>
|
||||
public static void CalculateLineSlotHoleCountExample()
|
||||
{
|
||||
Console.WriteLine("=== 线段槽孔孔数计算示例 ===");
|
||||
|
||||
// 创建线段槽孔(参考readme.md中的测试数据)
|
||||
var slot = new LineSlot(
|
||||
new Point2D(-69.659, 16.450), // 起点
|
||||
new Point2D(-94.159, 16.450), // 终点
|
||||
1.601 // 孔径
|
||||
);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
|
||||
Console.WriteLine($"槽孔长度: {slot.Length:F3} mm");
|
||||
Console.WriteLine($"槽孔宽度: {slot.Width} mm");
|
||||
Console.WriteLine($"计算孔数: {holeCount}");
|
||||
Console.WriteLine($"预期孔数: 88 (CAM350标准)");
|
||||
Console.WriteLine($"结果验证: {(holeCount == 88 ? "✓ 通过" : "✗ 失败")}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 示例2:从G85命令解析并计算
|
||||
/// </summary>
|
||||
public static void ParseFromG85Example()
|
||||
{
|
||||
Console.WriteLine("=== G85命令解析示例 ===");
|
||||
|
||||
// G85命令字符串(来自钻带文件)
|
||||
string g85Command = "X-069659Y016450G85X-094159Y016450";
|
||||
double width = 1.601;
|
||||
|
||||
try
|
||||
{
|
||||
// 解析G85命令
|
||||
var slot = SlotHoleCalculator.ParseLineSlotFromG85(g85Command, width);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
|
||||
Console.WriteLine($"G85命令: {g85Command}");
|
||||
Console.WriteLine($"解析起点: ({slot.StartPoint.X}, {slot.StartPoint.Y})");
|
||||
Console.WriteLine($"解析终点: ({slot.EndPoint.X}, {slot.EndPoint.Y})");
|
||||
Console.WriteLine($"槽孔宽度: {slot.Width} mm");
|
||||
Console.WriteLine($"计算孔数: {holeCount}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"解析失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 示例3:计算钻孔位置
|
||||
/// </summary>
|
||||
public static void CalculateHolePositionsExample()
|
||||
{
|
||||
Console.WriteLine("=== 钻孔位置计算示例 ===");
|
||||
|
||||
// 创建线段槽孔
|
||||
var slot = new LineSlot(
|
||||
new Point2D(0, 0),
|
||||
new Point2D(10, 0),
|
||||
1.0
|
||||
);
|
||||
|
||||
// 计算钻孔位置
|
||||
var positions = SlotHoleCalculator.CalculateLineSlotHolePositions(slot);
|
||||
|
||||
Console.WriteLine($"槽孔长度: {slot.Length} mm");
|
||||
Console.WriteLine($"钻孔数量: {positions.Count}");
|
||||
Console.WriteLine("钻孔位置列表:");
|
||||
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
{
|
||||
Console.WriteLine($" 孔 {i + 1}: ({positions[i].X:F3}, {positions[i].Y:F3})");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 示例4:弧段槽孔计算
|
||||
/// </summary>
|
||||
public static void CalculateArcSlotExample()
|
||||
{
|
||||
Console.WriteLine("=== 弧段槽孔计算示例 ===");
|
||||
|
||||
// 创建弧段槽孔(半圆)
|
||||
var arcSlot = new ArcSlot(
|
||||
new Point2D(10, 0), // 起点
|
||||
new Point2D(0, 10), // 终点
|
||||
new Point2D(0, 0), // 圆心
|
||||
1.0, // 宽度
|
||||
false // 顺时针
|
||||
);
|
||||
|
||||
// 计算孔数
|
||||
int holeCount = SlotHoleCalculator.CalculateArcSlotHoleCount(arcSlot);
|
||||
|
||||
// 计算钻孔位置
|
||||
var positions = SlotHoleCalculator.CalculateArcSlotHolePositions(arcSlot);
|
||||
|
||||
Console.WriteLine($"弧段半径: {arcSlot.Radius:F3} mm");
|
||||
Console.WriteLine($"弧段长度: {SlotHoleCalculator.CalculateArcLength(arcSlot):F3} mm");
|
||||
Console.WriteLine($"计算孔数: {holeCount}");
|
||||
Console.WriteLine($"钻孔位置数量: {positions.Count}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 示例5:批量计算不同孔径的槽孔
|
||||
/// </summary>
|
||||
public static void BatchCalculationExample()
|
||||
{
|
||||
Console.WriteLine("=== 批量计算示例 ===");
|
||||
|
||||
// 不同孔径的槽孔(参考readme.md中的测试数据)
|
||||
var diameters = new[] { 1.601, 1.701, 1.801, 1.901, 2.001, 1.501, 1.401, 1.301, 1.201, 1.101, 1.001, 0.706, 0.506 };
|
||||
var expectedCounts = new[] { 88, 85, 83, 81, 79, 91, 94, 97, 101, 106, 111, 132, 156 };
|
||||
|
||||
Console.WriteLine("孔径(mm)\t计算孔数\t预期孔数\t验证结果");
|
||||
Console.WriteLine("----------------------------------------");
|
||||
|
||||
for (int i = 0; i < diameters.Length; i++)
|
||||
{
|
||||
var slot = new LineSlot(
|
||||
new Point2D(-69.659, 16.450),
|
||||
new Point2D(-94.159, 16.450),
|
||||
diameters[i]
|
||||
);
|
||||
|
||||
int actualCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
int expectedCount = expectedCounts[i];
|
||||
bool passed = actualCount == expectedCount;
|
||||
|
||||
Console.WriteLine($"{diameters[i]}\t{actualCount}\t{expectedCount}\t{(passed ? "✓" : "✗")}");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 示例6:自定义凸位高度值
|
||||
/// </summary>
|
||||
public static void CustomToleranceExample()
|
||||
{
|
||||
Console.WriteLine("=== 自定义凸位高度值示例 ===");
|
||||
|
||||
var slot = new LineSlot(
|
||||
new Point2D(0, 0),
|
||||
new Point2D(10, 0),
|
||||
1.0
|
||||
);
|
||||
|
||||
// 使用默认凸位高度值
|
||||
int defaultHoleCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
|
||||
// 使用自定义凸位高度值
|
||||
double customTolerance = 0.01; // 0.01mm
|
||||
int customHoleCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot, customTolerance);
|
||||
|
||||
Console.WriteLine($"默认凸位高度值 (0.0127mm): {defaultHoleCount} 个孔");
|
||||
Console.WriteLine($"自定义凸位高度值 (0.01mm): {customHoleCount} 个孔");
|
||||
Console.WriteLine($"差异: {Math.Abs(defaultHoleCount - customHoleCount)} 个孔");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 示例7:处理实际钻带数据
|
||||
/// </summary>
|
||||
public static void ProcessRealDrillTapeExample()
|
||||
{
|
||||
Console.WriteLine("=== 处理实际钻带数据示例 ===");
|
||||
|
||||
// 模拟钻带数据中的槽孔命令
|
||||
var g85Commands = new[]
|
||||
{
|
||||
"X-069659Y016450G85X-094159Y016450", // T01 - 1.601mm
|
||||
"X-181341Y195550G85X-156841Y195550", // T02 - 1.601mm
|
||||
"X-181341Y389550G85X-156841Y389550", // T03 - 1.601mm
|
||||
};
|
||||
|
||||
var toolDiameters = new[] { 1.601, 1.601, 1.601 };
|
||||
var expectedHoleCounts = new[] { 88, 88, 88 };
|
||||
|
||||
Console.WriteLine("刀具\t孔径(mm)\tG85命令\t\t\t\t计算孔数\t预期孔数\t验证结果");
|
||||
Console.WriteLine("--------------------------------------------------------------------------------");
|
||||
|
||||
for (int i = 0; i < g85Commands.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var slot = SlotHoleCalculator.ParseLineSlotFromG85(g85Commands[i], toolDiameters[i]);
|
||||
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
|
||||
bool passed = holeCount == expectedHoleCounts[i];
|
||||
|
||||
Console.WriteLine($"T{i + 1:D2}\t{toolDiameters[i]}\t{g85Commands[i]}\t{holeCount}\t\t{expectedHoleCounts[i]}\t\t{(passed ? "✓" : "✗")}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"T{i + 1:D2}\t{toolDiameters[i]}\t{g85Commands[i]}\t解析失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行所有示例
|
||||
/// </summary>
|
||||
public static void RunAllExamples()
|
||||
{
|
||||
Console.WriteLine("槽孔计算器使用示例");
|
||||
Console.WriteLine("==================");
|
||||
Console.WriteLine();
|
||||
|
||||
CalculateLineSlotHoleCountExample();
|
||||
ParseFromG85Example();
|
||||
CalculateHolePositionsExample();
|
||||
CalculateArcSlotExample();
|
||||
BatchCalculationExample();
|
||||
CustomToleranceExample();
|
||||
ProcessRealDrillTapeExample();
|
||||
|
||||
Console.WriteLine("所有示例运行完成!");
|
||||
}
|
||||
}
|
||||
}
|
||||
98
ToolDetailViewModel.cs
Normal file
98
ToolDetailViewModel.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 刀具详情窗口视图模型
|
||||
/// </summary>
|
||||
public class ToolDetailViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private ToolItem? _tool;
|
||||
|
||||
/// <summary>
|
||||
/// 刀具对象
|
||||
/// </summary>
|
||||
public ToolItem? Tool
|
||||
{
|
||||
get => _tool;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _tool, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(WindowTitle));
|
||||
OnPropertyChanged(nameof(HoleLocationsHeader));
|
||||
OnPropertyChanged(nameof(FormattedHoleLocations));
|
||||
OnPropertyChanged(nameof(IsMachineCodeTool));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口标题
|
||||
/// </summary>
|
||||
public string WindowTitle => Tool != null ? $"刀具详情 - T{Tool.ToolNumber:D2}" : "刀具详情";
|
||||
|
||||
/// <summary>
|
||||
/// 孔位信息标题
|
||||
/// </summary>
|
||||
public string HoleLocationsHeader => Tool != null ? $"孔位信息 (共{Tool.HoleLocations?.Count ?? 0}个)" : "孔位信息";
|
||||
|
||||
/// <summary>
|
||||
/// 格式化后的孔位信息
|
||||
/// </summary>
|
||||
public string FormattedHoleLocations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Tool?.HoleLocations == null || Tool.HoleLocations.Count == 0)
|
||||
{
|
||||
return "无孔位数据";
|
||||
}
|
||||
|
||||
return string.Join(Environment.NewLine, Tool.HoleLocations);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为机台码刀具
|
||||
/// </summary>
|
||||
public bool IsMachineCodeTool => Tool?.ToolType == ToolType.MachineCode;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public ToolDetailViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的刀具初始化视图模型
|
||||
/// </summary>
|
||||
/// <param name="tool">刀具对象</param>
|
||||
public ToolDetailViewModel(ToolItem tool) : this()
|
||||
{
|
||||
Tool = tool;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
118
ToolDetailWindow.xaml
Normal file
118
ToolDetailWindow.xaml
Normal file
@@ -0,0 +1,118 @@
|
||||
<Window x:Class="DrillTools.ToolDetailWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:DrillTools"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{Binding WindowTitle}"
|
||||
Width="600"
|
||||
Height="700"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.DataContext>
|
||||
<local:ToolDetailViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<GroupBox Grid.Row="0" Header="基本信息" Margin="0,0,0,10">
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="刀具编号:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.ToolNumber, StringFormat=T{0:D2}, Mode=OneWay}" Margin="5" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="孔径(mm):" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.Diameter, StringFormat=F3, Mode=OneWay}" Margin="5" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="刀具类型:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.ToolTypeDisplay, Mode=OneWay}" Margin="5" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 分类信息 -->
|
||||
<GroupBox Grid.Row="1" Header="分类信息" Margin="0,0,0,10">
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="尾号类型:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.ToolSuffixTypeDisplay, Mode=OneWay}" Margin="5" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="刀具大类:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.ToolCategoryDisplay, Mode=OneWay}" Margin="5" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 机台码信息 (仅当刀具类型为机台码时显示) -->
|
||||
<GroupBox Grid.Row="2"
|
||||
Header="机台码信息"
|
||||
Margin="0,0,0,10"
|
||||
Visibility="{Binding IsMachineCodeTool, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="机台码类型:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.MachineCodeType, Mode=OneWay}" Margin="5" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="机台码命令:" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" IsReadOnly="True" Text="{Binding Tool.MachineCodeCommand, Mode=OneWay}" Margin="5" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 孔位信息 -->
|
||||
<GroupBox Grid.Row="3" Header="{Binding HoleLocationsHeader}">
|
||||
<TextBox IsReadOnly="True"
|
||||
Text="{Binding FormattedHoleLocations, Mode=OneWay}"
|
||||
FontFamily="{StaticResource CodeFontFamily}"
|
||||
FontSize="{StaticResource CodeFontSize}"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
TextWrapping="NoWrap"
|
||||
Margin="5" />
|
||||
</GroupBox>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<StackPanel Grid.Row="4"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Margin="0,15,0,0">
|
||||
<Button Width="80"
|
||||
Height="30"
|
||||
Content="关闭"
|
||||
IsCancel="True"
|
||||
Click="CloseButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
38
ToolDetailWindow.xaml.cs
Normal file
38
ToolDetailWindow.xaml.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// ToolDetailWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ToolDetailWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public ToolDetailWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的刀具初始化窗口
|
||||
/// </summary>
|
||||
/// <param name="tool">刀具对象</param>
|
||||
public ToolDetailWindow(ToolItem tool) : this()
|
||||
{
|
||||
if (DataContext is ToolDetailViewModel viewModel)
|
||||
{
|
||||
viewModel.Tool = tool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭按钮点击事件
|
||||
/// </summary>
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
309
ToolItem.cs
Normal file
309
ToolItem.cs
Normal file
@@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 刀具类型枚举
|
||||
/// </summary>
|
||||
public enum ToolType
|
||||
{
|
||||
Regular, // 圆孔刀具
|
||||
Slot, // 槽孔刀具
|
||||
MachineCode // 机台码刀具
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具尾号类型枚举
|
||||
/// </summary>
|
||||
public enum ToolSuffixType
|
||||
{
|
||||
Drill, // 0 - 钻针
|
||||
Slot, // 1 - 槽刀
|
||||
EASlot, // 2 - EA型槽刀
|
||||
DustSlot, // 3 - 粉尘刀(槽刀)
|
||||
DeburrSlot, // 4 - 去毛刺刀(槽刀)
|
||||
NonStandard, // 5 - 非标刀
|
||||
EASlot2, // 6 - EA型槽刀
|
||||
Special // 7 - 特殊刀具
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具大类枚举
|
||||
/// </summary>
|
||||
public enum ToolCategory
|
||||
{
|
||||
Drill, // 钻针
|
||||
Slot, // 槽刀(包含槽刀、粉尘刀、去毛刺刀)
|
||||
EA, // EA刀(EA型槽刀)
|
||||
NonStandard,// 非标刀
|
||||
Special // 特殊刀具
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具信息数据模型
|
||||
/// </summary>
|
||||
public class ToolItem : INotifyPropertyChanged
|
||||
{
|
||||
private int _toolNumber;
|
||||
private double _diameter;
|
||||
|
||||
// private int _regularHoles; // 孔数功能已移除
|
||||
// private int _slotHoles; // 孔数功能已移除
|
||||
// private int _totalHoles; // 孔数功能已移除
|
||||
private string _parameters = string.Empty;
|
||||
|
||||
private ToolType _toolType;
|
||||
private string _machineCodeType = string.Empty;
|
||||
private string _machineCodeCommand = string.Empty;
|
||||
private ToolSuffixType _toolSuffixType;
|
||||
private ToolCategory _toolCategory;
|
||||
private List<string> _holeLocations = new();
|
||||
|
||||
/// <summary>
|
||||
/// 刀具编号
|
||||
/// </summary>
|
||||
public int ToolNumber
|
||||
{
|
||||
get => _toolNumber;
|
||||
set => SetProperty(ref _toolNumber, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 孔径(mm)
|
||||
/// </summary>
|
||||
public double Diameter
|
||||
{
|
||||
get => _diameter;
|
||||
set => SetProperty(ref _diameter, value);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 普通孔数
|
||||
// /// </summary>
|
||||
// public int RegularHoles
|
||||
// {
|
||||
// get => _regularHoles;
|
||||
// set => SetProperty(ref _regularHoles, value);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 槽孔数
|
||||
// /// </summary>
|
||||
// public int SlotHoles
|
||||
// {
|
||||
// get => _slotHoles;
|
||||
// set => SetProperty(ref _slotHoles, value);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 总孔数
|
||||
// /// </summary>
|
||||
// public int TotalHoles
|
||||
// {
|
||||
// get => _totalHoles;
|
||||
// set => SetProperty(ref _totalHoles, value);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 钻机参数
|
||||
/// </summary>
|
||||
public string Parameters
|
||||
{
|
||||
get => _parameters;
|
||||
set => SetProperty(ref _parameters, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具类型
|
||||
/// </summary>
|
||||
public ToolType ToolType
|
||||
{
|
||||
get => _toolType;
|
||||
set => SetProperty(ref _toolType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 机台码类型 (A或B)
|
||||
/// </summary>
|
||||
public string MachineCodeType
|
||||
{
|
||||
get => _machineCodeType;
|
||||
set => SetProperty(ref _machineCodeType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 机台码命令 (M97或M98)
|
||||
/// </summary>
|
||||
public string MachineCodeCommand
|
||||
{
|
||||
get => _machineCodeCommand;
|
||||
set => SetProperty(ref _machineCodeCommand, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具尾号类型
|
||||
/// </summary>
|
||||
public ToolSuffixType ToolSuffixType
|
||||
{
|
||||
get => _toolSuffixType;
|
||||
set => SetProperty(ref _toolSuffixType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具大类
|
||||
/// </summary>
|
||||
public ToolCategory ToolCategory
|
||||
{
|
||||
get => _toolCategory;
|
||||
set => SetProperty(ref _toolCategory, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 孔位数据列表
|
||||
/// </summary>
|
||||
public List<string> HoleLocations
|
||||
{
|
||||
get => _holeLocations;
|
||||
set => SetProperty(ref _holeLocations, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刀具类型显示文本
|
||||
/// </summary>
|
||||
public string ToolTypeDisplay => ToolType switch
|
||||
{
|
||||
ToolType.Regular => "圆孔",
|
||||
ToolType.Slot => "槽孔",
|
||||
ToolType.MachineCode => "机台码",
|
||||
_ => "未知"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 刀具尾号类型显示文本
|
||||
/// </summary>
|
||||
public string ToolSuffixTypeDisplay => GetToolSuffixTypeDisplay(ToolSuffixType);
|
||||
|
||||
/// <summary>
|
||||
/// 刀具大类显示文本
|
||||
/// </summary>
|
||||
public string ToolCategoryDisplay => GetToolCategoryDisplay(ToolCategory);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据孔径尾号判断刀具尾号类型
|
||||
/// </summary>
|
||||
/// <param name="diameter">孔径</param>
|
||||
/// <returns>刀具尾号类型</returns>
|
||||
public static ToolSuffixType GetToolSuffixType(double diameter)
|
||||
{
|
||||
// 特殊孔径优先判断
|
||||
if (Math.Abs(diameter - 1.049) < 0.001 ||
|
||||
Math.Abs(diameter - 3.175) < 0.001 ||
|
||||
Math.Abs(diameter - 0.499) < 0.001)
|
||||
{
|
||||
return ToolSuffixType.Drill; // 固定为圆孔
|
||||
}
|
||||
|
||||
// 获取孔径的小数部分最后一位
|
||||
string diameterStr = diameter.ToString("F3");
|
||||
if (diameterStr.Length >= 4 && diameterStr.Contains('.'))
|
||||
{
|
||||
char lastChar = diameterStr[diameterStr.Length - 1];
|
||||
int suffix = int.Parse(lastChar.ToString());
|
||||
|
||||
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,
|
||||
8 => ToolSuffixType.Drill,
|
||||
9 => ToolSuffixType.Drill,
|
||||
_ => ToolSuffixType.NonStandard
|
||||
};
|
||||
}
|
||||
|
||||
return ToolSuffixType.NonStandard; // 默认为非标刀
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据刀具尾号类型获取刀具大类
|
||||
/// </summary>
|
||||
/// <param name="suffixType">刀具尾号类型</param>
|
||||
/// <returns>刀具大类</returns>
|
||||
public 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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取刀具尾号类型的显示文本
|
||||
/// </summary>
|
||||
/// <param name="suffixType">刀具尾号类型</param>
|
||||
/// <returns>显示文本</returns>
|
||||
public static string GetToolSuffixTypeDisplay(ToolSuffixType suffixType)
|
||||
{
|
||||
return suffixType switch
|
||||
{
|
||||
ToolSuffixType.Drill => "钻针",
|
||||
ToolSuffixType.Slot => "槽刀",
|
||||
ToolSuffixType.EASlot => "EA型槽刀",
|
||||
ToolSuffixType.DustSlot => "粉尘刀",
|
||||
ToolSuffixType.DeburrSlot => "去毛刺刀",
|
||||
ToolSuffixType.NonStandard => "非标刀",
|
||||
ToolSuffixType.EASlot2 => "EA型槽刀",
|
||||
ToolSuffixType.Special => "特殊刀具",
|
||||
_ => "未知"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取刀具大类的显示文本
|
||||
/// </summary>
|
||||
/// <param name="category">刀具大类</param>
|
||||
/// <returns>显示文本</returns>
|
||||
public static string GetToolCategoryDisplay(ToolCategory category)
|
||||
{
|
||||
return category switch
|
||||
{
|
||||
ToolCategory.Drill => "钻针",
|
||||
ToolCategory.Slot => "槽刀",
|
||||
ToolCategory.EA => "EA刀",
|
||||
ToolCategory.NonStandard => "非标刀",
|
||||
ToolCategory.Special => "特殊刀",
|
||||
_ => "未知"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
12
demo_drl_files/p20030722a1-cs.dpin
Normal file
12
demo_drl_files/p20030722a1-cs.dpin
Normal file
@@ -0,0 +1,12 @@
|
||||
M48
|
||||
T01C3.050
|
||||
T02C1.900
|
||||
%
|
||||
T01
|
||||
X-154500Y549000
|
||||
X154500Y562000
|
||||
X154500Y038000
|
||||
X-154500Y038000
|
||||
T02
|
||||
X045112Y593500
|
||||
M30
|
||||
6580
demo_drl_files/p20033811c0-a2-cs.drl
Normal file
6580
demo_drl_files/p20033811c0-a2-cs.drl
Normal file
File diff suppressed because it is too large
Load Diff
3359
demo_drl_files/p20033995a0-cs.drl
Normal file
3359
demo_drl_files/p20033995a0-cs.drl
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user