diff --git a/App.xaml.cs b/App.xaml.cs index 0770562..3b8bb8f 100644 --- a/App.xaml.cs +++ b/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); + } + } + /// /// 从 MainWindowViewModel.Tools 独立遍历计算叠板用最小刀径。 /// 分类逻辑与 UpdateMinDiameterInfo 一致(按尾号使用标准),不排除 0.749mm; diff --git a/DrlParameterReplacer.cs b/DrlParameterReplacer.cs new file mode 100644 index 0000000..9b7394e --- /dev/null +++ b/DrlParameterReplacer.cs @@ -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 +{ + /// + /// DRL 钻带参数替换:将 A01-A14 参数文件中的 H/Z/S/F/U 参数替换到 .drl 文件的刀径定义行。 + /// 与 ROU 不同,DRL 刀径行原本已有参数,此处为整体替换而非追加。 + /// + internal static class DrlParameterReplacer + { + private const int Gb2312CodePage = 936; + private const double DiameterTolerance = 0.001; + + /// + /// 匹配参数文件行:T00C<直径>H<参数...> + /// 例:T00C0.749H02500Z+0.000S105.00F105.0U1000.0 + /// + private static readonly Regex ParamLinePattern = new( + @"^T00C(?\d+(?:\.\d+)?)(?H.+)$", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + /// + /// 匹配 .drl 刀径定义行:T<刀号>C<直径> + 参数段 + /// 例:T01C0.799H05000Z+0.000S060.00F105.0U0700.0 + /// → prefix="T01C0.799", params="H05000Z+0.000S060.00F105.0U0700.0" + /// + private static readonly Regex ToolLinePattern = new( + @"^\s*(?T\d+C\d+(?:\.\d+)?)(?H.+)$", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + /// + /// 匹配 .drl 刀径行直径部分,用于提取直径数值 + /// + private static readonly Regex ToolLineDiameterPattern = new( + @"^T\d+C(?\d+(?:\.\d+)?)", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + /// + /// 匹配参数文件第一行(描述注释行):;<描述> + /// + private static readonly Regex DescriptionLinePattern = new( + @"^;(.+)$", + RegexOptions.Compiled); + + /// + /// 执行参数替换。 + /// + /// .drl 文件完整路径 + /// A01-A14 参数文件完整路径 + /// 未在参数文件中找到的直径列表 + public static void ReplaceParameters(string drlFilePath, string paramFilePath, out List 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(); + 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(unmatchedSet); + } + + /// + /// 从参数文件行中提取第一行描述(去掉前导分号)。 + /// + private static string ExtractDescription(IReadOnlyList 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; + } + + /// + /// 从参数文件加载直径→参数字符串(从 H 开始到行尾)的映射。 + /// + private static Dictionary LoadParameterDictionary(IReadOnlyList paramLines) + { + var dict = new Dictionary(); + + 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; + } + + /// + /// 在 .drl 文件内容中执行参数替换: + /// 1. 第2行(注释行)替换为参数文件的描述 + /// 2. M48~% 区域内按直径匹配替换刀径行的参数段 + /// + private static string ReplaceParametersInContent( + string drlContent, + Dictionary paramDict, + string paramDescription, + HashSet 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(); + } + + /// + /// 获取行尾空白字符。 + /// + private static string GetTrailingWhitespace(DrillTapeLine line) + { + string original = line.Text; + int trimmedLength = original.TrimEnd().Length; + return trimmedLength < original.Length ? original[trimmedLength..] : string.Empty; + } + } +} diff --git a/DrlParameterSelectionWindow.xaml b/DrlParameterSelectionWindow.xaml new file mode 100644 index 0000000..f8f31fa --- /dev/null +++ b/DrlParameterSelectionWindow.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + +