S6ProcessInspectionJudge.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System.Globalization;
  2. namespace Admin.NET.Plugin.AiDOP.Manufacturing;
  3. /// <summary>录入类型(快照冻结值)。</summary>
  4. public static class S6ResultType
  5. {
  6. public const string Numeric = "NUMERIC";
  7. public const string NonNumeric = "NON_NUMERIC";
  8. }
  9. /// <summary>逐样本 / 单项 / 整单判定值。</summary>
  10. public static class S6Judgement
  11. {
  12. public const string Pass = "PASS";
  13. public const string Fail = "FAIL";
  14. public const string Invalid = "INVALID"; // 配置无法判定 / 值非法
  15. public const string Pending = "PENDING"; // 样本不足,未完成
  16. }
  17. /// <summary>单项完成状态。</summary>
  18. public static class S6ItemStatus
  19. {
  20. public const string NotStarted = "NOT_STARTED";
  21. public const string InProgress = "IN_PROGRESS";
  22. public const string Completed = "COMPLETED";
  23. }
  24. /// <summary>
  25. /// S6 过程检验 自动判定引擎(Phase 1C,纯函数,后端唯一判定真源)。
  26. /// 只依据"快照冻结的规格"(ResultType/上下限/要求样本量)+ 实录样本值判定;绝不回查 S0 检规。
  27. /// FQC 未做的两处在此补齐:① NON_NUMERIC 自动 OK→PASS/NG→FAIL;② 单项完成度 + 整单聚合后端计算。
  28. /// </summary>
  29. public static class S6ProcessInspectionJudge
  30. {
  31. /// <summary>解析上/下限字符串为 decimal;空/非法 → null(该边界不生效)。</summary>
  32. public static decimal? ParseLimit(string? raw)
  33. {
  34. if (string.IsNullOrWhiteSpace(raw)) return null;
  35. return decimal.TryParse(raw.Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var d) ? d : null;
  36. }
  37. /// <summary>解析数值样本;空/非法 → null。</summary>
  38. public static decimal? ParseNumeric(string? raw)
  39. {
  40. if (string.IsNullOrWhiteSpace(raw)) return null;
  41. return decimal.TryParse(raw.Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var d) ? d : null;
  42. }
  43. /// <summary>
  44. /// 逐样本判定。NUMERIC:闭区间(上下限可各自缺省,均缺=INVALID 不可判);值非法=INVALID。
  45. /// NON_NUMERIC:OK→PASS / NG→FAIL / 其他=INVALID。
  46. /// </summary>
  47. public static string JudgeSample(string? resultType, string? actualRaw, string? lowerRaw, string? upperRaw)
  48. {
  49. var rt = (resultType ?? string.Empty).Trim().ToUpperInvariant();
  50. if (rt == S6ResultType.NonNumeric)
  51. {
  52. var v = (actualRaw ?? string.Empty).Trim().ToUpperInvariant();
  53. return v switch { "OK" => S6Judgement.Pass, "NG" => S6Judgement.Fail, _ => S6Judgement.Invalid };
  54. }
  55. if (rt == S6ResultType.Numeric)
  56. {
  57. var lo = ParseLimit(lowerRaw);
  58. var up = ParseLimit(upperRaw);
  59. if (lo == null && up == null) return S6Judgement.Invalid; // 无任何界限 → 无法判定
  60. var actual = ParseNumeric(actualRaw);
  61. if (actual == null) return S6Judgement.Invalid; // 非法/空数值 → 不可默认 PASS
  62. var ok = (lo == null || actual.Value >= lo.Value) && (up == null || actual.Value <= up.Value);
  63. return ok ? S6Judgement.Pass : S6Judgement.Fail;
  64. }
  65. return S6Judgement.Invalid; // 未知录入类型
  66. }
  67. /// <summary>
  68. /// 单项聚合:由该项已录样本的判定 + 要求样本量 → (完成状态, 单项结果)。
  69. /// 任一 INVALID→INVALID;任一 FAIL→FAIL;已录满且全 PASS→PASS;否则 PENDING。
  70. /// </summary>
  71. public static (string Status, string Result) AggregateItem(int requiredCount, IReadOnlyList<string> sampleJudgements)
  72. {
  73. var recorded = sampleJudgements?.Count ?? 0;
  74. var required = requiredCount <= 0 ? 1 : requiredCount;
  75. if (recorded == 0) return (S6ItemStatus.NotStarted, S6Judgement.Pending);
  76. var status = recorded >= required ? S6ItemStatus.Completed : S6ItemStatus.InProgress;
  77. if (sampleJudgements!.Any(j => j == S6Judgement.Invalid)) return (status, S6Judgement.Invalid);
  78. if (sampleJudgements.Any(j => j == S6Judgement.Fail)) return (status, S6Judgement.Fail);
  79. if (status == S6ItemStatus.Completed && sampleJudgements.All(j => j == S6Judgement.Pass))
  80. return (status, S6Judgement.Pass);
  81. return (status, S6Judgement.Pending); // 已录未满
  82. }
  83. /// <summary>
  84. /// 整单聚合:由各项 (完成状态, 单项结果) → 整单结果。
  85. /// 任一 INVALID→INVALID;任一 FAIL→FAIL;全部 COMPLETED 且 PASS→PASS;否则 PENDING。
  86. /// </summary>
  87. public static string AggregateOverall(IReadOnlyList<(string Status, string Result)> items)
  88. {
  89. if (items == null || items.Count == 0) return S6Judgement.Pending;
  90. if (items.Any(i => i.Result == S6Judgement.Invalid)) return S6Judgement.Invalid;
  91. if (items.Any(i => i.Result == S6Judgement.Fail)) return S6Judgement.Fail;
  92. if (items.All(i => i.Status == S6ItemStatus.Completed && i.Result == S6Judgement.Pass)) return S6Judgement.Pass;
  93. return S6Judgement.Pending;
  94. }
  95. /// <summary>是否全部项目均已完成(可完成检验的前置)。</summary>
  96. public static bool AllItemsCompleted(IReadOnlyList<(string Status, string Result)> items)
  97. => items != null && items.Count > 0 && items.All(i => i.Status == S6ItemStatus.Completed);
  98. }