Files
WCTDataMiner/docs/architecture/parser/Ploss解析器.md
2026-08-13 13:29:56 +08:00

7.1 KiB
Raw Blame History

Ploss 解析器

所属模块:解析器设计


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. 正则表达式

日志由两行组成,需要分别匹配:

// 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. 解析器实现

// Parsers/PlossParser.cs
using System.Text.RegularExpressions;
using Gpulse.WCT.DataAnalyzer.Models;

namespace Gpulse.WCT.DataAnalyzer.Parsers;

public class PlossParser : IParser<PlossRecord>
{
    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<PlossRecord> 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<PlossRecord>.Failure(
            "FORMAT_MISMATCH",
            "Line does not match Ploss pattern"
        );
    }

    private ParseResult<PlossRecord> 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<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)
    {
        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<PlossRecord>.Success(record);
        }
        catch (Exception ex)
        {
            return ParseResult<PlossRecord>.Failure(
                "TYPE_CONVERSION",
                $"Failed to convert FOD field: {ex.Message}"
            );
        }
    }
}

注意:实际实现中,两行日志需要合并为一条完整记录。可以使用 PlossRecordMerger 或在业务逻辑层处理合并。


5. 核心判定逻辑

5.1 FOD 触发条件

if (Ploss > Threshold) → 计数器累积
if (Count > Limit) → 触发 FOD 报警,停止充电

5.2 安全余量计算

// 派生属性(不落库)
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 高功率允许更多损耗