Files
WCTDataMiner/src/WCTDataMiner.Core/Parsers/PlossParser.cs

236 lines
9.0 KiB
C#
Raw Normal View History

using System.Text.RegularExpressions;
using WCTDataMiner.Core.Models;
namespace WCTDataMiner.Core.Parsers;
/// <summary>
2026-07-03 18:06:01 +08:00
/// Ploss FOD日志解析器 - 支持两行格式
/// 第一行: pow_loss = 3053, delta_p = 1950
/// 第二行: FOD-> (1) (2) ... (16)
/// 同时兼容旧的单行格式
/// </summary>
public class PlossParser : IParser<PlossRecord>
{
2026-07-03 18:06:01 +08:00
// 第一行正则pow_loss = 3053, delta_p = 1950
private static readonly Regex HeaderPattern = new(
@"^pow_loss\s*=\s*(\d+),\s*delta_p\s*=\s*(\d+)$",
RegexOptions.Compiled
);
2026-07-03 18:06:01 +08:00
// 第二行正则FOD-> 后跟16个数字均支持负数
private static readonly Regex FodPattern = new(
@"^FOD->\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)$",
RegexOptions.Compiled
);
2026-07-03 18:06:01 +08:00
// 旧格式正则兼容前3个字段被忽略所有捕获组支持负数
private static readonly Regex LegacyPattern = new(
@"^FOD->\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)$",
RegexOptions.Compiled
);
/// <summary>
/// 匹配类型枚举
/// </summary>
public enum MatchType { None, Header, Fod, Legacy }
/// <summary>
/// 单次匹配结果(避免重复正则匹配)
/// </summary>
public (MatchType Type, Match Match) TryMatch(string line)
{
var headerMatch = HeaderPattern.Match(line);
if (headerMatch.Success)
return (MatchType.Header, headerMatch);
var fodMatch = FodPattern.Match(line);
if (fodMatch.Success)
return (MatchType.Fod, fodMatch);
var legacyMatch = LegacyPattern.Match(line);
if (legacyMatch.Success)
return (MatchType.Legacy, legacyMatch);
return (MatchType.None, Match.Empty);
}
2026-07-03 18:06:01 +08:00
/// <summary>
/// 判断是否可以解析该行(任意一行)
/// </summary>
public bool CanParse(string line) => TryMatch(line).Type != MatchType.None;
2026-07-03 18:06:01 +08:00
/// <summary>
/// 判断是否为多行记录的起始行header行
/// </summary>
public bool IsMultiLineStart(string line) => TryMatch(line).Type == MatchType.Header;
2026-07-03 18:06:01 +08:00
/// <summary>
/// 单行解析(兼容旧格式)
/// </summary>
public ParseResult<PlossRecord> Parse(string line, Guid scenarioId)
{
var (type, match) = TryMatch(line);
return type switch
2026-07-03 18:06:01 +08:00
{
MatchType.Legacy => ParseLegacyFormat(match, scenarioId),
MatchType.Fod => ParseFodLine(match, scenarioId),
MatchType.Header => ParseHeaderLine(match, scenarioId),
_ => ParseResult<PlossRecord>.Failure("FORMAT_MISMATCH", "Line does not match Ploss pattern")
};
2026-07-03 18:06:01 +08:00
}
/// <summary>
/// 多行解析(新两行格式)
/// </summary>
public ParseResult<PlossRecord>? ParseMultiLine(string[] lines, Guid scenarioId)
{
if (lines.Length < 2 || lines[0] == null || lines[1] == null)
2026-07-03 18:06:01 +08:00
return null;
var headerMatch = HeaderPattern.Match(lines[0].Trim());
var fodMatch = FodPattern.Match(lines[1].Trim());
if (!headerMatch.Success || !fodMatch.Success)
return null;
try
{
var record = new PlossRecord
{
ScenarioId = scenarioId,
2026-07-03 18:06:01 +08:00
// 第一行字段
PowLoss = int.Parse(headerMatch.Groups[1].Value),
DeltaP = int.Parse(headerMatch.Groups[2].Value),
// 第二行字段
Field1 = int.Parse(fodMatch.Groups[1].Value),
Field2 = int.Parse(fodMatch.Groups[2].Value),
Field3 = int.Parse(fodMatch.Groups[3].Value),
Field4 = int.Parse(fodMatch.Groups[4].Value),
Field5 = int.Parse(fodMatch.Groups[5].Value),
Field6 = int.Parse(fodMatch.Groups[6].Value),
Field7 = int.Parse(fodMatch.Groups[7].Value),
Field8 = int.Parse(fodMatch.Groups[8].Value),
Field9 = int.Parse(fodMatch.Groups[9].Value), // 支持负数
Field10 = int.Parse(fodMatch.Groups[10].Value),
Field11 = int.Parse(fodMatch.Groups[11].Value),
Field12 = int.Parse(fodMatch.Groups[12].Value),
Field13 = int.Parse(fodMatch.Groups[13].Value),
Field14 = int.Parse(fodMatch.Groups[14].Value),
Field15 = int.Parse(fodMatch.Groups[15].Value),
Field16 = int.Parse(fodMatch.Groups[16].Value)
};
return ParseResult<PlossRecord>.Success(record);
}
catch (Exception ex)
{
return ParseResult<PlossRecord>.Failure(
"TYPE_CONVERSION",
$"Failed to convert field: {ex.Message}"
);
}
}
2026-07-03 18:06:01 +08:00
private ParseResult<PlossRecord> ParseLegacyFormat(Match match, Guid scenarioId)
{
// 保持原有逻辑不变,映射到新字段名
try
{
var record = new PlossRecord
{
ScenarioId = scenarioId,
// 旧格式没有第一行字段
PowLoss = null,
DeltaP = null,
// 映射到新字段名
Field1 = int.Parse(match.Groups[1].Value), // RxType
Field2 = int.Parse(match.Groups[2].Value), // RxPower
Field3 = int.Parse(match.Groups[3].Value), // TxPower
Field4 = int.Parse(match.Groups[4].Value), // Vcoil
Field5 = int.Parse(match.Groups[5].Value), // Vin
Field6 = int.Parse(match.Groups[6].Value), // Isns
Field7 = int.Parse(match.Groups[7].Value), // Ploss
Field8 = int.Parse(match.Groups[8].Value), // Threshold
Field9 = int.Parse(match.Groups[9].Value), // TriggerCount
Field10 = int.Parse(match.Groups[10].Value), // FodResult
Field11 = int.Parse(match.Groups[11].Value), // ProtocolType
Field12 = int.Parse(match.Groups[12].Value), // PwmDuty
Field13 = int.Parse(match.Groups[13].Value), // CoilIndex
// 旧格式没有Field14-16使用默认值
Field14 = 0,
Field15 = 0,
Field16 = 0
};
return ParseResult<PlossRecord>.Success(record);
}
catch (Exception ex)
{
return ParseResult<PlossRecord>.Failure(
"TYPE_CONVERSION",
$"Failed to convert field: {ex.Message}"
);
}
}
private ParseResult<PlossRecord> ParseHeaderLine(Match match, Guid scenarioId)
{
// 单独解析header行返回部分记录
// 实际使用时应通过ParseMultiLine合并
try
{
var record = new PlossRecord
{
ScenarioId = scenarioId,
PowLoss = int.Parse(match.Groups[1].Value),
DeltaP = int.Parse(match.Groups[2].Value)
};
return ParseResult<PlossRecord>.Success(record);
}
catch (Exception ex)
{
return ParseResult<PlossRecord>.Failure(
"TYPE_CONVERSION",
$"Failed to convert header field: {ex.Message}"
);
}
}
private ParseResult<PlossRecord> ParseFodLine(Match match, Guid scenarioId)
{
// 单独解析FOD行新格式无header
try
{
var record = new PlossRecord
{
ScenarioId = scenarioId,
PowLoss = null,
DeltaP = null,
Field1 = byte.Parse(match.Groups[1].Value),
Field2 = int.Parse(match.Groups[2].Value),
Field3 = int.Parse(match.Groups[3].Value),
Field4 = int.Parse(match.Groups[4].Value),
Field5 = int.Parse(match.Groups[5].Value),
Field6 = int.Parse(match.Groups[6].Value),
Field7 = int.Parse(match.Groups[7].Value),
Field8 = int.Parse(match.Groups[8].Value),
Field9 = int.Parse(match.Groups[9].Value),
Field10 = int.Parse(match.Groups[10].Value),
Field11 = int.Parse(match.Groups[11].Value),
Field12 = int.Parse(match.Groups[12].Value),
Field13 = int.Parse(match.Groups[13].Value),
Field14 = int.Parse(match.Groups[14].Value),
Field15 = int.Parse(match.Groups[15].Value),
Field16 = int.Parse(match.Groups[16].Value)
};
return ParseResult<PlossRecord>.Success(record);
}
catch (Exception ex)
{
return ParseResult<PlossRecord>.Failure(
"TYPE_CONVERSION",
$"Failed to convert FOD field: {ex.Message}"
);
}
}
}