130 lines
6.9 KiB
Python
130 lines
6.9 KiB
Python
# ============================================================
|
||
# 图纸工艺/工序/细节识别黄金测试(R71.2 增强)
|
||
# 覆盖:合成文本识别、真实图纸标题栏/零件名、不误判比例、候选门禁。
|
||
# ============================================================
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
from server.aps_domain.drawing_dxf import build_drawing_master_candidates, inspect_dxf
|
||
from server.aps_domain.drawing_process import (
|
||
recognize_process_candidates,
|
||
recognize_process_text,
|
||
)
|
||
from server.aps_domain.drawing_understanding import build_drawing_understanding
|
||
from tests.external_data import external_dir
|
||
|
||
_RUIYANG_DIR = external_dir("RUIYANG_DEMO_DIR", "ruiyang")
|
||
_DEFAULT_SAMPLE = _RUIYANG_DIR / "5060102101-001-e(1).dxf"
|
||
_SAMPLE_003 = _RUIYANG_DIR / "5060102101-003-e(1).dxf"
|
||
|
||
|
||
def _sample(path: Path) -> Path:
|
||
if not path.is_file():
|
||
import pytest
|
||
pytest.skip(f"real DXF fixture not available: {path}")
|
||
return path
|
||
|
||
|
||
def test_synthetic_process_and_detail_recognition():
|
||
parsed = {
|
||
"asset": {"sha256": "0" * 64},
|
||
"texts": [
|
||
{"text": "DREHEN Ø20 TOLERANZ ±0.01", "evidence": {"source": "drawingText", "page": 1}},
|
||
{"text": "OBERFLÄCHE Ra 3.2", "evidence": {"source": "drawingText", "page": 1}},
|
||
{"text": "M30x1.5 16 TIEF", "evidence": {"source": "drawingText", "page": 1}},
|
||
{"text": "MONTAGE", "evidence": {"source": "drawingText", "page": 1}},
|
||
{"text": "SCALE 1:5", "evidence": {"source": "drawingText", "page": 1}},
|
||
],
|
||
}
|
||
process = recognize_process_candidates(parsed)
|
||
codes = {row["processCode"] for row in process["operations"]}
|
||
assert {"TURNING", "ASSEMBLY", "THREADING"} <= codes
|
||
assert "SURFACE_TREATMENT" in codes
|
||
details = {row["field"]: row["value"] for row in process["details"]}
|
||
assert details["tolerance"] == "±0.01"
|
||
assert details["surfaceRoughness"] == "Ra 3.2"
|
||
assert details["threadSpecification"] == "M30x1.5"
|
||
assert details["depthMm"] == "16 TIEF"
|
||
assert "M1" not in [row["value"] for row in process["details"]]
|
||
|
||
|
||
def test_real_dxf_recognizes_process_and_details():
|
||
parsed = inspect_dxf(_sample(_DEFAULT_SAMPLE))
|
||
fields = {row["field"]: row["value"] for row in parsed["fieldCandidates"]}
|
||
assert fields.get("threadSpecification") in {"M10", "M12", "M6", "M18x1.5"}
|
||
assert "M1" not in [row["value"] for row in parsed["fieldCandidates"]]
|
||
|
||
change_set = build_drawing_master_candidates(parsed)
|
||
ops = change_set["routingOperations"]
|
||
assert any(row["operationCode"] != "ENGINEERING_REVIEW" for row in ops)
|
||
assert change_set["processDetails"]
|
||
assert "processBlocks" in change_set
|
||
assert "processAssemblies" in change_set
|
||
assert "processLocations" in change_set
|
||
assert "processSequenceSteps" in change_set
|
||
for row in ops:
|
||
assert row["reviewRequired"] is True
|
||
assert row["standardTime"] is None
|
||
assert row["resourceCode"] is None
|
||
assert row["evidence"]
|
||
|
||
|
||
def test_real_parts_list_extracts_assembly_thread_and_depth():
|
||
understanding = build_drawing_understanding(inspect_dxf(_sample(_SAMPLE_003)))
|
||
assert understanding["processCandidates"]
|
||
assert understanding["routingOperations"]
|
||
details = {row["field"]: row["value"] for row in understanding["processDetails"]}
|
||
assert details.get("threadSpecification")
|
||
assert details.get("depthMm")
|
||
assert any(row["processCode"] == "ASSEMBLY" for row in understanding["processCandidates"])
|
||
assert any(row["processCode"] == "THREADING" for row in understanding["processCandidates"])
|
||
|
||
|
||
_CHINESE_PROCESS_TEXT = """
|
||
烟囱(9005)分段分为首面围壁组立、尾面围壁组立、侧壁组立、顶板组立等。
|
||
2.1首、尾围壁组立、侧壁组立、顶板组立在上建场地平胎位制作完成。
|
||
2.2烟囱分段在经托架改造的胎架上以首面围壁为胎,依次装配顶板组立、侧壁组立和尾面围壁组立,根据《9004分段建造工艺》要求穿插进行管系、舾装作业。
|
||
上建主体分段(9011、9012、9013、9014、9015、9016、9017分段)主要分为甲板组立、围堰双壳组立、外围壁组立、内部隔舱壁组立、伙食吊筒壁(仅9016分段)等组立。
|
||
3.1外围壁组立、内部隔舱壁组立在平胎位拼板,装焊骨材,形成平面组立。甲板板在分片制作场地拼板,然后甲板板上大组胎架进行拼板(单面焊)、划线、装焊骨材、横梁、纵桁等形成甲板组立。伙食吊筒壁在联合厂房进行下料、卷制、焊接。
|
||
3.2围堰双壳组立内的板片、隔板、顶板、骨材、筋板肘板等在联合厂房下料送至上建场地。在上建场地以一侧围板为胎装焊内部构件,翻身与另一侧围板装焊形成完整的围堰双壳组立。
|
||
9212/9312围堰分段的建造流程与3.2项类似。
|
||
9211/9311/9411/9412/9512分段在上建场地以甲板为胎反造。
|
||
9216/9316分段在上建场地制作成型。分段主要包含驾驶甲板平台组立、FR46横壁组立、箱形立柱和斜纵壁等组立。
|
||
6.2驾驶甲板平台中组立先以驾驶甲板为基装配隔板组立,完成焊接后翻身装配到驾驶甲板平台组立上,再装配FR46舷墙组立。
|
||
"""
|
||
|
||
|
||
def test_chinese_process_text_extracts_rich_key_information():
|
||
result = recognize_process_text(_CHINESE_PROCESS_TEXT)
|
||
|
||
codes = {row["blockCode"] for row in result["blocks"]}
|
||
assert "9005" in codes
|
||
assert any("9011" in code for code in codes)
|
||
assert any("9212/9312" in code or "9212" in code for code in codes)
|
||
assert any("9211/9311/9411/9412/9512" in code or "9211" in code for code in codes)
|
||
assert any("9216/9316" in code or "9216" in code for code in codes)
|
||
assert any("FR46" in code for code in codes)
|
||
|
||
assemblies = {row["assemblyName"] for row in result["assemblies"]}
|
||
assert {"首面围壁组立", "尾面围壁组立", "侧壁组立", "顶板组立", "甲板组立",
|
||
"围堰双壳组立", "外围壁组立", "内部隔舱壁组立", "驾驶甲板平台组立"} <= assemblies
|
||
assert any("FR46横壁组立" in name for name in assemblies)
|
||
|
||
ops = {row["processName"] for row in result["operations"]}
|
||
assert {"拼板", "装焊", "下料", "卷制", "翻身", "反造", "舾装", "管系", "焊接", "划线"} <= ops
|
||
|
||
locations = {row["location"] for row in result["locations"]}
|
||
assert {"上建场地", "联合厂房", "平胎位", "大组胎架"} <= locations
|
||
assert result["sequenceSteps"]
|
||
assert result["status"] == "PENDING_REVIEW"
|
||
|
||
|
||
def test_process_candidates_stay_pending_with_evidence():
|
||
understanding = build_drawing_understanding(inspect_dxf(_sample(_SAMPLE_003)))
|
||
for row in understanding["processCandidates"] + understanding["processDetails"]:
|
||
assert row["status"] == "PENDING_REVIEW"
|
||
assert row["reviewRequired"] is True
|
||
assert 0 < row["confidence"] <= 1
|
||
assert row["evidence"]
|