Files
AohDrllTools/DrlParameterReplacer.cs

276 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<直径>,参数段(H...)可选
/// 例:T01C0.799H05000Z+0.000S060.00F105.0U0700.0 → prefix="T01C0.799"
/// T01C0.799(无参数段)→ prefix="T01C0.799"
/// 匹配后统一重建为 prefix + 参数文件参数段:已有参数段整体替换,无参数段则追加。
/// </summary>
private static readonly Regex ToolLinePattern = new(
@"^\s*(?<prefix>T\d+C\d+(?:\.\d+)?)(?: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. M48 后一行若是注释行则替换为参数文件的描述;否则原样保留并在 M48 后插入描述行
/// 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)
{
// 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();
}
/// <summary>
/// 按刀径行规则追加一行:匹配直径则重建为 prefix + 参数文件参数段(
/// 已有参数段整体替换,无参数段则追加);未匹配或非刀径行保留原行,
/// 未匹配直径计入 unmatchedDiameters。
/// </summary>
private static void AppendToolLine(
StringBuilder builder,
DrillTapeLine line,
string trimmed,
Dictionary<double, string> paramDict,
HashSet<double> 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);
}
}
/// <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;
}
}
}