# ============================================================ # 康尼映射表解析契约黄金测试(真实数据验证工程师) # 覆盖:设备能力/模具适配映射表表头契约、共享设备全工序覆盖、 # 映射表存在时 build_flex 优先读取并输出推断说明。 # ============================================================ from __future__ import annotations from pathlib import Path import pytest from server.aps_domain.kangni_intake import ( build_flex_equipment_and_molds, parse_equipment_capability_map, parse_mold_adaptation_map, ) KANGNI_DATA_DIR = Path(r"D:\ItemSpace\14.工业智核\康尼\数据") EQUIP_HEADER = [ "设备编号", "设备名称", "可执行工序编号", "单件工时", "可动率", "是否可移动", "移动耗时(分钟)", "区域编码", "适配模具编号", "状态", "备注", ] MOLD_HEADER = [ "模具编号", "模具名称", "适用工序编号", "适配设备编号", "寿命上限", "已用寿命", "区域编码", "换型耗时(分钟)", "状态", "备注", ] def _write_equip_map(tmp_path: Path) -> Path: from openpyxl import Workbook p = tmp_path / "设备能力映射模板.xlsx" wb = Workbook() ws = wb.active ws.title = "设备能力" ws.append(EQUIP_HEADER) ws.append(["EQ-SHARED-CG-01", "共享装配设备(待现场确认)", "OP-1,OP-2", "OP-1:27;OP-2:5", 0.95, "否", 0, "ZONE-CG", "", "RUNNING", "保守假设,待现场确认"]) ws.append(["CO01001", "落地式直读光谱仪", "", "", 0.95, "否", 0, "ZONE-CG", "", "RUNNING", "真实设备,能力待现场确认"]) wb.save(p) return p def _write_mold_map(tmp_path: Path) -> Path: from openpyxl import Workbook p = tmp_path / "模具适配映射模板.xlsx" wb = Workbook() ws = wb.active ws.title = "模具适配" ws.append(MOLD_HEADER) ws.append(["00000001", "胶条切割打孔工装", "", "", "", "", "ZONE-CG", "", "AVAILABLE", "真实模具,工序/寿命待现场确认"]) wb.save(p) return p def test_equipment_map_parses_exact_header_contract(tmp_path: Path): rows = parse_equipment_capability_map(_write_equip_map(tmp_path)) assert len(rows) == 2 shared = rows[0] assert shared["code"] == "EQ-SHARED-CG-01" assert shared["capabilities"] == ["OP-1", "OP-2"] assert shared["opStdTime"] == {"OP-1": 27.0, "OP-2": 5.0} assert shared["movable"] is False assert shared["availabilityRate"] == 0.95 assert shared["status"] == "RUNNING" assert rows[1]["code"] == "CO01001" assert rows[1]["capabilities"] == [] def test_mold_map_parses_exact_header_contract(tmp_path: Path): rows = parse_mold_adaptation_map(_write_mold_map(tmp_path)) assert len(rows) == 1 row = rows[0] assert row["code"] == "00000001" assert row["name"] == "胶条切割打孔工装" assert row["operationCode"] == "" assert row["adaptableEquipment"] == [] assert row["lifeTotal"] == 0.0 assert row["lifeUsed"] == 0.0 assert row["status"] == "AVAILABLE" def test_build_flex_prefers_mapping_table_and_emits_inference(tmp_path: Path): equip = _write_equip_map(tmp_path) mold = _write_mold_map(tmp_path) assert equip.parent == mold.parent equipment, molds, mold_ops, infs = build_flex_equipment_and_molds( tmp_path, [], {"OP-1": 27.0, "OP-2": 5.0}, ["OP-1", "OP-2"], {"zone": {"code": "ZONE-CG"}, "availabilityRate": 0.95}, ) assert len(equipment) == 2 assert not equipment[0]["inferred"] assert equipment[0]["capabilities"] == ["OP-1", "OP-2"] assert len(molds) == 1 assert mold_ops == set() assert any("设备能力映射表已读取" in x for x in infs) assert any("模具适配映射表已读取" in x for x in infs) @pytest.mark.skipif(not (KANGNI_DATA_DIR / "工时.xlsx").exists(), reason="康尼现场工时表不在本机") def test_shared_equipment_covers_all_real_kangni_operations(): from openpyxl import load_workbook equip_path = KANGNI_DATA_DIR / "设备能力映射模板.xlsx" if not equip_path.exists(): pytest.skip("康尼/数据 设备能力映射模板.xlsx 未生成") wb = load_workbook(KANGNI_DATA_DIR / "工时.xlsx", read_only=True, data_only=True) real_ops = set() for sn in wb.sheetnames: ws = wb[sn] for row in ws.iter_rows(min_row=2, values_only=True): code = str(row[1]).strip() if len(row) > 1 and row[1] is not None else "" if code: real_ops.add(code) wb.close() assert real_ops, "工时.xlsx 未读到任何工序编号" rows = parse_equipment_capability_map(equip_path) shared = [r for r in rows if r["code"] == "EQ-SHARED-CG-01"] assert shared, "缺少共享设备占位行 EQ-SHARED-CG-01" caps = set(shared[0]["capabilities"]) missing = sorted(real_ops - caps) assert not missing, f"共享设备未覆盖真实工序: {missing}" assert all(op in shared[0]["opStdTime"] for op in real_ops)