diff --git a/.claude/skills/ubisam-deliverables/references/build_02설계_pptx.py b/.claude/skills/ubisam-deliverables/references/build_02설계_pptx.py new file mode 100644 index 0000000..966878b --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/build_02설계_pptx.py @@ -0,0 +1,322 @@ +# -*- coding: utf-8 -*- +""" +ILSOS PrismaticRD PTOP — 2.설계 SW_설계서.pptx 자동 생성 스크립트. + +실행: + set PYTHONUTF8=1 && py .claude/skills/ubisam-deliverables/references/build_02설계_pptx.py + +출력: docs/[Ubisam산출물]/02.설계/설계리뷰_DR/SW_설계서.pptx +""" +import os, json, subprocess, sys, tempfile + +HERE = os.path.dirname(os.path.abspath(__file__)) +REPO = os.path.abspath(os.path.join(HERE, "..", "..", "..", "..")) +BUILDER = os.path.join(REPO, ".claude", "skills", "ubisam-ppt-make", "build_deck.py") +OUTPUT = os.path.join(REPO, "docs", "[Ubisam산출물]", "02.설계", "설계리뷰_DR", "SW_설계서.pptx") + +SPEC = { + "doc_title": "ILSOS PrismaticRD PTOP SW 설계서", + "slides": [ + { + "type": "cover", + "tag": "SW DESIGN DOCUMENT", + "title": "ILSOS PrismaticRD PTOP\nSW 설계서", + "subtitles": [ + "LGES OC5 PtoP Welder — Prismatic 전지 점 용접 자동화 SW", + "Aurora 2.0 / .NET 4.8.1 / WPF · MX Component · ACS EtherCAT · Ajin IO" + ], + "team": "로봇자동화사업부", + "contacts": ["노도연 대리 otteryeonny@ubisam.com"] + }, + { + "type": "history", + "no": "00.", + "title": "변경 이력", + "subtitle": "Revision History", + "rows": [ + ["01", "0.1", "2026-06-29", "노도연", "소스 기반 최초 작성 (기능명세·시퀀스·신호정의·IO맵·TactTime)"], + ["02", "0.2", "2026-06-30", "노도연", "SW_설계서.pptx 최초 생성 (build_02설계_pptx.py)"] + ] + }, + { + "type": "agenda", + "title": "Contents", + "subtitle": "목차", + "items": [ + {"no": "01", "title": "기능명세서", "sub": "메뉴 구조 · View별 기능"}, + {"no": "02", "title": "제어 시퀀스 / 인터록", "sub": "SeqAutoRunPTOP · 인터록 정의"}, + {"no": "03", "title": "PLC-PC 신호 정의", "sub": "통신 사양 · PC↔PLC 신호 목록"}, + {"no": "04", "title": "모션축 / IO 맵", "sub": "축 정의 · DI/DO 맵"}, + {"no": "05", "title": "TactTime 산정서", "sub": "자동 1사이클 단위동작별 T/T"} + ] + }, + # ── 01. 기능명세서 ────────────────────────────────────────────── + {"type": "divider", "no": "01", "title": "기능명세서", "subtitle": "UI / Function Specification"}, + { + "type": "table", + "no": "01.", "title": "메뉴 구조", + "subtitle": "RegisterViews — 메인 메뉴 · 권한 · 서브뷰 · 기능 요약", + "headers": ["메인 메뉴", "권한", "서브뷰", "기능"], + "widths": [1.6, 1.2, 3.4, 5.2], "center_cols": [0, 1], + "rows": [ + ["AutoRun", "NONE", "UCMainAutoRun", "자동 운전 메인(SeqAutoRun 연동), 페이지/상태 표시"], + ["Recipe", "NONE", "UCRecipe / UCRecipeChangeHistory / UCModel", "레시피 편집·변경이력·모델"], + ["Manual", "NONE", "UCManualScannerOperation / UCManualModelFinder", "스캐너 수동조작·모델 파인더"], + ["Calibration","NONE", "UCScannerCalibration / UCCameraPixelResolution", "스캐너 보정·픽셀 해상도"], + ["Monitoring", "NONE", "UCPLCBit / UCPLCWord / UCIOInterfaceView", "PLC 비트(M)·워드(D)·IO 모니터링"], + ["Device", "LEVEL3", "DeviceSettingView", "디바이스 설정"], + ["Setting", "LEVEL1", "EquipSettingView / LogSettingView / AutoBackupView2", "장비·로그·자동백업 설정"], + ["Alarm", "NONE", "AlarmHistoryView / AlarmDescView", "알람 이력·설명"] + ] + }, + { + "type": "table", + "no": "01.", "title": "View별 기능 (1/2)", + "subtitle": "MainView · RecipeView · ManualView", + "headers": ["View", "no", "기능 영역", "기능명", "연동"], + "widths": [1.6, 0.6, 1.8, 2.8, 4.6], "center_cols": [0, 1], + "rows": [ + ["MainView", "1", "운전", "StartSequence / Stop", "SeqAutoRunPTOP"], + ["MainView", "2", "상태", "UpdatePageNum", "EVENT_AUTO_RUN_UPDATE_PAGENUM"], + ["MainView", "3", "상태", "WorkingPosition", "eWorkingPosition"], + ["MainView", "4", "운전", "Auto→Manual+Resume", "bit15(PLC_WELD_READY_REQ)"], + ["MainView", "5", "표시", "ScannerCanvasCtrlTeaching", "레이어/오브젝트"], + ["RecipeView", "1", "레시피", "ActivateRecipe", "RecipeManager"], + ["RecipeView", "2", "레시피", "RecipeChangeHistory", "RchHandler"], + ["RecipeView", "3", "모델", "UCModel / EquipmentSpecificModel", "EquipmentSpecificModel_PTOP"], + ["ManualView", "1", "수동", "ScannerOperation", "CommonDeviceIds.Scanner"], + ["ManualView", "2", "수동", "ModelFinder", "UCManualModelFinder"] + ] + }, + { + "type": "table", + "no": "01.", "title": "View별 기능 (2/2)", + "subtitle": "MonitoringView · CalibrationView · SettingView · AlarmView", + "headers": ["View", "no", "기능 영역", "기능명", "연동"], + "widths": [1.8, 0.6, 1.8, 2.8, 4.4], "center_cols": [0, 1], + "rows": [ + ["MonitoringView", "1", "모니터링", "PLCBitMonitoring", "ePLCDevice.M"], + ["MonitoringView", "2", "모니터링", "PLCWordMonitoring", "ePLCDevice.D"], + ["MonitoringView", "3", "모니터링", "IOInterfaceView", "IODevice"], + ["CalibrationView", "1", "보정", "UCScannerCalibration", "스캐너 보정"], + ["CalibrationView", "2", "보정", "UCCameraPixelResolution","카메라 픽셀 해상도"], + ["SettingView", "1", "설정", "EquipSettingView", "장비 설정"], + ["SettingView", "2", "설정", "LogSettingView / AutoBackupView2", "로그·자동백업"], + ["AlarmView", "1", "알람", "AlarmHistoryView", "AlarmCode_PTOP"], + ["AlarmView", "2", "알람", "AlarmDescView", "AlarmManager_PTOP"] + ] + }, + # ── 02. 제어 시퀀스 / 인터록 ──────────────────────────────────── + {"type": "divider", "no": "02", "title": "제어 시퀀스 / 인터록", "subtitle": "Control Sequence & Interlock"}, + { + "type": "flow", + "no": "02.", "title": "SeqAutoRunPTOP — 자동 시퀀스 (초기화 ~ 업로드)", + "subtitle": "Sequences/SeqAutoRunPTOP.cs — Builder() 노드 흐름 (1/2)", + "steps": [ + {"title": "Sequence_Enter", "code": "레시피/장비 초기화 · PC_AUTO_STATUS ON", "kind": "proc"}, + {"title": "CheckInitCondition", "code": "초기 조건 판단", "kind": "dec"}, + {"title": "CheckPageNo", "code": "PLC_PAGE_CHANGE_REQ(0x08) 대기", "kind": "proc"}, + {"title": "CheckRecipeNoComp", "code": "PLC_RECIPE_NO 확정/전환 · PC_RECIPE_NO 에코", "kind": "proc"}, + {"title": "CheckPageNoComp", "code": "PLC_PAGE_NO 3회 검증 · PC_PAGE_CHANGE_COMPLETE ACK", "kind": "proc"}, + {"title": "SeqUploadWeldingList","code": "레이어 선택 → 용접리스트 업로드 · offset(D70510) 적용", "kind": "proc"} + ], + "branch": {"title": "Resume", "sub": "핸드셰이크 스킵"} + }, + { + "type": "flow", + "no": "02.", "title": "SeqAutoRunPTOP — 자동 시퀀스 (발진 ~ 완료)", + "subtitle": "Sequences/SeqAutoRunPTOP.cs — Builder() 노드 흐름 (2/2)", + "steps": [ + {"title": "WeldingStartReadBit", "code": "PLC_WELD_TRIGGER_REQ(0x10) 대기", "kind": "proc"}, + {"title": "PreScoutCheck", "code": "M70509 ON → PreScout 실행", "kind": "dec"}, + {"title": "WaitWeldingStart", "code": "PC_WELD_TRIGGER_ACK ON / WELD_COMPLETE OFF", "kind": "proc"}, + {"title": "ExecuteWelding", "code": "HeadTheta 인터락 → Gate ON(15ms) → ExecuteList → Gate OFF", "kind": "proc"}, + {"title": "WriteWeldComplete", "code": "PC_WELD_TRIGGER_ACK OFF · PC_WELD_COMPLETE ON", "kind": "proc"}, + {"title": "CheckCycleStop", "code": "루프백 or SaveResult → WriteCompletionPLCBit", "kind": "dec"} + ], + "branch": {"title": "PostScout", "sub": "M70509 ON 시 실행"} + }, + { + "type": "table", + "no": "02.", "title": "인터록 정의", + "subtitle": "LaserInterlockSetting.cs · SeqAutoRunPTOP.cs", + "headers": ["#", "인터록", "조건", "동작"], + "widths": [0.5, 2.3, 4.0, 4.6], "center_cols": [0], + "rows": [ + ["1", "Head Theta 발진 게이트", "LaserInterlockSetting.HeadThetaInterlockEnabled ON · Front/Down ±tol 벗어남", "발진 차단 (ALARM_LASER_HEAD_THETA_INTERLOCK). PLC 미연결 시 fail-closed"], + ["2", "레시피별 방향 차단", "RecipeLaserInterlock.Front/Down 체크", "해당 방향 구역이면 발진 차단"], + ["3", "중알람(Heavy)", "PLC_HEAVY_ALARM_STATUS(0x05)", "AlarmManager_PTOP.StopSequenceByAlarm → 시퀀스 정지"], + ["4", "Door / EMO", "PLC_DOOR_INTERLOCK(0x06) / PLC_EMO_STATUS(0x07)", "안전 정지"], + ["5", "업로드 실패", "용접 오브젝트 0개 / Upload Success=false", "시퀀스 중단 (return null)"] + ] + }, + # ── 03. PLC-PC 신호 정의서 ────────────────────────────────────── + {"type": "divider", "no": "03", "title": "PLC-PC 신호 정의서", "subtitle": "PLC Interface Signal Definition"}, + { + "type": "table", + "no": "03.", "title": "통신 사양", + "subtitle": "EquipmentSetup.cs · PLCController.Address.cs", + "headers": ["항목", "내용"], + "widths": [2.8, 8.6], "center_cols": [], + "rows": [ + ["PLC 기종", "Mitsubishi Melsec"], + ["통신 방식", "MX Component (ePLCNetType.MXComponent) — EquipmentSetup.RegisterDevices()"], + ["맵 파일", "CSV (PLCMMapFileType.CSV)"], + ["데이터 갱신", "폴링(10ms 주기 ReadBit/ReadWord) + 이벤트 비트(REQ/ACK)"], + ["디바이스 영역", "비트=M, 워드=D (입력 M70500/D70500 · 출력 M70000/D70000 — 제안)"], + ["핸드셰이크 규칙", "요청(REQ)→처리→완료(ACK/COMPLETE). 단일 트리거 PLC_PAGE_CHANGE_REQ(0x08)"], + ["인터락", "Door(0x06)·EMO(0x07)·Heavy알람(0x05) → 시퀀스 정지. HeadTheta(D70510) 발진 게이트"], + ["Watchdog", "PLC_LIVE_STATUS(0x00) ↔ PC_LIVE_STATUS(0x00) Alive 토글"] + ] + }, + { + "type": "table", + "no": "03.", "title": "PC → PLC 신호 목록", + "subtitle": "eBitOutput / eWordOutput (Write) — 주요 신호", + "headers": ["No", "신호명(enum)", "디바이스", "Type", "의미"], + "widths": [0.7, 3.3, 1.4, 0.8, 5.2], "center_cols": [0, 2, 3], + "rows": [ + ["0x00", "PC_LIVE_STATUS", "M70000", "BIT", "PC Alive 토글 (Watchdog)"], + ["0x01", "PC_AUTO_STATUS", "M70001", "BIT", "Auto 상태 — Enter 시 ON"], + ["0x05", "PC_HEAVY_ALARM_STATUS", "M70005", "BIT", "중알람"], + ["0x06", "PC_DOOR_INTERLOCK", "M70006", "BIT", "Door 인터락"], + ["0x07", "PC_EMO_STATUS", "M70007", "BIT", "EMO"], + ["0x08", "PC_PAGE_CHANGE_COMPLETE", "M70008", "BIT", "페이지 확정 ACK — PageNo 일치 시 ON"], + ["0x0F", "PC_WELD_READY_ACK", "M70015", "BIT", "Resume ACK (bit15, 260621)"], + ["0x10", "PC_WELD_TRIGGER_ACK", "M70016", "BIT", "용접 트리거 ACK"], + ["0x11", "PC_WELD_BUSY", "M70017", "BIT", "Laser On 표시(발진중)"], + ["0x12", "PC_WELD_COMPLETE", "M70018", "BIT", "용접 완료"], + ["D0x01", "PC_PAGE_NO", "D70001", "INT", "페이지 번호 에코"], + ["D0x02", "PC_RECIPE_NO", "D70002", "INT", "레시피 번호 에코"] + ] + }, + { + "type": "table", + "no": "03.", "title": "PLC → PC 신호 목록", + "subtitle": "eBitInput / eWordInput (Read) — 주요 신호", + "headers": ["No", "신호명(enum)", "디바이스", "Type", "의미"], + "widths": [0.7, 3.3, 1.4, 0.8, 5.2], "center_cols": [0, 2, 3], + "rows": [ + ["0x00", "PLC_LIVE_STATUS", "M70500", "BIT", "PLC Alive (Watchdog)"], + ["0x05", "PLC_HEAVY_ALARM_STATUS", "M70505", "BIT", "중알람 → 시퀀스 정지"], + ["0x06", "PLC_DOOR_INTERLOCK", "M70506", "BIT", "Door 인터락"], + ["0x07", "PLC_EMO_STATUS", "M70507", "BIT", "EMO"], + ["0x08", "PLC_PAGE_CHANGE_REQ", "M70508", "BIT", "단일 게이트 트리거 — PageNo+RecipeNo 확정"], + ["0x09", "PLC_SCOUT_ENABLE", "M70509", "BIT", "Scout 상시 ON → 용접 전/후 Scout"], + ["0x0F", "PLC_WELD_READY_REQ", "M70515", "BIT", "Weld_01 Ready → Resume 트리거 (bit15)"], + ["0x10", "PLC_WELD_TRIGGER_REQ", "M70516", "BIT", "용접 발진 요청"], + ["D0x01", "PLC_PAGE_NO", "D70501", "INT", "페이지 번호 (1-base, 3회 검증)"], + ["D0x02", "PLC_RECIPE_NO", "D70502", "INT", "레시피 번호 (11/21/22/31/32)"], + ["D0x0A", "HEAD_THETA_POSITION", "D70510", "INT", "Laser1 Head Theta — 발진 인터락"] + ] + }, + # ── 04. 모션축 / IO 맵 ────────────────────────────────────────── + {"type": "divider", "no": "04", "title": "모션축 / IO 맵", "subtitle": "Motion Axis & I/O Map"}, + { + "type": "table", + "no": "04.", "title": "통신·제어 구성", + "subtitle": "EquipmentSetup.cs · PAIX_PTOP.cs · architecture.md §4", + "headers": ["구분", "대상", "컨트롤러/디바이스", "통신/버스", "수량·범위"], + "widths": [1.8, 1.8, 2.8, 2.0, 3.0], "center_cols": [0], + "rows": [ + ["PLC통신", "외부 신호 제어", "Mitsubishi Melsec", "MX Component", "1"], + ["I/O", "디지털 IO", "Ajin(AJINEXTEK)", "필드버스", "DI/DO"], + ["Laser", "Core/Ring 레이저", "IO Gate(TruPulse nano 계열)", "DO 게이트", "2(Core+Ring)"], + ["Scanner", "레이저 스캐너", "RTC LaserScanner", "RTC", "1"], + ["모션(Servo)", "X/Y 축", "ACS SPiiPlus", "EtherCAT", "2축 (제안)"], + ["모션(Servo)", "Z 축", "Ajin Motion Board", "보드", "1축 (제안)"], + ["Vision", "카메라/측정", "MIL 기반 AuroraVision", "—", "(현재 미사용)"], + ["상/하위(MES)", "호스트 연동", "Stream(LBAD/RPC)", "TCP", "옵션"] + ] + }, + { + "type": "table", + "no": "04.", "title": "모션축 정의", + "subtitle": "ACS SPiiPlus(EtherCAT) · Ajin Motion Board — 파라미터 미확정(제안)", + "headers": ["Axis", "축명", "용도", "컨트롤러/통신", "행정/범위", "최대속도", "Home방식"], + "widths": [0.7, 0.8, 1.7, 2.5, 1.7, 1.5, 2.5], "center_cols": [0, 1], + "rows": [ + ["0", "X", "셀 위치 X", "ACS SPiiPlus / EtherCAT", "(제안)", "(제안)", "(제안)"], + ["1", "Y", "셀 위치 Y", "ACS SPiiPlus / EtherCAT", "(제안)", "(제안)", "(제안)"], + ["2", "Z", "포커스/높이", "Ajin Motion Board", "(제안)", "(제안)", "(제안)"], + ["—", "Scanner θ/XY", "빔 편향(갈바노)", "RTC LaserScanner", "List 기반 Jump/Mark", "—", "보정테이블"] + ] + }, + { + "type": "table", + "no": "04.", "title": "I/O 맵 — DI / DO", + "subtitle": "PAIX_PTOP.ePrismaticDIAddress / ePrismaticDOAddress (Ajin 필드버스)", + "headers": ["구분", "CH(No)", "신호명", "Active", "설명"], + "widths": [0.8, 1.0, 3.2, 1.0, 5.4], "center_cols": [0, 1, 3], + "rows": [ + ["DI", "9", "CoreLaser_Laser_Ready", "High", "Core 레이저 Ready"], + ["DI", "10", "CoreLaser_Error_State", "High", "Core 레이저 에러"], + ["DI", "11", "CoreLaser_Laser_Output_State", "High", "Core 발진 출력 상태"], + ["DI", "12", "CoreLaser_Control_Status", "High", "Core 제어 상태"], + ["DI", "13", "RingLaser_Laser_Ready", "High", "Ring 레이저 Ready"], + ["DI", "14", "RingLaser_Error_State", "High", "Ring 레이저 에러"], + ["DI", "15", "RingLaser_Laser_Output_State", "High", "Ring 발진 출력 상태"], + ["DI", "23", "RingLaser_Control_Status", "High", "Ring 제어 상태"], + ["DO", "9", "CoreLaser_Control", "High", "Core 레이저 게이트(발진 ON/OFF)"], + ["DO", "10", "CoreLaser_ErrorClearing", "High", "Core 에러 클리어"], + ["DO", "13", "RingLaser_Control", "High", "Ring 레이저 게이트(발진 ON/OFF)"], + ["DO", "14", "RingLaser_ErrorClearing", "High", "Ring 에러 클리어"], + ["DO", "17", "EX_Red_Control", "High", "EX Red(가이드/표시등) 제어"] + ] + }, + # ── 05. TactTime 산정서 ───────────────────────────────────────── + {"type": "divider", "no": "05", "title": "TactTime 산정서", "subtitle": "Tact Time Analysis"}, + { + "type": "table", + "no": "05.", "title": "PC TactTime — 자동 1사이클 (음극 1페이지 기준)", + "subtitle": "SeqAutoRunPTOP.cs 지연·로그 상수 기준 (제안 — 현장 실측 시 확정)", + "headers": ["Unit", "단위동작", "T/T(ms)", "PC", "PLC", "출처"], + "widths": [0.7, 3.0, 1.3, 0.6, 0.6, 5.2], "center_cols": [0, 2, 3, 4], + "rows": [ + ["1", "Page Change 요청 대기", "가변", "●", "●", "CheckPageNo (폴링 10ms, timeout 60s)"], + ["2", "Recipe 확정/전환", "~0~수백","●", "—", "CheckRecipeNoComp (동일이면 ~0, 전환 시 Activate+reload)"], + ["3", "Page 3회 검증", "~600", "●", "●", "CheckPageNoComp (200ms×3)"], + ["4", "용접리스트 업로드", "가변", "●", "—", "SeqUploadWeldingList (라인수 의존)"], + ["5", "발진 요청 대기", "가변", "●", "●", "WeldingStartReadBit"], + ["6", "준비 응답", "~205", "●", "●", "WaitWeldingStart (200+5ms)"], + ["7", "Gate ON delay", "15", "●", "—", "laserEmissionGateDelayMs (TruPulse 매뉴얼 ≥10ms)"], + ["8", "실제 발진(ExecuteList)", "~285", "—", "—", "로그 예시 'TactTime: [285ms]' (라인수 의존)"], + ["9", "Gate OFF + BUSY OFF", "~50", "●", "—", "ExecuteWelding 후미"], + ["10", "Jump+CorrectionTable 복귀", "~500", "—", "—", "Thread.Sleep(500)"], + ["11", "완료비트 송신", "~55", "●", "●", "WriteWeldComplete (50+5ms)"], + ["(옵션)","Pre/Post Scout", "+100/회","●","Vision","SCOUT_PROCEED_DELAY_MS, M70509 ON 시"] + ] + }, + { + "type": "closing", + "title": "Thank You", + "message": "본 설계서는 소스코드 분석 기반 0.2버전입니다. 파라미터 확정 후 갱신 예정.", + "team": "로봇자동화사업부", + "contacts": ["노도연 대리 otteryeonny@ubisam.com"] + } + ] +} + + +def main(): + os.makedirs(os.path.dirname(OUTPUT), exist_ok=True) + fd, spec_path = tempfile.mkstemp(suffix=".json", prefix="sw_spec_") + try: + with os.fdopen(fd, "w", encoding="utf-8") as f: + json.dump(SPEC, f, ensure_ascii=False, indent=2) + result = subprocess.run( + [sys.executable, BUILDER, "--input", spec_path, "--output", OUTPUT], + capture_output=True, text=True, encoding="utf-8" + ) + if result.stdout: + print(result.stdout.strip()) + if result.returncode != 0: + print("[ERROR]", result.stderr.strip(), file=sys.stderr) + sys.exit(result.returncode) + finally: + if os.path.exists(spec_path): + os.remove(spec_path) + + +if __name__ == "__main__": + main()