Files
AohDrllTools/DrlParameterReplacer.cs

249 lines
10 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<直径> + 参数段
/// 例: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;
}
}
}