feat: ROU成型参数替换功能 — 支持 .rou/.rou1/.rou2/.roux 文件从参数 txt 自动补充 S/F/R/Z/H
This commit is contained in:
178
RouParameterReplacer.cs
Normal file
178
RouParameterReplacer.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DrillTools
|
||||
{
|
||||
/// <summary>
|
||||
/// ROU 成型参数替换:将参数文件中的 S/F/R/Z/H 参数追加到 .rou 文件的刀径定义行。
|
||||
/// </summary>
|
||||
internal static class RouParameterReplacer
|
||||
{
|
||||
private const int Gb2312CodePage = 936;
|
||||
private const double DiameterTolerance = 0.001;
|
||||
|
||||
/// <summary>
|
||||
/// 匹配参数行:T00C<直径>S<参数...>
|
||||
/// 例:T00C1.800S55.0F5.0R25Z0H600
|
||||
/// </summary>
|
||||
private static readonly Regex ParamLinePattern = new(
|
||||
@"^T00C(?<diameter>\d+(?:\.\d+)?)(?<params>S.+?)\s*$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// 匹配 .rou 刀径定义行:T<刀号>C<直径>
|
||||
/// 例:T01C1.8
|
||||
/// </summary>
|
||||
private static readonly Regex ToolLinePattern = new(
|
||||
@"^T\d+C(?<diameter>\d+(?:\.\d+)?)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// 执行参数替换。
|
||||
/// </summary>
|
||||
/// <param name="rouFilePath">.rou 文件完整路径</param>
|
||||
/// <param name="paramFilePath">参数 .txt 文件完整路径</param>
|
||||
public static void ReplaceParameters(string rouFilePath, string paramFilePath)
|
||||
{
|
||||
// 1. 读取参数文件,构建直径→参数字符串映射
|
||||
var paramDict = LoadParameterDictionary(paramFilePath);
|
||||
|
||||
if (paramDict.Count == 0)
|
||||
throw new InvalidOperationException("参数文件中未找到有效的参数行(格式:T00C<直径>S...)");
|
||||
|
||||
// 2. 读取 .rou 文件
|
||||
string rouContent = CommandTypeFileReader.ReadAllText(rouFilePath);
|
||||
|
||||
// 3. 执行替换
|
||||
string replacedContent = ReplaceParametersInContent(rouContent, paramDict);
|
||||
|
||||
// 4. 备份原文件
|
||||
string backupFilePath = rouFilePath + ".bak";
|
||||
File.Copy(rouFilePath, backupFilePath, true);
|
||||
|
||||
// 5. 写回文件
|
||||
using var writer = new StreamWriter(rouFilePath, false, Encoding.GetEncoding(Gb2312CodePage));
|
||||
writer.Write(replacedContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从参数文件加载直径→参数字符串(从 S 开始到行尾)的映射。
|
||||
/// </summary>
|
||||
private static Dictionary<double, string> LoadParameterDictionary(string paramFilePath)
|
||||
{
|
||||
var dict = new Dictionary<double, string>();
|
||||
|
||||
string content = CommandTypeFileReader.ReadAllText(paramFilePath);
|
||||
var lines = DrillTapeLineParser.SplitLines(content);
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
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;
|
||||
|
||||
string paramString = match.Groups["params"].Value;
|
||||
|
||||
// 如果同一直径出现多次,后面的覆盖前面的
|
||||
dict[diameter] = paramString;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在 .rou 文件内容中,将 M48~% 区域内的刀径行末尾追加参数。
|
||||
/// </summary>
|
||||
private static string ReplaceParametersInContent(string rouContent, Dictionary<double, string> paramDict)
|
||||
{
|
||||
var lines = DrillTapeLineParser.SplitLines(rouContent);
|
||||
var builder = new StringBuilder(rouContent.Length + 1024);
|
||||
|
||||
bool inHeader = false;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string trimmed = line.Text.Trim();
|
||||
|
||||
// 检测 M48 头部开始
|
||||
if (!inHeader && trimmed == "M48")
|
||||
{
|
||||
inHeader = true;
|
||||
builder.Append(line.Text);
|
||||
builder.Append(line.LineEnding);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测 % 头部结束
|
||||
if (inHeader && trimmed == "%")
|
||||
{
|
||||
inHeader = false;
|
||||
builder.Append(line.Text);
|
||||
builder.Append(line.LineEnding);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在头部区域内匹配刀径定义行
|
||||
if (inHeader)
|
||||
{
|
||||
var match = ToolLinePattern.Match(trimmed);
|
||||
if (match.Success &&
|
||||
double.TryParse(match.Groups["diameter"].Value,
|
||||
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
|
||||
{
|
||||
// 按直径数值匹配参数(允许浮点误差)
|
||||
string? paramString = FindMatchingParameter(diameter, paramDict);
|
||||
if (paramString != null)
|
||||
{
|
||||
// 保留原始行尾空白
|
||||
string textWithoutTrailing = line.Text.TrimEnd();
|
||||
int trailingLength = line.Text.Length - textWithoutTrailing.Length;
|
||||
string trailing = trailingLength > 0 ? line.Text[^trailingLength..] : string.Empty;
|
||||
|
||||
builder.Append(textWithoutTrailing);
|
||||
builder.Append(paramString);
|
||||
builder.Append(trailing);
|
||||
builder.Append(line.LineEnding);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 其他行原样保留
|
||||
builder.Append(line.Text);
|
||||
builder.Append(line.LineEnding);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按直径查找匹配的参数字符串,允许 0.001 的浮点误差。
|
||||
/// 返回 null 表示未找到匹配。
|
||||
/// </summary>
|
||||
private static string? FindMatchingParameter(double diameter, Dictionary<double, string> paramDict)
|
||||
{
|
||||
// 精确匹配优先
|
||||
if (paramDict.TryGetValue(diameter, out string? paramString))
|
||||
return paramString;
|
||||
|
||||
// 浮点容差匹配
|
||||
foreach (var kvp in paramDict)
|
||||
{
|
||||
if (Math.Abs(kvp.Key - diameter) < DiameterTolerance)
|
||||
return kvp.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user