feat: DRL钻带更改参数功能 — 支持.drl文件从A01-A14参数文件按直径替换H/Z/S/F/U参数段
This commit is contained in:
62
App.xaml.cs
62
App.xaml.cs
@@ -53,6 +53,7 @@ namespace DrillTools
|
||||
string extension = Path.GetExtension(filePath).ToLowerInvariant();
|
||||
bool canScaleDrillTape = extension == ".dr2" || extension == ".dpin";
|
||||
bool isRouFile = extension == ".rou" || extension == ".rou1" || extension == ".rou2" || extension == ".roux";
|
||||
bool isDrlFile = extension == ".drl";
|
||||
double minDrill = viewModel.MinDrillDiameter;
|
||||
double minSlot = viewModel.MinSlotDiameter;
|
||||
double minEA = viewModel.MinEADiameter;
|
||||
@@ -85,7 +86,8 @@ namespace DrillTools
|
||||
canClearParameters,
|
||||
canGeneratePpDrillTape,
|
||||
canScaleDrillTape,
|
||||
canReplaceRouParameters: isRouFile);
|
||||
canReplaceRouParameters: isRouFile,
|
||||
canReplaceDrlParameters: isDrlFile);
|
||||
selectionWindow.ShowDialog();
|
||||
|
||||
switch (selectionWindow.SelectedAction)
|
||||
@@ -114,6 +116,10 @@ namespace DrillTools
|
||||
PerformRouParameterReplace(filePath);
|
||||
Shutdown();
|
||||
break;
|
||||
case StartupAction.ReplaceDrlParameters:
|
||||
PerformDrlParameterReplace(filePath);
|
||||
Shutdown();
|
||||
break;
|
||||
default:
|
||||
Shutdown();
|
||||
break;
|
||||
@@ -363,6 +369,60 @@ namespace DrillTools
|
||||
}
|
||||
}
|
||||
|
||||
private static void PerformDrlParameterReplace(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
string paramDir = @"C:\Users\14244\source\repos\Aoh\DrillTools\DRL-canshu";
|
||||
#else
|
||||
string paramDir = @"\\ah-file-2\workdata\Drill\DRILLING\etc";
|
||||
#endif
|
||||
if (!Directory.Exists(paramDir))
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
$"参数文件目录不存在:\n{paramDir}",
|
||||
"错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var selectionWindow = new DrlParameterSelectionWindow(paramDir);
|
||||
selectionWindow.ShowDialog();
|
||||
|
||||
if (selectionWindow.SelectedFilePath == null)
|
||||
return;
|
||||
|
||||
DrlParameterReplacer.ReplaceParameters(filePath, selectionWindow.SelectedFilePath, out var unmatchedDiameters);
|
||||
|
||||
string message = $"参数替换完成:\n{filePath}\n原文件已备份为:\n{Path.GetFileName(filePath)}.bak";
|
||||
|
||||
if (unmatchedDiameters.Count > 0)
|
||||
{
|
||||
var diaStrings = unmatchedDiameters.Select(d => d.ToString("F3", CultureInfo.InvariantCulture)).ToList();
|
||||
message += $"\n\n⚠ 以下直径未在参数文件中找到匹配,已保留原行:\n{string.Join(", ", diaStrings)}";
|
||||
}
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
message,
|
||||
"参数替换完成",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
$"参数替换失败:\n{ex.Message}",
|
||||
"错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 MainWindowViewModel.Tools 独立遍历计算叠板用最小刀径。
|
||||
/// 分类逻辑与 UpdateMinDiameterInfo 一致(按尾号使用标准),不排除 0.749mm;
|
||||
|
||||
248
DrlParameterReplacer.cs
Normal file
248
DrlParameterReplacer.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// DRL 钻带参数替换:将 A01-A14 参数文件中的 H/Z/S/F/U 参数替换到 .drl 文件的刀径定义行。
|
||||
/// 与 ROU 不同,DRL 刀径行原本已有参数,此处为整体替换而非追加。
|
||||
/// </summary>
|
||||
internal static class DrlParameterReplacer
|
||||
{
|
||||
private const int Gb2312CodePage = 936;
|
||||
private const double DiameterTolerance = 0.001;
|
||||
|
||||
/// <summary>
|
||||
/// 匹配参数文件行:T00C<直径>H<参数...>
|
||||
/// 例:T00C0.749H02500Z+0.000S105.00F105.0U1000.0
|
||||
/// </summary>
|
||||
private static readonly Regex ParamLinePattern = new(
|
||||
@"^T00C(?<diameter>\d+(?:\.\d+)?)(?<params>H.+)$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// 匹配 .drl 刀径定义行:T<刀号>C<直径> + 参数段
|
||||
/// 例:T01C0.799H05000Z+0.000S060.00F105.0U0700.0
|
||||
/// → prefix="T01C0.799", params="H05000Z+0.000S060.00F105.0U0700.0"
|
||||
/// </summary>
|
||||
private static readonly Regex ToolLinePattern = new(
|
||||
@"^\s*(?<prefix>T\d+C\d+(?:\.\d+)?)(?<params>H.+)$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// 匹配 .drl 刀径行直径部分,用于提取直径数值
|
||||
/// </summary>
|
||||
private static readonly Regex ToolLineDiameterPattern = new(
|
||||
@"^T\d+C(?<diameter>\d+(?:\.\d+)?)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// 匹配参数文件第一行(描述注释行):;<描述>
|
||||
/// </summary>
|
||||
private static readonly Regex DescriptionLinePattern = new(
|
||||
@"^;(.+)$",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// 执行参数替换。
|
||||
/// </summary>
|
||||
/// <param name="drlFilePath">.drl 文件完整路径</param>
|
||||
/// <param name="paramFilePath">A01-A14 参数文件完整路径</param>
|
||||
/// <param name="unmatchedDiameters">未在参数文件中找到的直径列表</param>
|
||||
public static void ReplaceParameters(string drlFilePath, string paramFilePath, out List<double> unmatchedDiameters)
|
||||
{
|
||||
// 1. 读取参数文件,提取第一行描述和直径→参数字符串映射
|
||||
string paramContent = CommandTypeFileReader.ReadAllText(paramFilePath);
|
||||
var paramLines = DrillTapeLineParser.SplitLines(paramContent);
|
||||
|
||||
string paramDescription = ExtractDescription(paramLines);
|
||||
if (string.IsNullOrEmpty(paramDescription))
|
||||
throw new InvalidOperationException("参数文件第一行未找到有效的描述信息(格式:;<描述>)");
|
||||
|
||||
var paramDict = LoadParameterDictionary(paramLines);
|
||||
|
||||
if (paramDict.Count == 0)
|
||||
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>H...)");
|
||||
|
||||
// 2. 读取 .drl 文件
|
||||
string drlContent = CommandTypeFileReader.ReadAllText(drlFilePath);
|
||||
|
||||
// 3. 执行替换(同时收集未匹配直径)
|
||||
var unmatchedSet = new HashSet<double>();
|
||||
string replacedContent = ReplaceParametersInContent(drlContent, paramDict, paramDescription, unmatchedSet);
|
||||
|
||||
// 4. 备份原文件
|
||||
string backupFilePath = drlFilePath + ".bak";
|
||||
File.Copy(drlFilePath, backupFilePath, true);
|
||||
|
||||
// 5. 写回文件
|
||||
using var writer = new StreamWriter(drlFilePath, false, Encoding.GetEncoding(Gb2312CodePage));
|
||||
writer.Write(replacedContent);
|
||||
|
||||
unmatchedDiameters = new List<double>(unmatchedSet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从参数文件行中提取第一行描述(去掉前导分号)。
|
||||
/// </summary>
|
||||
private static string ExtractDescription(IReadOnlyList<DrillTapeLine> paramLines)
|
||||
{
|
||||
foreach (var line in paramLines)
|
||||
{
|
||||
string trimmed = line.Text.Trim();
|
||||
var match = DescriptionLinePattern.Match(trimmed);
|
||||
if (match.Success)
|
||||
return match.Groups[1].Value.Trim();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从参数文件加载直径→参数字符串(从 H 开始到行尾)的映射。
|
||||
/// </summary>
|
||||
private static Dictionary<double, string> LoadParameterDictionary(IReadOnlyList<DrillTapeLine> paramLines)
|
||||
{
|
||||
var dict = new Dictionary<double, string>();
|
||||
|
||||
foreach (var line in paramLines)
|
||||
{
|
||||
string trimmed = line.Text.Trim();
|
||||
var match = ParamLinePattern.Match(trimmed);
|
||||
if (!match.Success)
|
||||
continue;
|
||||
|
||||
if (!double.TryParse(match.Groups["diameter"].Value,
|
||||
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
|
||||
continue;
|
||||
|
||||
diameter = Math.Round(diameter, 3);
|
||||
|
||||
string paramString = match.Groups["params"].Value;
|
||||
|
||||
// 同一直径后面覆盖前面
|
||||
dict[diameter] = paramString;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在 .drl 文件内容中执行参数替换:
|
||||
/// 1. 第2行(注释行)替换为参数文件的描述
|
||||
/// 2. M48~% 区域内按直径匹配替换刀径行的参数段
|
||||
/// </summary>
|
||||
private static string ReplaceParametersInContent(
|
||||
string drlContent,
|
||||
Dictionary<double, string> paramDict,
|
||||
string paramDescription,
|
||||
HashSet<double> unmatchedDiameters)
|
||||
{
|
||||
var lines = DrillTapeLineParser.SplitLines(drlContent);
|
||||
|
||||
// 找到头部边界
|
||||
int headerStart = -1; // M48 所在行
|
||||
int headerEnd = -1; // % 所在行
|
||||
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
string trimmed = lines[i].Text.Trim();
|
||||
if (headerStart < 0 && trimmed == "M48")
|
||||
headerStart = i;
|
||||
else if (headerStart >= 0 && trimmed == "%")
|
||||
{
|
||||
headerEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (headerStart < 0 || headerEnd < 0)
|
||||
throw new InvalidOperationException("DRL 文件格式异常:未找到 M48 或 % 头部标记");
|
||||
|
||||
var builder = new StringBuilder(drlContent.Length + 1024);
|
||||
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
if (i == headerStart + 1)
|
||||
{
|
||||
// 第2行:替换为参数文件的描述注释行
|
||||
string trailing = GetTrailingWhitespace(lines[i]);
|
||||
builder.Append(';');
|
||||
builder.Append(paramDescription);
|
||||
builder.Append(trailing);
|
||||
builder.Append(lines[i].LineEnding);
|
||||
}
|
||||
else if (i > headerStart && i < headerEnd)
|
||||
{
|
||||
// 头部区域(M48 和 % 之间,排除已处理的第2行)
|
||||
string trimmed = lines[i].Text.Trim();
|
||||
var match = ToolLinePattern.Match(trimmed);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
string prefix = match.Groups["prefix"].Value;
|
||||
string trailing = GetTrailingWhitespace(lines[i]);
|
||||
|
||||
// 从 prefix 中提取直径
|
||||
var diaMatch = ToolLineDiameterPattern.Match(prefix);
|
||||
if (diaMatch.Success &&
|
||||
double.TryParse(diaMatch.Groups["diameter"].Value,
|
||||
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
|
||||
{
|
||||
double roundedDiameter = Math.Round(diameter, 3);
|
||||
|
||||
if (paramDict.TryGetValue(roundedDiameter, out string? newParams))
|
||||
{
|
||||
builder.Append(prefix);
|
||||
builder.Append(newParams);
|
||||
builder.Append(trailing);
|
||||
builder.Append(lines[i].LineEnding);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 未匹配:保留原行
|
||||
unmatchedDiameters.Add(roundedDiameter);
|
||||
builder.Append(lines[i].Text);
|
||||
builder.Append(lines[i].LineEnding);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 无法提取直径,保留原行
|
||||
builder.Append(lines[i].Text);
|
||||
builder.Append(lines[i].LineEnding);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非刀径行(如其他注释等)原样保留
|
||||
builder.Append(lines[i].Text);
|
||||
builder.Append(lines[i].LineEnding);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// % 之后或其他区域原样保留
|
||||
builder.Append(lines[i].Text);
|
||||
builder.Append(lines[i].LineEnding);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取行尾空白字符。
|
||||
/// </summary>
|
||||
private static string GetTrailingWhitespace(DrillTapeLine line)
|
||||
{
|
||||
string original = line.Text;
|
||||
int trimmedLength = original.TrimEnd().Length;
|
||||
return trimmedLength < original.Length ? original[trimmedLength..] : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
DrlParameterSelectionWindow.xaml
Normal file
32
DrlParameterSelectionWindow.xaml
Normal file
@@ -0,0 +1,32 @@
|
||||
<Window x:Class="DrillTools.DrlParameterSelectionWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="选择钻带参数"
|
||||
Width="500"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="请选择要使用的钻带参数文件:" Margin="0,0,0,10" />
|
||||
|
||||
<ListBox Grid.Row="1"
|
||||
Name="FileListBox"
|
||||
Height="280"
|
||||
Margin="0,0,0,15"
|
||||
DisplayMemberPath="Description" />
|
||||
|
||||
<StackPanel Grid.Row="2" HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Button Name="OkButton" Width="80" Height="28" Content="确定" Margin="0,0,15,0"
|
||||
Click="OkButton_Click" IsDefault="True" />
|
||||
<Button Name="CancelButton" Width="80" Height="28" Content="取消"
|
||||
Click="CancelButton_Click" IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
105
DrlParameterSelectionWindow.xaml.cs
Normal file
105
DrlParameterSelectionWindow.xaml.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 钻带参数文件选择窗口,列出 A01-A14 文件并以第一行描述文本展示供用户选择。
|
||||
/// </summary>
|
||||
public partial class DrlParameterSelectionWindow : Window
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户选中的文件完整路径;若取消则为 null。
|
||||
/// </summary>
|
||||
public string? SelectedFilePath { get; private set; }
|
||||
|
||||
public DrlParameterSelectionWindow(string paramDirectory)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var items = new List<ParamFileItem>();
|
||||
for (int i = 1; i <= 14; i++)
|
||||
{
|
||||
string fileName = $"A{i:D2}";
|
||||
string filePath = Path.Combine(paramDirectory, fileName);
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
continue;
|
||||
|
||||
string description = ReadFirstLineDescription(filePath);
|
||||
items.Add(new ParamFileItem
|
||||
{
|
||||
FileName = fileName,
|
||||
FilePath = filePath,
|
||||
Description = $"{fileName} — {description}"
|
||||
});
|
||||
}
|
||||
|
||||
FileListBox.ItemsSource = items;
|
||||
OkButton.IsEnabled = false;
|
||||
|
||||
FileListBox.SelectionChanged += (_, _) =>
|
||||
{
|
||||
OkButton.IsEnabled = FileListBox.SelectedItem != null;
|
||||
};
|
||||
|
||||
FileListBox.MouseDoubleClick += (_, _) =>
|
||||
{
|
||||
if (FileListBox.SelectedItem is ParamFileItem item)
|
||||
{
|
||||
SelectedFilePath = item.FilePath;
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取参数文件的第一行描述文本(去掉前导分号)。
|
||||
/// </summary>
|
||||
private static string ReadFirstLineDescription(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
string content = CommandTypeFileReader.ReadAllText(filePath);
|
||||
var lines = DrillTapeLineParser.SplitLines(content);
|
||||
if (lines.Count > 0)
|
||||
{
|
||||
string firstLine = lines[0].Text.Trim();
|
||||
if (firstLine.StartsWith(";"))
|
||||
return firstLine[1..].Trim();
|
||||
return firstLine;
|
||||
}
|
||||
return "(无法读取)";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "(无法读取)";
|
||||
}
|
||||
}
|
||||
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (FileListBox.SelectedItem is ParamFileItem item)
|
||||
{
|
||||
SelectedFilePath = item.FilePath;
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private class ParamFileItem
|
||||
{
|
||||
public string FileName { get; init; } = string.Empty;
|
||||
public string FilePath { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,7 @@
|
||||
<Button Width="120" Height="30" Content="导出孔数" Margin="0,0,15,0" Click="ExportHoleCount_Click"/>
|
||||
<Button Name="ClearParametersButton" Width="120" Height="30" Content="清空参数" Margin="0,0,15,0" Click="ClearParameters_Click"/>
|
||||
<Button Name="ReplaceRouParametersButton" Width="120" Height="30" Content="更改参数" Margin="0,0,15,0" Click="ReplaceRouParameters_Click" Visibility="Collapsed"/>
|
||||
<Button Name="ReplaceDrlParametersButton" Width="120" Height="30" Content="更改参数" Margin="0,0,15,0" Click="ReplaceDrlParameters_Click" Visibility="Collapsed"/>
|
||||
<Button Name="GeneratePpDrillTapeButton" Width="120" Height="30" Content="生成PP钻带" Margin="0,0,15,0" Click="GeneratePpDrillTape_Click"/>
|
||||
<Button Name="ScaleDrillTapeButton" Width="120" Height="30" Content="二钻涨缩" Click="ScaleDrillTape_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace DrillTools
|
||||
ClearParameters,
|
||||
GeneratePpDrillTape,
|
||||
ScaleDrillTape,
|
||||
ReplaceRouParameters
|
||||
ReplaceRouParameters,
|
||||
ReplaceDrlParameters
|
||||
}
|
||||
|
||||
public partial class StartupSelectionWindow : Window
|
||||
@@ -19,7 +20,8 @@ namespace DrillTools
|
||||
|
||||
public StartupSelectionWindow(StartupSelectionViewModel viewModel,
|
||||
bool canClearParameters = false, bool canGeneratePpDrillTape = false,
|
||||
bool canScaleDrillTape = false, bool canReplaceRouParameters = false)
|
||||
bool canScaleDrillTape = false, bool canReplaceRouParameters = false,
|
||||
bool canReplaceDrlParameters = false)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
@@ -27,6 +29,7 @@ namespace DrillTools
|
||||
GeneratePpDrillTapeButton.Visibility = canGeneratePpDrillTape ? Visibility.Visible : Visibility.Collapsed;
|
||||
ScaleDrillTapeButton.Visibility = canScaleDrillTape ? Visibility.Visible : Visibility.Collapsed;
|
||||
ReplaceRouParametersButton.Visibility = canReplaceRouParameters ? Visibility.Visible : Visibility.Collapsed;
|
||||
ReplaceDrlParametersButton.Visibility = canReplaceDrlParameters ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void AdjustToolOrder_Click(object sender, RoutedEventArgs e)
|
||||
@@ -64,5 +67,11 @@ namespace DrillTools
|
||||
SelectedAction = StartupAction.ReplaceRouParameters;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ReplaceDrlParameters_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectedAction = StartupAction.ReplaceDrlParameters;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user