补充真实 MOM 数据验证
This commit is contained in:
parent
9aec353c73
commit
818885951c
|
|
@ -1043,6 +1043,8 @@ def _record_rejected_candidate(
|
|||
closed_loop: ClosedLoopProblem,
|
||||
report: Any,
|
||||
candidate_result: Mapping[str, Any],
|
||||
*,
|
||||
engine_type: str = "CLOSED_LOOP",
|
||||
) -> dict[str, Any]:
|
||||
"""Persist validation diagnostics without leaking candidate WO/VL artifacts."""
|
||||
|
||||
|
|
@ -1053,12 +1055,21 @@ def _record_rejected_candidate(
|
|||
version_id = next_id("flexScheduleVersion")
|
||||
version_no = f"FV{closed_loop.business_date.replace('-', '')}-{len(world['flexScheduleVersions']) + 1:03d}"
|
||||
violations = list(report.hardViolations)
|
||||
normalized_engine = (
|
||||
"OPTIMIZE"
|
||||
if str(engine_type or "CLOSED_LOOP").strip().upper() == "OPTIMIZE"
|
||||
else "CLOSED_LOOP"
|
||||
)
|
||||
version = {
|
||||
"id": version_id,
|
||||
"versionNo": version_no,
|
||||
"versionName": f"闭环排产校验失败 {closed_loop.business_date}",
|
||||
"sortMode": "CLOSED_LOOP",
|
||||
"engineType": "CLOSED_LOOP",
|
||||
"engineType": normalized_engine,
|
||||
"solverId": candidate_result.get("solverId"),
|
||||
"solverVersion": candidate_result.get("solverVersion"),
|
||||
"algorithmId": candidate_result.get("algorithmId"),
|
||||
"algorithmVersion": candidate_result.get("algorithmVersion"),
|
||||
"status": "DRAFT",
|
||||
"solveStatus": "REJECTED",
|
||||
"planningProblemId": closed_loop.problem_id,
|
||||
|
|
@ -1094,7 +1105,11 @@ def _record_rejected_candidate(
|
|||
return {
|
||||
"versionId": version_id,
|
||||
"versionNo": version_no,
|
||||
"engineType": "CLOSED_LOOP",
|
||||
"engineType": normalized_engine,
|
||||
"solverId": version.get("solverId"),
|
||||
"solverVersion": version.get("solverVersion"),
|
||||
"algorithmId": version.get("algorithmId"),
|
||||
"algorithmVersion": version.get("algorithmVersion"),
|
||||
"status": "DRAFT",
|
||||
"solveStatus": "REJECTED",
|
||||
"orderCount": version["orderCount"],
|
||||
|
|
@ -1226,7 +1241,14 @@ def run_closed_loop_candidate(
|
|||
solved["projectedFlexOrders"] = projected
|
||||
|
||||
if not report.valid or solution.solveStatus != SolveStatus.FEASIBLE:
|
||||
rejected = _record_rejected_candidate(world, next_id, closed_loop, report, solved)
|
||||
rejected = _record_rejected_candidate(
|
||||
world,
|
||||
next_id,
|
||||
closed_loop,
|
||||
report,
|
||||
solved,
|
||||
engine_type=normalized_engine,
|
||||
)
|
||||
return {**rejected, **base, "projectedFlexOrders": projected}
|
||||
|
||||
for key in (
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from server.aps_domain.closed_loop_runtime import run_closed_loop_candidate
|
||||
from server.aps_domain.kangni_intake import apply_site_payload_to_world, build_site_payload_from_data_dir
|
||||
from server.engines import get_engine
|
||||
from server.engines.optimize_engine import normalize_dispatch_rule
|
||||
from server.engines.pool_engine import PoolEngine
|
||||
from server.state.seed import seed_world
|
||||
|
||||
from tests.golden.test_closed_loop_runtime import BUSINESS_DATE, _ready_world
|
||||
|
||||
|
|
@ -73,3 +81,39 @@ def test_optimize_blocker_keeps_engine_identity_and_zero_artifacts():
|
|||
assert result["woCount"] == 0
|
||||
assert world["flexScheduleVersions"][-1]["engineType"] == "OPTIMIZE"
|
||||
assert world["flexWorkOrders"] == []
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("APS_KANGNI_DATA_DIR"),
|
||||
reason="set APS_KANGNI_DATA_DIR to run the external Kangni/MOM workbook test",
|
||||
)
|
||||
def test_real_kangni_workbooks_run_through_optimize_closed_loop_without_source_mutation():
|
||||
data_dir = Path(os.environ["APS_KANGNI_DATA_DIR"])
|
||||
files = sorted(data_dir.glob("*.xlsx"))
|
||||
assert files, f"no .xlsx workbooks found in {data_dir}"
|
||||
before = {path.name: hashlib.sha256(path.read_bytes()).hexdigest() for path in files}
|
||||
|
||||
payload = build_site_payload_from_data_dir(data_dir, station_count=4)
|
||||
world = seed_world()
|
||||
intake = apply_site_payload_to_world(world, payload, clear_all=True)
|
||||
result = run_closed_loop_candidate(
|
||||
world,
|
||||
_next_id_factory(),
|
||||
business_date=BUSINESS_DATE,
|
||||
engine_type="OPTIMIZE",
|
||||
sort_mode="EDD",
|
||||
strict=True,
|
||||
)
|
||||
|
||||
after = {path.name: hashlib.sha256(path.read_bytes()).hexdigest() for path in files}
|
||||
assert before == after
|
||||
assert intake["orderCount"] == 10
|
||||
assert intake["routingRecordCount"] == 72
|
||||
assert intake["bomCount"] == 823
|
||||
assert result["engineType"] == "OPTIMIZE"
|
||||
assert result["solveStatus"] == "REJECTED"
|
||||
assert result["solverId"] == "optimize-dispatch"
|
||||
assert result["algorithmId"] == "optimize.edd"
|
||||
assert result["vlCount"] == 0
|
||||
assert result["woCount"] == 0
|
||||
assert result["planning"]["summary"]["blockerCounts"]["SUPPLY_SHORTAGE"] > 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue