using System.Text.RegularExpressions; using WCTDataMiner.Core.Models; namespace WCTDataMiner.Core.Parsers; /// /// Ploss FOD日志解析器 - 支持两行格式 /// 第一行: pow_loss = 3053, delta_p = 1950 /// 第二行: FOD-> (1) (2) ... (16) /// 同时兼容旧的单行格式 /// public class PlossParser : IParser { // 第一行正则: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 ); // 第二行正则: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 ); // 旧格式正则(兼容):前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 ); /// /// 判断是否可以解析该行(任意一行) /// public bool CanParse(string line) => HeaderPattern.IsMatch(line) || FodPattern.IsMatch(line) || LegacyPattern.IsMatch(line); /// /// 判断是否为多行记录的起始行(header行) /// public bool IsMultiLineStart(string line) => HeaderPattern.IsMatch(line); /// /// 单行解析(兼容旧格式) /// public ParseResult Parse(string line, Guid scenarioId) { // 尝试匹配旧格式 var legacyMatch = LegacyPattern.Match(line); if (legacyMatch.Success) { return ParseLegacyFormat(legacyMatch, scenarioId); } // 尝试匹配第二行(新格式) var fodMatch = FodPattern.Match(line); if (fodMatch.Success) { return ParseFodLine(fodMatch, scenarioId); } // 尝试匹配第一行 var headerMatch = HeaderPattern.Match(line); if (headerMatch.Success) { return ParseHeaderLine(headerMatch, scenarioId); } return ParseResult.Failure( "FORMAT_MISMATCH", "Line does not match Ploss pattern" ); } /// /// 多行解析(新两行格式) /// public ParseResult? ParseMultiLine(string[] lines, Guid scenarioId) { if (lines.Length < 2) 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, // 第一行字段 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.Success(record); } catch (Exception ex) { return ParseResult.Failure( "TYPE_CONVERSION", $"Failed to convert field: {ex.Message}" ); } } private ParseResult 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.Success(record); } catch (Exception ex) { return ParseResult.Failure( "TYPE_CONVERSION", $"Failed to convert field: {ex.Message}" ); } } private ParseResult 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.Success(record); } catch (Exception ex) { return ParseResult.Failure( "TYPE_CONVERSION", $"Failed to convert header field: {ex.Message}" ); } } private ParseResult 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.Success(record); } catch (Exception ex) { return ParseResult.Failure( "TYPE_CONVERSION", $"Failed to convert FOD field: {ex.Message}" ); } } }