{"record":{"id":"55073dc0c06f3f6f","repo":"babalae/better-genshin-impact","slug":"counttext","errorCode":null,"errorMessage":"原粹树脂上限解析失败：{countText}","messagePattern":"原粹树脂上限解析失败：(.+?)","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/GameTask/AutoBoss/AutoBossTask.cs","lineNumber":354,"sourceCode":"\n    /// <summary>\n    /// 按原右上角 OCR 区域读取当前/上限文本，并取数字串最后三位作为树脂上限。\n    /// </summary>\n    private int RecognizeOriginalResinLimit(ImageRegion capture, int resinIconRight)\n    {\n        var countRect = new Rect(resinIconRight + ScaleX(25), ScaleY(37), ScaleX(120), ScaleY(24));\n        using var countRegion = capture.DeriveCrop(countRect);\n        var countText = OcrFactory.Paddle.OcrWithoutDetector(countRegion.SrcMat);\n        var digits = Regex.Replace(StringUtils.ConvertFullWidthNumToHalfWidth(countText), @\"\\D\", \"\");\n        if (digits.Length < 3)\n        {\n            throw new FormatException($\"原粹树脂上限 OCR 失败：{countText}\");\n        }\n\n        var limitText = digits[^3..];\n        if (!int.TryParse(limitText, NumberStyles.None, CultureInfo.InvariantCulture, out var resinLimit) || resinLimit <= 0)\n        {\n            throw new FormatException($\"原粹树脂上限解析失败：{countText}\");\n        }\n\n        _logger.LogDebug(\"{Name}：原粹树脂上限 OCR：{Text} -> {Limit}\", Name, countText, resinLimit);\n        return resinLimit;\n    }\n\n    /// <summary>\n    /// 读取树脂详情弹窗中的全部恢复时间；已完全恢复时返回零时长。\n    /// </summary>\n    private TimeSpan RecognizeFullRecoveryTime(ImageRegion capture, int resinIconLeft, int resinIconBottom)\n    {\n        // 该偏移来自截图实际像素，不随 AssetScale 缩放。\n        var detailRect = new Rect(\n            resinIconLeft - 13,\n            resinIconBottom + 29,\n            220,\n            150);\n        using var detailRegion = capture.DeriveCrop(detailRect);","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/GameTask/AutoBoss/AutoBossTask.cs#L336-L372","documentation":"在取到至少 3 位数字后，代码取最后三位作为 limitText 并 int.TryParse。由于 digits 已剔除非数字，解析仅在三种情况失败：数值溢出（>int.MaxValue）、或解析出 0（resinLimit <= 0 分支，如 limitText='000'）。FormatException 表示上限被读成 0 或异常大值。","triggerScenarios":"'当前/上限' 被读成 '000/000' 或 limit 段为 '000'（例如把图标/分隔线误读为 0）；或 digits 含超长数字串使最后三位虽能解析但整体上限异常（此处不会溢出，三位最大 999）。","commonSituations":"OCR 把无数字区域读成多个 0；区域漂移到非数字元素；游戏 UI 临时显示占位符。","solutions":["记录 countText 原文（debug 日志）确认是否为 '000' 类误读。","当 limit==0 时按树脂上限常识（如 160）做一次容错重试，而非直接抛出。","校准 countRect 区域，避免覆盖到非数字图形。","在外层兜底 catch 中降级为预检跳过。"],"exampleFix":"// before\nvar limitText = digits[^3..];\nif (!int.TryParse(limitText, NumberStyles.None, CultureInfo.InvariantCulture, out var resinLimit) || resinLimit <= 0)\n{\n    throw new FormatException($\"原粹树脂上限解析失败：{countText}\");\n}\n\n// after\nvar limitText = digits[^3..];\nif (!int.TryParse(limitText, NumberStyles.None, CultureInfo.InvariantCulture, out var resinLimit) || resinLimit <= 0)\n{\n    throw new FormatException($\"原粹树脂上限解析失败（原文={countText}, 末三位={limitText}）\");\n}","handlingStrategy":"validation","validationCode":"// 取末三位前先校验是否为合理上限范围\nvar limitText = digits[^3..];\nif (int.TryParse(limitText, NumberStyles.None, CultureInfo.InvariantCulture, out var candidate)\n    && candidate is >= 60 and <= 200)\n{\n    return candidate;\n}\n// 否则按异常处理","typeGuard":null,"tryCatchPattern":null,"preventionTips":["对解析出的上限做业务范围校验（常见 160/200）。","digits 含 '/' 时优先按分隔符取右段而非盲目取末三位。","记录 countText 原文以便排查 '000' 类误读。"],"tags":["ocr","resin","parse","genshin","autoboss"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}