diff --git a/alembic/versions/f8a9b0c1d2e3_backfill_machine_area_criticality.py b/alembic/versions/f8a9b0c1d2e3_backfill_machine_area_criticality.py new file mode 100644 index 0000000..c911b40 --- /dev/null +++ b/alembic/versions/f8a9b0c1d2e3_backfill_machine_area_criticality.py @@ -0,0 +1,50 @@ +"""backfill machine area and criticality from existing location data + +Revision ID: f8a9b0c1d2e3 +Revises: e7f8a9b0c1d2 +Create Date: 2026-02-10 19:00:00.000000 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +revision: str = "f8a9b0c1d2e3" +down_revision: Union[str, None] = "e7f8a9b0c1d2" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def _extract_area(location: str) -> str: + parts = location.strip().split(" ", 1) + if len(parts) > 1: + return parts[1] + return location + + +def upgrade() -> None: + conn = op.get_bind() + + rows = conn.execute( + sa.text( + "SELECT id, location FROM machines WHERE area IS NULL AND location IS NOT NULL" + ) + ).fetchall() + + for row in rows: + machine_id, location = row[0], row[1] + area = _extract_area(location) + conn.execute( + sa.text("UPDATE machines SET area = :area WHERE id = :id"), + {"area": area, "id": machine_id}, + ) + + conn.execute( + sa.text("UPDATE machines SET criticality = 'major' WHERE criticality IS NULL") + ) + + +def downgrade() -> None: + pass diff --git a/scripts/seed.py b/scripts/seed.py index 16aad63..7091618 100644 --- a/scripts/seed.py +++ b/scripts/seed.py @@ -31,7 +31,7 @@ DATABASE_URL = os.getenv( ) TENANTS = [ - {"id": "spifox", "name": "SpiFox", "industry_type": "semiconductor"}, + {"id": "spifox", "name": "SpiFox", "industry_type": "press_forming"}, {"id": "enkid", "name": "Enkid", "industry_type": "manufacturing"}, {"id": "alpet", "name": "Alpet", "industry_type": "chemical"}, ] @@ -75,39 +75,169 @@ NOW = datetime.now(timezone.utc) MACHINES = { "spifox": [ { - "name": "CVD Chamber #1", - "equipment_code": "SF-CVD-001", - "model": "AMAT Centura 5200", - "manufacturer": "Applied Materials", - "location": "클린룸 Bay 3", - "area": "Bay 3", + "name": "반제품 프레스 #1", + "equipment_code": "SF-CUP-001", + "model": "Cup Making Press 60T", + "manufacturer": "SpiFox 자체설계", + "location": "1동 반제품 라인", + "area": "반제품 프레스", "criticality": "critical", - "rated_capacity": "200mm 웨이퍼", - "power_rating": "30kW", - "description": "화학기상증착 장비. SiO2, Si3N4 박막 증착용.", + "rated_capacity": "60T, UPH 520pcs", + "power_rating": "15kW", + "description": "알루미늄 슬러그 → CUP 형상 1차 성형. 딥드로잉 공법.", }, { - "name": "Etcher DRY-A", - "equipment_code": "SF-ETCH-001", - "model": "LAM 9600", - "manufacturer": "Lam Research", - "location": "클린룸 Bay 5", - "area": "Bay 5", + "name": "반제품 프레스 #2", + "equipment_code": "SF-CUP-002", + "model": "Cup Making Press 60T", + "manufacturer": "SpiFox 자체설계", + "location": "1동 반제품 라인", + "area": "반제품 프레스", "criticality": "critical", - "rated_capacity": "200mm 웨이퍼", - "power_rating": "25kW", - "description": "건식 식각 장비. Poly-Si, Metal 식각용.", + "rated_capacity": "60T, UPH 520pcs", + "power_rating": "15kW", + "description": "알루미늄 슬러그 → CUP 형상 1차 성형.", }, { - "name": "Sputter MC-200", - "equipment_code": "SF-SPT-001", - "model": "Ulvac SH-450", - "manufacturer": "ULVAC", - "location": "클린룸 Bay 7", - "area": "Bay 7", + "name": "완제품 프레스 #1", + "equipment_code": "SF-FIN-001", + "model": "Finish Press 80T", + "manufacturer": "SpiFox 자체설계", + "location": "2동 완제품 성형 라인 A", + "area": "완제품 성형 A", + "criticality": "critical", + "rated_capacity": "80T, UPH 480pcs", + "power_rating": "18.5kW", + "description": "2차 딥드로잉 성형. 콘덴서 케이스 최종 형상.", + }, + { + "name": "완제품 프레스 #2", + "equipment_code": "SF-FIN-002", + "model": "Finish Press 80T", + "manufacturer": "SpiFox 자체설계", + "location": "2동 완제품 성형 라인 A", + "area": "완제품 성형 A", + "criticality": "critical", + "rated_capacity": "80T, UPH 480pcs", + "power_rating": "18.5kW", + "description": "2차 딥드로잉 성형.", + }, + { + "name": "완제품 프레스 #3", + "equipment_code": "SF-FIN-003", + "model": "Finish Press 80T", + "manufacturer": "SpiFox 자체설계", + "location": "2동 완제품 성형 라인 B", + "area": "완제품 성형 B", + "criticality": "critical", + "rated_capacity": "80T, UPH 480pcs", + "power_rating": "18.5kW", + }, + { + "name": "건조기 #1", + "equipment_code": "SF-DRY-001", + "model": "Hot Air Dryer 300", + "manufacturer": "대한열기", + "location": "1동 건조 구역", + "area": "건조/세척", "criticality": "major", - "rated_capacity": "Ti/TiN 300mm", - "power_rating": "20kW", + "rated_capacity": "반제품 건조 300kg/batch", + "power_rating": "25kW", + "description": "반제품 CUP 건조. 열풍 순환 방식.", + }, + { + "name": "탈유기 #1", + "equipment_code": "SF-DEG-001", + "model": "Degreaser SC-500", + "manufacturer": "세정산업", + "location": "2동 후공정 라인", + "area": "건조/세척", + "criticality": "major", + "rated_capacity": "500L 세척조", + "power_rating": "12kW", + "description": "성형유 제거. 초음파 탈유 방식.", + }, + { + "name": "세척기 #1", + "equipment_code": "SF-WSH-001", + "model": "Ultrasonic Washer UW-800", + "manufacturer": "세정산업", + "location": "2동 후공정 라인", + "area": "건조/세척", + "criticality": "major", + "rated_capacity": "800L 세척조", + "power_rating": "15kW", + "description": "탈유 후 초음파 세척.", + }, + { + "name": "열처리로 #1", + "equipment_code": "SF-HT-001", + "model": "Annealing Furnace AF-1200", + "manufacturer": "한국전기로", + "location": "2동 열처리 구역", + "area": "열처리", + "criticality": "critical", + "rated_capacity": "1200°C, 500kg/batch", + "power_rating": "80kW", + "description": "어닐링 열처리. 알루미늄 케이스 응력 제거.", + }, + { + "name": "머신비전 검사기 #1", + "equipment_code": "SF-VIS-001", + "model": "AI Vision Inspector V3", + "manufacturer": "SpiFox 자체개발", + "location": "2동 검사 라인", + "area": "검사/포장", + "criticality": "critical", + "rated_capacity": "검사속도 1,200pcs/min", + "power_rating": "3kW", + "description": "AI 머신러닝 기반 전수 외관검사. 치수/형상/결함 동시 검출.", + }, + { + "name": "머신비전 검사기 #2", + "equipment_code": "SF-VIS-002", + "model": "AI Vision Inspector V3", + "manufacturer": "SpiFox 자체개발", + "location": "2동 검사 라인", + "area": "검사/포장", + "criticality": "critical", + "rated_capacity": "검사속도 1,200pcs/min", + "power_rating": "3kW", + }, + { + "name": "자동포장기 #1", + "equipment_code": "SF-PKG-001", + "model": "Auto Packer AP-200", + "manufacturer": "포장기계", + "location": "2동 포장 구역", + "area": "검사/포장", + "criticality": "minor", + "rated_capacity": "200box/h", + "power_rating": "5kW", + }, + { + "name": "구름다리 컨베이어", + "equipment_code": "SF-CVY-001", + "model": "Overbridge Conveyor OB-50", + "manufacturer": "물류자동화", + "location": "1동-2동 연결", + "area": "물류/반송", + "criticality": "major", + "rated_capacity": "반제품 이송 50m", + "power_rating": "7.5kW", + "description": "1동(반제품)→2동(완제품) 반제품 및 빈 통 이송.", + }, + { + "name": "AMR #1", + "equipment_code": "SF-AMR-001", + "model": "자율주행로봇 AGV-300", + "manufacturer": "로봇솔루션", + "location": "2동 물류 구역", + "area": "물류/반송", + "criticality": "minor", + "rated_capacity": "적재 300kg", + "power_rating": "배터리 48V", + "description": "완제품 자동 반송. 성형→후공정→포장 경로.", }, ], "enkid": [ @@ -187,74 +317,171 @@ MACHINES = { # 부품: (name, part_number, category, lifecycle_type, lifecycle_limit, alarm_threshold, counter_source, current_value) PARTS = { "spifox": { - "CVD Chamber #1": [ - ("Showerhead", "SH-CVD-01", "소모품", "hours", 3000, 80, "auto_time", 2450), + "반제품 프레스 #1": [ ( - "Heater Block", - "HB-CVD-01", + "컷트핀 (Cut Pin)", + "CP-CUP-01", + "금형", + "count", + 150000, + 80, + "manual", + 128000, + ), + ( + "컷트메스 (Cut Die)", + "CD-CUP-01", + "금형", + "count", + 200000, + 85, + "manual", + 165000, + ), + ("스트리퍼", "ST-CUP-01", "금형", "count", 300000, 80, "manual", 210000), + ], + "반제품 프레스 #2": [ + ( + "컷트핀 (Cut Pin)", + "CP-CUP-02", + "금형", + "count", + 150000, + 80, + "manual", + 95000, + ), + ( + "컷트메스 (Cut Die)", + "CD-CUP-02", + "금형", + "count", + 200000, + 85, + "manual", + 120000, + ), + ("스트리퍼", "ST-CUP-02", "금형", "count", 300000, 80, "manual", 180000), + ], + "완제품 프레스 #1": [ + ( + "정형메스 (Forming Die)", + "FD-FIN-01", + "금형", + "count", + 100000, + 80, + "manual", + 87000, + ), + ( + "컷트핀 (Cut Pin)", + "CP-FIN-01", + "금형", + "count", + 120000, + 80, + "manual", + 98000, + ), + ("펀치", "PH-FIN-01", "금형", "count", 250000, 85, "manual", 175000), + ("성형유 필터", "OF-FIN-01", "소모품", "hours", 720, 80, "auto_time", 580), + ], + "완제품 프레스 #2": [ + ( + "정형메스 (Forming Die)", + "FD-FIN-02", + "금형", + "count", + 100000, + 80, + "manual", + 62000, + ), + ( + "컷트핀 (Cut Pin)", + "CP-FIN-02", + "금형", + "count", + 120000, + 80, + "manual", + 73000, + ), + ("펀치", "PH-FIN-02", "금형", "count", 250000, 85, "manual", 140000), + ], + "완제품 프레스 #3": [ + ( + "정형메스 (Forming Die)", + "FD-FIN-03", + "금형", + "count", + 100000, + 80, + "manual", + 45000, + ), + ( + "컷트핀 (Cut Pin)", + "CP-FIN-03", + "금형", + "count", + 120000, + 80, + "manual", + 52000, + ), + ("펀치", "PH-FIN-03", "금형", "count", 250000, 85, "manual", 95000), + ], + "건조기 #1": [ + ( + "히터 엘리먼트", + "HE-DRY-01", "핵심부품", "hours", 8000, 85, "auto_time", - 3200, - ), - ("O-Ring Seal", "OR-CVD-01", "소모품", "count", 500, 90, "manual", 480), - ], - "Etcher DRY-A": [ - ( - "RF Generator", - "RF-ETCH-01", - "핵심부품", - "hours", - 10000, - 80, - "auto_time", - 4500, + 6500, ), ( - "Focus Ring", - "FR-ETCH-01", + "순환팬 베어링", + "FB-DRY-01", "소모품", "hours", - 2000, - 85, - "auto_time", - 1750, - ), - ("ESC Plate", "ESC-ETCH-01", "핵심부품", "count", 5000, 80, "manual", 2100), - ], - "Sputter MC-200": [ - ( - "Target (Ti)", - "TG-SPT-01", - "소모품", - "hours", - 1500, + 15000, 80, "auto_time", - 1200, + 11000, ), + ], + "열처리로 #1": [ + ("발열체", "HT-HT-01", "핵심부품", "hours", 10000, 80, "auto_time", 7200), ( - "Magnet Assembly", - "MA-SPT-01", - "핵심부품", + "온도 센서 (TC)", + "TC-HT-01", + "계측", "hours", - 12000, - 85, - "auto_time", 5000, + 90, + "auto_time", + 4200, ), + ("단열재", "IN-HT-01", "소모품", "hours", 20000, 85, "auto_time", 14000), + ], + "머신비전 검사기 #1": [ ( - "교정 인증서", - "CAL-SPT-01", - "인증", - "date", - 365, - 80, - "manual", - 0, + "카메라 모듈", + "CM-VIS-01", + "핵심부품", + "hours", + 20000, + 85, + "auto_time", + 8500, ), + ("조명 LED", "LD-VIS-01", "소모품", "hours", 10000, 80, "auto_time", 7800), + ("교정 인증서", "CAL-VIS-01", "인증", "date", 365, 80, "manual", 0), ], }, "enkid": { @@ -424,80 +651,236 @@ PARTS = { TEMPLATES = { "spifox": [ { - "name": "CVD 챔버 일일 점검", + "name": "프레스 일일 점검", "subject_type": "equipment", - "machine_name": "CVD Chamber #1", + "machine_name": "완제품 프레스 #1", "schedule_type": "daily", "inspection_mode": "checklist", "items": [ { - "name": "챔버 진공도 확인", - "category": "진공", + "name": "타발 압력", + "category": "프레스", "data_type": "numeric", - "unit": "mTorr", - "spec_min": 0, - "spec_max": 10, - "warning_max": 8, + "unit": "T", + "spec_min": 70, + "spec_max": 85, + "warning_min": 72, + "warning_max": 83, }, { - "name": "가스 유량 정상 여부", - "category": "가스", + "name": "슬라이드 스트로크", + "category": "프레스", + "data_type": "numeric", + "unit": "mm", + "spec_min": 148, + "spec_max": 152, + "warning_min": 149, + "warning_max": 151, + }, + { + "name": "성형유 공급 정상", + "category": "윤활", "data_type": "boolean", }, { - "name": "히터 온도", - "category": "온도", + "name": "금형 상태", + "category": "금형", + "data_type": "select", + "select_options": ["양호", "주의", "교체필요"], + }, + { + "name": "이상 소음/진동 여부", + "category": "기계", + "data_type": "boolean", + }, + { + "name": "성형유 온도", + "category": "윤활", "data_type": "numeric", "unit": "°C", - "spec_min": 350, - "spec_max": 450, - "warning_min": 360, - "warning_max": 440, - }, - { - "name": "RF 전력 안정성", - "category": "전기", - "data_type": "select", - "select_options": ["정상", "불안정", "이상"], - }, - { - "name": "파티클 카운트", - "category": "청정도", - "data_type": "numeric", - "unit": "ea", - "spec_min": 0, + "spec_min": 30, "spec_max": 50, - "warning_max": 30, + "warning_max": 45, + }, + { + "name": "안전장치 작동 확인", + "category": "안전", + "data_type": "boolean", + }, + { + "name": "타발수 (현재)", + "category": "생산", + "data_type": "numeric", + "unit": "회", }, ], }, { - "name": "웨이퍼 두께 품질검사", + "name": "반제품 프레스 일일 점검", + "subject_type": "equipment", + "machine_name": "반제품 프레스 #1", + "schedule_type": "daily", + "inspection_mode": "checklist", + "items": [ + { + "name": "타발 압력", + "category": "프레스", + "data_type": "numeric", + "unit": "T", + "spec_min": 50, + "spec_max": 65, + "warning_min": 52, + "warning_max": 63, + }, + { + "name": "슬러그 공급 정상", + "category": "소재", + "data_type": "boolean", + }, + { + "name": "금형 상태", + "category": "금형", + "data_type": "select", + "select_options": ["양호", "주의", "교체필요"], + }, + { + "name": "성형유 공급 정상", + "category": "윤활", + "data_type": "boolean", + }, + { + "name": "이상 소음/진동 여부", + "category": "기계", + "data_type": "boolean", + }, + { + "name": "안전장치 작동 확인", + "category": "안전", + "data_type": "boolean", + }, + ], + }, + { + "name": "열처리로 일일 점검", + "subject_type": "equipment", + "machine_name": "열처리로 #1", + "schedule_type": "daily", + "inspection_mode": "checklist", + "items": [ + { + "name": "노내 온도", + "category": "온도", + "data_type": "numeric", + "unit": "°C", + "spec_min": 350, + "spec_max": 420, + "warning_min": 360, + "warning_max": 410, + }, + { + "name": "온도 균일도 (편차)", + "category": "온도", + "data_type": "numeric", + "unit": "°C", + "spec_min": 0, + "spec_max": 10, + "warning_max": 7, + }, + { + "name": "배기 팬 작동 정상", + "category": "기계", + "data_type": "boolean", + }, + {"name": "도어 씰 상태", "category": "기계", "data_type": "boolean"}, + { + "name": "가스 분위기", + "category": "분위기", + "data_type": "select", + "select_options": ["정상", "불안정", "이상"], + }, + ], + }, + { + "name": "콘덴서 케이스 치수검사", "subject_type": "product", - "product_code": "WF-300-SI", + "product_code": "CASE-AL-1012", "schedule_type": "ad_hoc", "inspection_mode": "measurement", "items": [ { - "name": "중심부 두께", - "category": "두께", + "name": "컷트핀 내경", + "category": "치수", "data_type": "numeric", - "unit": "μm", - "spec_min": 725, - "spec_max": 775, - "warning_min": 730, - "warning_max": 770, + "unit": "mm", + "spec_min": 9.98, + "spec_max": 10.02, + "warning_min": 9.99, + "warning_max": 10.01, }, { - "name": "Edge 두께 편차", - "category": "두께", + "name": "컷트핀 외경", + "category": "치수", + "data_type": "numeric", + "unit": "mm", + "spec_min": 12.48, + "spec_max": 12.52, + "warning_min": 12.49, + "warning_max": 12.51, + }, + { + "name": "정형메스 내경", + "category": "치수", + "data_type": "numeric", + "unit": "mm", + "spec_min": 9.96, + "spec_max": 10.04, + "warning_min": 9.98, + "warning_max": 10.02, + }, + { + "name": "CR값", + "category": "형상", + "data_type": "numeric", + "unit": "mm", + "spec_min": 0.3, + "spec_max": 0.5, + "warning_min": 0.32, + "warning_max": 0.48, + }, + { + "name": "케이스 높이", + "category": "치수", + "data_type": "numeric", + "unit": "mm", + "spec_min": 11.9, + "spec_max": 12.1, + "warning_min": 11.95, + "warning_max": 12.05, + }, + { + "name": "표면 조도 (Ra)", + "category": "표면", "data_type": "numeric", "unit": "μm", "spec_min": 0, - "spec_max": 5, - "warning_max": 3, + "spec_max": 0.8, + "warning_max": 0.6, + }, + {"name": "외관 결함 유무", "category": "외관", "data_type": "boolean"}, + { + "name": "결함 유형", + "category": "외관", + "data_type": "select", + "select_options": [ + "없음", + "스크래치", + "찍힘", + "필름짤림", + "오염", + "기타", + ], + "is_required": False, }, - {"name": "표면 결함 유무", "category": "외관", "data_type": "boolean"}, { "name": "비고", "category": "기타", @@ -757,6 +1140,8 @@ async def seed(): manufacturer=m.get("manufacturer"), installation_date=install_date, location=m.get("location"), + area=m.get("area"), + criticality=m.get("criticality", "major"), rated_capacity=m.get("rated_capacity"), power_rating=m.get("power_rating"), description=m.get("description"),