# Ploss 解析器 > 所属模块:[解析器设计](./概览.md) --- ## 1. 日志格式 ``` pow_loss = 3053, delta_p = 1950 FOD-> (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) ``` --- ## 2. 字段定义 ### 2.1 第一行字段 | 编号 | 字段名称 | 数据类型 | 说明 | |------|----------|----------|------| | (A) | PowLoss | int | 功率损耗值 | | (B) | DeltaP | int | 功率差值 | ### 2.2 第二行字段 (FOD->) | 编号 | 字段名称 | 数据类型 | 说明 | |------|----------|----------|------| | (1) | Field1 | int | 待定义 | | (2) | Field2 | int | 待定义 | | (3) | Field3 | int | 待定义 | | (4) | Field4 | int | 待定义 | | (5) | Field5 | int | 待定义 | | (6) | Field6 | int | 待定义 | | (7) | Field7 | int | 待定义 | | (8) | Field8 | int | 待定义 | | (9) | Field9 | int | 待定义 | | (10) | Field10 | int | 待定义 | | (11) | Field11 | int | 待定义 | | (12) | Field12 | int | 待定义 | | (13) | Field13 | int | 待定义 | | (14) | Field14 | int | 待定义 | | (15) | Field15 | int | 待定义 | | (16) | Field16 | int | 待定义 | --- ## 3. 正则表达式 日志由两行组成,需要分别匹配: ```csharp // Parsers/PlossParser.cs // 第一行格式: 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 ); ``` > **说明**:所有16个字段均支持负数,使用 `-?\d+` 匹配。 **捕获组映射:** **第一行 (HeaderPattern):** | 捕获组 | 字段 | 类型转换 | |--------|------|----------| | Group 1 | PowLoss | int.Parse() | | Group 2 | DeltaP | int.Parse() | **第二行 (FodPattern):** | 捕获组 | 字段 | 类型转换 | |--------|------|----------| | Group 1 | Field1 | int.Parse() | | Group 2 | Field2 | int.Parse() | | Group 3 | Field3 | int.Parse() | | Group 4 | Field4 | int.Parse() | | Group 5 | Field5 | int.Parse() | | Group 6 | Field6 | int.Parse() | | Group 7 | Field7 | int.Parse() | | Group 8 | Field8 | int.Parse() | | Group 9 | Field9 | int.Parse() | | Group 10 | Field10 | int.Parse() | | Group 11 | Field11 | int.Parse() | | Group 12 | Field12 | int.Parse() | | Group 13 | Field13 | int.Parse() | | Group 14 | Field14 | int.Parse() | | Group 15 | Field15 | int.Parse() | | Group 16 | Field16 | int.Parse() | --- ## 4. 解析器实现 ```csharp // Parsers/PlossParser.cs using System.Text.RegularExpressions; using Gpulse.WCT.DataAnalyzer.Models; namespace Gpulse.WCT.DataAnalyzer.Parsers; public class PlossParser : IParser { private static readonly Regex HeaderPattern = new( @"^pow_loss\s*=\s*(\d+),\s*delta_p\s*=\s*(\d+)$", RegexOptions.Compiled ); 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 ); public bool CanParse(string line) => HeaderPattern.IsMatch(line) || FodPattern.IsMatch(line); public ParseResult Parse(string line, Guid scenarioId) { // 尝试匹配第一行 var headerMatch = HeaderPattern.Match(line); if (headerMatch.Success) { return ParseHeaderLine(headerMatch, scenarioId); } // 尝试匹配第二行 var fodMatch = FodPattern.Match(line); if (fodMatch.Success) { return ParseFodLine(fodMatch, scenarioId); } return ParseResult.Failure( "FORMAT_MISMATCH", "Line does not match Ploss pattern" ); } private ParseResult ParseHeaderLine(Match match, Guid scenarioId) { try { var record = new PlossRecord { ScenarioId = scenarioId, PowLoss = int.Parse(match.Groups[1].Value), DeltaP = int.Parse(match.Groups[2].Value), IsHeaderLine = true }; 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) { try { var record = new PlossRecord { ScenarioId = scenarioId, Field1 = int.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), IsHeaderLine = false }; return ParseResult.Success(record); } catch (Exception ex) { return ParseResult.Failure( "TYPE_CONVERSION", $"Failed to convert FOD field: {ex.Message}" ); } } } ``` > **注意**:实际实现中,两行日志需要合并为一条完整记录。可以使用 `PlossRecordMerger` 或在业务逻辑层处理合并。 --- ## 5. 核心判定逻辑 ### 5.1 FOD 触发条件 ``` if (Ploss > Threshold) → 计数器累积 if (Count > Limit) → 触发 FOD 报警,停止充电 ``` ### 5.2 安全余量计算 ```csharp // 派生属性(不落库) Margin = Threshold - Ploss ``` | Margin 状态 | 含义 | |-------------|------| | Margin > 0 | 安全,损耗低于阈值 | | Margin < 0 | 危险,损耗超过阈值,可能触发 FOD | --- ## 6. 动态阈值表 阈值根据接收功率动态查表: | 接收功率区间 (Rx Power) | 阈值 (Threshold) | 备注 | |------------------------|------------------|------| | 0 ~ 5W (0-5000 mW) | 350 mW | 低功率最敏感 | | 5 ~ 10W (5000-10000 mW) | 500 mW | | | 10 ~ 15W (10000-15000 mW) | 750 mW | | | 15 ~ 30W (15000-30000 mW) | 1000 mW | 示例 Log 处于此区间 | | 30 ~ 50W (30000-50000 mW) | 1250 mW | 高功率允许更多损耗 |