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<直径>,参数段(H...)可选 /// 例:T01C0.799H05000Z+0.000S060.00F105.0U0700.0 → prefix="T01C0.799" /// T01C0.799(无参数段)→ prefix="T01C0.799" /// 匹配后统一重建为 prefix + 参数文件参数段:已有参数段整体替换,无参数段则追加。 /// 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. M48 后一行若是注释行则替换为参数文件的描述;否则原样保留并在 M48 后插入描述行 /// 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) { // M48 下一行:若是注释行则替换为参数文件的描述; // 否则(如工具定义行)原样保留,并在其前(M48 后)插入描述行,不破坏数据; // 该行本身仍按刀径行规则处理(替换/追加/未匹配)。 string trimmed = lines[i].Text.Trim(); if (trimmed.StartsWith(";")) { string trailing = GetTrailingWhitespace(lines[i]); builder.Append(';'); builder.Append(paramDescription); builder.Append(trailing); builder.Append(lines[i].LineEnding); } else { builder.Append(';'); builder.Append(paramDescription); builder.Append(lines[i].LineEnding); AppendToolLine(builder, lines[i], trimmed, paramDict, unmatchedDiameters); } } else if (i > headerStart && i < headerEnd) { // 头部区域(M48 和 % 之间,排除已处理的第2行) string trimmed = lines[i].Text.Trim(); AppendToolLine(builder, lines[i], trimmed, paramDict, unmatchedDiameters); } else { // % 之后或其他区域原样保留 builder.Append(lines[i].Text); builder.Append(lines[i].LineEnding); } } return builder.ToString(); } /// /// 按刀径行规则追加一行:匹配直径则重建为 prefix + 参数文件参数段( /// 已有参数段整体替换,无参数段则追加);未匹配或非刀径行保留原行, /// 未匹配直径计入 unmatchedDiameters。 /// private static void AppendToolLine( StringBuilder builder, DrillTapeLine line, string trimmed, Dictionary paramDict, HashSet unmatchedDiameters) { var match = ToolLinePattern.Match(trimmed); if (!match.Success) { // 非刀径行(如其他注释等)原样保留 builder.Append(line.Text); builder.Append(line.LineEnding); return; } string prefix = match.Groups["prefix"].Value; string trailing = GetTrailingWhitespace(line); // 从 prefix 中提取直径 var diaMatch = ToolLineDiameterPattern.Match(prefix); if (!diaMatch.Success || !double.TryParse(diaMatch.Groups["diameter"].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter)) { // 无法提取直径,保留原行 builder.Append(line.Text); builder.Append(line.LineEnding); return; } double roundedDiameter = Math.Round(diameter, 3); if (paramDict.TryGetValue(roundedDiameter, out string? newParams)) { builder.Append(prefix); builder.Append(newParams); builder.Append(trailing); builder.Append(line.LineEnding); } else { // 未匹配:保留原行 unmatchedDiameters.Add(roundedDiameter); builder.Append(line.Text); builder.Append(line.LineEnding); } } /// /// 获取行尾空白字符。 /// private static string GetTrailingWhitespace(DrillTapeLine line) { string original = line.Text; int trimmedLength = original.TrimEnd().Length; return trimmedLength < original.Length ? original[trimmedLength..] : string.Empty; } } }