diff --git a/.claude/skills/doyeon_note/SKILL.md b/.claude/skills/doyeon_note/SKILL.md new file mode 100644 index 0000000..5c04d26 --- /dev/null +++ b/.claude/skills/doyeon_note/SKILL.md @@ -0,0 +1,42 @@ +--- +name: doyeon_note +description: 도연님의 작업일지를 기록합니다. "/doyeon_note <내용>" 형태로 호출하면 docs/작업일지/yyyy_mm_dd.md 파일에 오늘 날짜로 내용을 추가합니다. 작업일지 기록, 일지 작성, 노트 추가 요청 시 사용. +--- + +# doyeon_note — 작업일지 기록 스킬 + +사용자가 말한 내용을 `docs/작업일지/yyyy_mm_dd.md` 파일에 기록합니다. + +## 동작 절차 + +1. **오늘 날짜 확인**: 시스템의 현재 날짜를 `yyyy_mm_dd` 형식으로 만든다. + (컨텍스트에 `currentDate`가 있으면 그 값을 사용. 없으면 `date +%Y_%m_%d` 실행) + +2. **대상 파일 경로**: `docs/작업일지/.md` + +3. **내용 정리**: 사용자가 `args`로 전달한 내용을 작업일지 항목으로 정리한다. + - 단순 한 줄이면 그대로 한 항목으로. + - 여러 작업/생각이 섞여 있으면 자연스럽게 불릿(`-`)으로 나눈다. + - 사용자가 적은 의도/뉘앙스는 보존하고, 임의로 내용을 지어내지 않는다. + +4. **파일 기록**: + - 파일이 **없으면** 아래 템플릿으로 새로 만든다. + - 파일이 **있으면** 기존 내용을 Read한 뒤, 맨 아래에 새 항목을 이어 붙인다(append). 기존 내용은 절대 지우지 않는다. + - 같은 날 여러 번 호출되면 시각(`HH:MM`) 소제목을 붙여 구분한다. + +5. 기록 후, 어떤 파일에 무엇을 추가했는지 한 줄로 사용자에게 알린다. + +## 새 파일 템플릿 + +```markdown +# 작업일지 — yyyy-mm-dd + +## HH:MM +- (항목) +``` + +## 주의 + +- `args`가 비어 있으면 무엇을 기록할지 사용자에게 한 줄로 물어본다. +- 날짜/파일명 형식은 반드시 `yyyy_mm_dd`를 지킨다. +- 작업일지는 사용자의 기록이므로, 내용을 요약·각색하지 말고 사용자의 표현을 최대한 살린다. diff --git a/.claude/skills/ptop-deliverables/SKILL.md b/.claude/skills/ptop-deliverables/SKILL.md new file mode 100644 index 0000000..27f8b9e --- /dev/null +++ b/.claude/skills/ptop-deliverables/SKILL.md @@ -0,0 +1,79 @@ +--- +name: ptop-deliverables +description: >- + LGES OC5 PtoP Welder 전용 산출물 자동 생성·갱신 스킬. + 프로젝트 소스를 분석해 docs/[Ubisam산출물]/ 하위 MD 산출물(색상 디자인 포함)을 + 생성하거나 최신화한다. 다른 PC에서 Git clone 후 이 스킬 한 번으로 전체 산출물 + 재생산 가능. "ptop 산출물", "산출물 업데이트", "/ptop-deliverables" 요청 시 사용. +--- + +# PTOP 산출물 자동 생성 SKILL + +LGES OC5 PtoP Welder(`ILSOS_PrismaticRD_PTOP`) 전용. +소스를 분석해 6단계 산출물 MD를 **Ubisam 디자인 시스템**(배너·뱃지·진행률 포함) 기준으로 생성한다. + +## 참조 맵 + +| # | 파일 | 용도 | +|---|---|---| +| D | [references/design-system.md](references/design-system.md) | 색상 팔레트·배너·뱃지 HTML 템플릿 | +| S | [references/source-map.md](references/source-map.md) | 소스 파일 → 산출물 필드 매핑 | + +## 소스 진실원 — 읽는 순서 + +다른 PC에서 실행 시 아래 파일을 순서대로 읽어 산출물을 채운다. + +| 순서 | 파일 경로 | 추출 정보 | +|---|---|---| +| 1 | `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs` | BIT/WORD 신호 enum 전수 | +| 2 | `Machines/ILSOS_PrismaticRD_PTOP/EquipmentSetup.cs` | 디바이스 등록·메뉴·뷰 목록 | +| 3 | `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs` | 시퀀스 노드·인터락·TactTime | +| 4 | `Machines/ILSOS_PrismaticRD_PTOP/Device/IO/PAIX_PTOP.cs` | DI/DO 채널 맵 | +| 5 | `docs/architecture.md` | 플랫폼·모션·디바이스 개요 | +| 6 | `docs/[Ubisam산출물]/_meta/project.yaml` | 프로젝트 코드·팀 정보 | + +## 실행 절차 + +### STEP 0 — 진입 체크 +`docs/[Ubisam산출물]/` 폴더 존재 여부 확인. +없으면 `/ubisam-deliverables` 스캐폴딩을 먼저 실행 후 재진입. + +### STEP 1 — 소스 분석 +소스 진실원 6개를 순서대로 읽어 추출한다: +- PLC BIT/WORD 신호 목록 (eBitInput/Output, eWordInput/Output) +- 등록 디바이스 (PLC·IO·Laser·Scanner·Motion·Vision) +- 메뉴·뷰 트리 (RegisterViews — 권한 레벨 포함) +- 시퀀스 노드 흐름 + 인터락 조건 + 정지/에러 처리 +- TactTime 상수 (지연 ms, 타임아웃 s, 루프 슬립) +- DI/DO 채널 번호·신호명·설명 (PAIX_PTOP.cs) + +### STEP 2 — 산출물 MD 생성/갱신 + +[design-system.md](references/design-system.md) 배너·뱃지 템플릿을 적용해 +아래 파일을 **없으면 생성, 있으면 내용 갱신**한다. + +| 산출물 | 경로 | 채움 소스 | +|---|---|---| +| PLC-PC 신호정의서 | `02.설계/IO_DB설계/PLC_PC_인터페이스_신호정의서.md` | PLCController.Address.cs | +| 모션축·IO 맵 정의서 | `02.설계/IO_DB설계/모션축_IO맵_정의서.md` | EquipmentSetup + PAIX_PTOP | +| 기능명세서 | `02.설계/UIUX_기능설계/기능명세서.md` | EquipmentSetup.RegisterViews | +| 시퀀스·인터락 정의서 | `02.설계/시퀀스_인터록/제어시퀀스_인터락_정의서.md` | SeqAutoRunPTOP.Builder | +| TactTime 산정서 | `02.설계/시퀀스_인터록/TactTime_산정서.md` | SeqAutoRunPTOP 지연 상수 | + +각 파일 구조: **배너(진네이비) → 부제(파랑) → 내용 테이블/mermaid → enum 요약** + +### STEP 3 — _task 카드 갱신 +`01~06` 각 `_task.md` 상태·진행률을 현황 반영해 갱신. +적용 디자인: 상태 뱃지 + 유형 뱃지 + `` 진행률 바. + +### STEP 4 — 분석 로그 기록 +`[분석]/YYYY_MM_DD_PTOP설계산출물-분석.md` 작성. +내용: 소스→필드 매핑 표 + 미확정 항목 목록 + 진실원 인덱스. + +## 불변 규칙 +- **비침습**: `docs/[Ubisam산출물]/` 밖은 읽기만. 소스·CLAUDE.md 수정 금지. +- **MD 200줄 상한**: 초과 시 분할 + 상대경로 링크. +- **출처 명시**: 채운 값마다 `— 출처: 파일명:심볼` 주석. +- **미확정 표기**: 주소·행정·속도 등 미확정값 → `(제안)`. +- **멱등**: 재실행 시 배너·뱃지는 교체, 테이블 데이터는 행 추가/갱신. +- **Aurora 고정 문구**: "운영 SW는 LG PRI의 Aurora 플랫폼으로 개발한다." diff --git a/.claude/skills/ptop-deliverables/references/design-system.md b/.claude/skills/ptop-deliverables/references/design-system.md new file mode 100644 index 0000000..a2ec800 --- /dev/null +++ b/.claude/skills/ptop-deliverables/references/design-system.md @@ -0,0 +1,126 @@ +# PTOP 산출물 — Ubisam 디자인 시스템 + +> MD 산출물에 적용하는 HTML 색상 템플릿 모음. +> VS Code Markdown Preview / Typora / GitLab 렌더링 기준. + +--- + +## 색상 팔레트 + +| 구분 | 이름 | 색 코드 | 용도 | +|---|---|---|---| +| 배너 | 진네이비 | `#1F4E78` | 문서 최상단 제목 배너 | +| 헤더 | 파랑 | `#2E75B6` | 배너 하단 부제 바, 섹션 강조 | +| 연파랑 | — | `#DDEBF7` | 테이블 홀수 행 참조 (xlsx 전용, MD 생략 가능) | +| 상태·해야 할 일 | 회색 | `#757575` | 미착수 | +| 상태·분석 진행 | 청색 | `#1976D2` | 선택됨 | +| 상태·업무 진행중 | 파랑 | `#1565C0` | 진행 중 | +| 상태·진행 완료 | 연두 | `#689F38` | 완료 | +| 유형·오류수정 | 빨강 | `#D32F2F` | — | +| 유형·불합리 | 주황 | `#F57C00` | — | +| 유형·미팅 | 청록 | `#00796B` | 1.분석/협의 | +| 유형·설계 | 연두 | `#689F38` | 2.설계 | +| 유형·개발 | 파랑 | `#1976D2` | — | +| 유형·제작 | 남파랑 | `#283593` | 3.제작 | +| 유형·입고셋업 | 보라 | `#7B1FA2` | 4.셋업 | +| 유형·적용 및 모니터링 | 하늘 | `#0288D1` | 5.양산 | +| 유형·요청대응 | 남색 | `#1A237E` | 6.CS | + +--- + +## 1. 문서 배너 (최상단) + +산출물 MD 파일 1번째 줄부터 적용. + +```html +
+문서 제목 +
+
+근거: 소스 경로  ·  Aurora 2.0 / .NET 4.8.1 / WPF +
+``` + +--- + +## 2. 상태 뱃지 + +`_task.md` 상태 필드에 사용. 값에 맞춰 색 코드 교체. + +```html + +해야 할 일 + + +분석 진행 + + +업무 진행중 + + +진행 완료 +``` + +--- + +## 3. 유형 뱃지 + +```html +미팅 +설계 +제작 +입고셋업 +적용 및 모니터링 +요청대응 +개발 +``` + +--- + +## 4. 진행률 바 + +`` 태그 + 텍스트 병기 (렌더러 호환). + +```html + 85% +``` + +--- + +## 5. _task.md 전체 템플릿 + +```markdown +
+N.단계명 — LGES OC5 PtoP Welder +
+
+LGES_PTOP_01_20260605-XXXX +
+ +| 필드 | 값 | +|---|---| +| 유형 | 유형명 | +| 상태 | 상태명 | +| 담당자 / 참조자 | {미정} / {미정} | +| 진행률 | **N%** | +| Hashtag | 태그1 / 태그2 | +``` + +--- + +## 6. 산출물 섹션 헤더 + +산출물 MD 내 시트 구분 헤더. + +```markdown +## 시트① 제목 +``` + +--- + +## 7. 미확정·제안 강조 + +```markdown +> [!NOTE] +> 아래 값은 소스 enum offset 기반 `(제안)`. PLC 맵 CSV 입수 후 확정. +``` diff --git a/.claude/skills/ptop-deliverables/references/source-map.md b/.claude/skills/ptop-deliverables/references/source-map.md new file mode 100644 index 0000000..597a851 --- /dev/null +++ b/.claude/skills/ptop-deliverables/references/source-map.md @@ -0,0 +1,105 @@ +# PTOP 소스 → 산출물 필드 매핑 + +> 이 파일은 소스 파일과 산출물 필드 간 1:1 매핑 인덱스. +> 다른 PC에서 스킬 실행 시 이 파일을 보고 어떤 소스를 읽어야 할지 판단한다. + +--- + +## A. PLC-PC 인터페이스 신호정의서 + +출력: `02.설계/IO_DB설계/PLC_PC_인터페이스_신호정의서.md` + +| 시트 | 소스 파일 | 읽어야 할 심볼 | +|---|---|---| +| 통신 사양 | `Device/PLC/PLCContoller.Config.cs` | `ePLCNetType`, `PLCMMapFileType`, 베이스 주소 설정 | +| PC→PLC (Write) | `Device/PLC/PLCController.Address.cs` | `eBitOutput`, `eWordOutput` enum 전수 | +| PLC→PC (Read) | `Device/PLC/PLCController.Address.cs` | `eBitInput`, `eWordInput` enum 전수 | +| 핸드셰이크 시퀀스 | `Sequences/SeqAutoRunPTOP.cs` | `CheckPageNo`, `CheckRecipeNoComp`, `CheckPageNoComp`, `WaitWeldingStart`, `WriteWeldComplete` | + +**PLC 주소 규칙**: +- `eBitOutput` offset → `M70000 + offset` +- `eBitInput` offset → `M70500 + offset` +- `eWordOutput` offset → `D70000 + offset` +- `eWordInput` offset → `D70500 + offset` +- 두 번째 블록(0x65~, 양극 P) → `M70600~` (현재 미사용) + +--- + +## B. 모션축·IO 맵 정의서 + +출력: `02.설계/IO_DB설계/모션축_IO맵_정의서.md` + +| 시트 | 소스 파일 | 읽어야 할 심볼 | +|---|---|---| +| 통신·제어 구성 | `EquipmentSetup.cs` | `RegisterDevices()` 내 모든 `Register*Device` 호출 | +| 모션축 정의 | `docs/architecture.md §4` | AuroraACS(X/Y), AuroraMotionBoard(Z), RTC(Scanner) | +| I/O 맵 DI | `Device/IO/PAIX_PTOP.cs` | `ePrismaticDIAddress` enum — 채널 번호·신호명 | +| I/O 맵 DO | `Device/IO/PAIX_PTOP.cs` | `ePrismaticDOAddress` enum — 채널 번호·신호명 | + +**모션축 주의사항**: +- X/Y 행정·속도·Home 방식 = 소스에 없음 → 전부 `(제안)` 표기. +- `AuroraACS` = EtherCAT SPiiPlus. `AuroraMotionBoard` = Ajin 보드. + +--- + +## C. 기능명세서 / UI 설계 + +출력: `02.설계/UIUX_기능설계/기능명세서.md` + +| 탭 | 소스 파일 | 읽어야 할 심볼 | +|---|---|---| +| 메뉴 구조 | `EquipmentSetup.cs` | `RegisterViews()` 전수 (MenuId, ViewType, AuthLevel) | +| MainView | `Views/Main/UCMainAutoRun*.cs` | 버튼·이벤트·바인딩 목록 | +| RecipeView | `Views/Recipe/UCRecipe*.cs` | ActivateRecipe, RecipeChangeHistory, UCModel | +| ManualView | `Views/Manual/UCManualScannerOperation*.cs` | ScannerOperation, ModelFinder | +| MonitoringView | `Views/Monitoring/UCPLC*.cs`, `UCIOInterfaceView.cs` | PLCBit, PLCWord, IOInterface | +| Sequence 흐름 | `Sequences/SeqAutoRunPTOP.cs` | Builder() 노드 목록 순서대로 | + +**8탭 고정** (xlsx ref 호환): OverView / MainView / RecipeView / ManualView / DeviceView / SettingView / VisionView / Sequence 흐름 + +--- + +## D. 제어 시퀀스·인터락 정의서 + +출력: `02.설계/시퀀스_인터록/제어시퀀스_인터락_정의서.md` + +| 섹션 | 소스 파일 | 읽어야 할 심볼 | +|---|---|---| +| 메인 시퀀스 노드 흐름 | `SeqAutoRunPTOP.cs` | `Builder()` 내 모든 AddNode·AddTransition | +| 단계별 동작 요약 | `SeqAutoRunPTOP.cs` | 각 노드 메서드 구현 첫 줄 주석·로그 | +| 인터락 정의 | `Settings/LaserInterlockSetting.cs` | `HeadThetaInterlockEnabled`, `ThetaTolerance` | +| 인터락 정의 (추가) | `SeqAutoRunPTOP.cs` | `LaserInterlock`, `PLC_HEAVY_ALARM_STATUS` 처리 | +| 정지/에러 처리 | `SeqAutoRunPTOP.cs` | `OnStopped`, `OnForceError`, `ResumeMode` | + +**Resume(bit15) 흐름**: `PLC_WELD_READY_REQ(0x0F)` 수신 → 팝업 + Auto→Manual 전환 → `PC_WELD_READY_ACK(0x0F)` ON → 1사이클 후 OFF. + +--- + +## E. TactTime 산정서 + +출력: `02.설계/시퀀스_인터록/TactTime_산정서.md` + +| 단위동작 | 소스 파일 | 상수/값 | +|---|---|---| +| Page 3회 검증 | `SeqAutoRunPTOP.cs` | `PAGE_VERIFY_DELAY_MS` × 3 (≈200ms×3 = 600ms) | +| 준비 응답 | `SeqAutoRunPTOP.cs` | `WELD_START_DELAY_MS` (200ms) + 소신호(5ms) | +| Gate ON delay | `SeqAutoRunPTOP.cs` | `laserEmissionGateDelayMs` = 15ms | +| 실제 발진 | `SeqAutoRunPTOP.cs` | 로그 "TactTime: [Nms]" — 예시 285ms (라인 수 의존) | +| Jump 복귀 | `SeqAutoRunPTOP.cs` | `Thread.Sleep(500)` | +| 완료비트 | `SeqAutoRunPTOP.cs` | 50ms + 5ms | +| Pre/Post Scout | `SeqAutoRunPTOP.cs` | `SCOUT_PROCEED_DELAY_MS` = 100ms/회 (M70509 ON 시) | + +--- + +## F. _task.md 진행률 기준 (현황 기준) + +| 단계 | 상태 | 진행률 | 근거 | +|---|---|---|---| +| 1.분석/협의 | 해야 할 일 | 0% | 사양서 미입수 | +| 2.설계 | 업무 진행중 | 85% | xlsx 4종 완료, 모션 파라미터 미확정 | +| 3.제작 | 해야 할 일 | 0% | CheckList 미작성 | +| 4.셋업 | 해야 할 일 | 0% | 현장 셋업 미완료 | +| 5.양산 | 업무 진행중 | 40% | 운영매뉴얼 3종 완료, 나머지 미완료 | +| 6.CS | 해야 할 일 | 0% | 미착수 | + +> 이 값은 스킬 실행 시점 기준. 재실행 시 소스·폴더 현황 재확인 후 갱신. diff --git a/.claude/skills/ubisam-deliverable/SKILL.md b/.claude/skills/ubisam-deliverable/SKILL.md new file mode 100644 index 0000000..1540d5d --- /dev/null +++ b/.claude/skills/ubisam-deliverable/SKILL.md @@ -0,0 +1,49 @@ +--- +name: ubisam-deliverable +description: Ubisam 산출물 재생산 하네스의 오케스트레이터. 타사 양식 산출물(Doc/산출물문서의 xlsx·pptx)을 Ubisam 협업관리 양식(상태4·유형9·6단계 슬롯·필드 스키마)으로 분석·재배치·재생산·검증하는 분석가·작성가·검증가 3에이전트 팀을 구동한다. "Ubisam 산출물 만들어/재생산/재배치", "협업관리 양식으로", "산출물 채움", "이 문서 Ubisam 단계로", 그리고 후속 표현("다시 실행", "재실행", "업데이트", "수정", "보완", "OO만 다시", "이전 결과 기반") 시 반드시 이 스킬을 사용할 것. 단순 양식 질문은 ubisam-format-map만으로 답할 수 있다. +--- + +# ubisam-deliverable — Ubisam 산출물 재생산 오케스트레이터 + +타사 양식 산출물을 **Ubisam 양식으로 재생산**하는 3에이전트 팀(생성-검증 파이프라인)을 조율한다. + +- 실행 모드: **에이전트 팀** (분석 → 재생산 ↔ 검증) +- 팀원: `ubisam-analyst`, `ubisam-producer`, `ubisam-qa` (모두 `model: "opus"`) +- 공유 스킬: `ubisam-format-map` (양식 enum·스키마·슬롯 매핑 — 모든 팀원이 먼저 읽음) +- 작업공간: 대상별 `_workspace/`, 최종 산출물은 지정 경로 또는 `docs/[Ubisam산출물]/` 하위 + +## Phase 0: 컨텍스트 확인 +1. 대상 산출물과 작업 디렉토리를 정한다. 기본 작업공간: `docs/[Ubisam산출물]/_workspace/`. +2. 실행 모드 판별: + - `_workspace/` 없음 → **초기 실행**(Phase 1부터) + - `_workspace/` 있음 + 부분 수정 요청 → **부분 재실행**(해당 에이전트만) + - `_workspace/` 있음 + 새 입력 → **새 실행**(기존을 `_workspace_prev/`로 이동) +3. `ubisam-format-map` 스킬을 로드해 양식 기준을 확보한다. + +## Phase 1: 분석 (analyst) +- `ubisam-analyst`에게 대상 문서를 주고 구조 분석·타설비 잔재 식별·6단계 슬롯 매핑을 요청한다. +- 산출: `_workspace/01_analyst_{문서}.md`. + +## Phase 2: 재생산 ↔ 검증 (producer ↔ qa, 점진적) +- `ubisam-producer`가 매핑·채움 항목으로 산출물을 재생산한다(xlsx=openpyxl, pptx=`ubisam-ppt-make`, md). +- 각 문서 완성 직후 `ubisam-qa`가 **점진적으로** 검증(enum·슬롯·스키마·잔재·출처·자동화). +- Critical/Major는 producer가 수정 후 재검증. 통과까지 반복(최대 합리적 횟수, 1회 재시도 원칙). + +## Phase 3: 종합·보고 +- 최종 산출물 경로, qa 통과 여부, 미정/TBD 항목, 출처를 사용자에게 요약 보고한다. +- 중간 산출물(`_workspace/`)은 보존(감사 추적). + +## 데이터 전달 프로토콜 +- 태스크 기반(`TaskCreate`/`TaskUpdate`)으로 진행 추적, 파일 기반(`_workspace/`)으로 산출물 전달, 메시지 기반(`SendMessage`)으로 실시간 조율. +- 파일명: `{phase}_{agent}_{artifact}.{ext}` (예: `01_analyst_sw점검결과.md`). + +## 에러 핸들링 +- 에이전트 실패 시 1회 재시도, 재실패하면 해당 결과 없이 진행하고 보고서에 누락을 명시한다. +- 상충 데이터는 삭제하지 않고 출처를 병기한다. + +## 테스트 시나리오 +- **정상 흐름**: `23. SW 점검 결과` xlsx → analyst가 SW Version/Gantt를 6단계 슬롯(제작/양산 등)에 매핑 → producer가 Ubisam 양식 시트로 재배치 → qa가 enum/스키마 검증 → 통과 보고. +- **에러 흐름**: 대상 파일이 편집기 잠금 → producer가 잠금 경고·작업 사본 진행 → qa가 원본 미반영 표시 → 사용자에게 "원본 닫기 후 반영 필요" 보고. + +## 후속 작업 +- 부분 수정 요청("OO만 다시")은 해당 에이전트만 재호출하고, 이전 `_workspace/` 산출물을 기반으로 변경분만 갱신한다. diff --git a/.claude/skills/ubisam-deliverables/README.md b/.claude/skills/ubisam-deliverables/README.md new file mode 100644 index 0000000..baa8941 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/README.md @@ -0,0 +1,36 @@ +# ubisam-deliverables (Claude Code Skill) + +Ubisam 협업관리 구조로 **어떤 프로젝트에서든 동일한 산출물 트리·양식**을 생성·관리하는 스킬. +`ref/` 원본 파일 없이도 동작하도록 양식 골격을 MD에 내장(자기완결). + +## 설치 +대상 프로젝트에 폴더째 복사: +``` +<프로젝트>/.claude/skills/ubisam-deliverables/ +``` +(개인 전역으로 쓰려면 `~/.claude/skills/` 아래에 둬도 됨.) + +## 사용 +- "Ubisam 산출물 구조 만들어줘" / "산출물 트리 스캐폴딩" → 트리 생성(A). +- "이 양식 채워줘"(`산출물문서/양식/`에 파일 투입 후) → 양식 채움(B). + +## 구성 +``` +SKILL.md # 인덱스 + 오케스트레이션(0 하네스 / A 스캐폴딩 / B 채움) +references/ + 00-harness.md # 호스트 레포 맞물림·비침습 계약 + 01-tree.md # 목표 트리·배치 규칙 + 02-enums.md # 상태4·유형9·티켓코드 + 03-schema.md # 일감·프로젝트 스키마 + 04-stage-deliverables.md# 6단계↔슬롯 매핑 + 05-form-schemas.md # 설계 양식 시트·컬럼(ref 불필요) + 06-procedure.md # 분석→채움 절차 + 07-fill-mapping.md # 프로젝트 분석→산출물 필드 자동 채움 +templates/ + _task.template.md # 일감 카드 템플릿 + project.yaml.template # 프로젝트 메타 템플릿 +``` + +## 원칙 +- 모든 MD 200줄 이내, 상호 링크로 매핑. +- enum 고정(상태/유형) · 출처 명시 · 조건부 서식 보존 · 레포 루트 보호. diff --git a/.claude/skills/ubisam-deliverables/references/00-harness.md b/.claude/skills/ubisam-deliverables/references/00-harness.md new file mode 100644 index 0000000..4e5b241 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/00-harness.md @@ -0,0 +1,47 @@ +# 00 · 하네스 (호스트 프로젝트 맞물림 설계) + +> 이 스킬이 **이미 CLAUDE.md·.claude가 있는 임의의 소스 레포**에 들어가 기존 에이전트와 +> 충돌 없이 맞물려 동작하기 위한 계약. SKILL.md가 가장 먼저 참조한다. + +## 비침습 계약 (절대 규칙) +- **읽기만**: 호스트의 `CLAUDE.md`, `.claude/`, `RULES.md`, `src/`·소스코드는 입력으로만 읽는다. +- **쓰기 격리**: 모든 생성/수정은 `docs/[Ubisam산출물]/` 하위로 한정. +- **우선순위**: 호스트 루트 `RULES.md`/`CLAUDE.md` > 본 산출물 규칙. 충돌 시 호스트 우선. +- **멱등**: 폴더·파일은 "없으면 생성, 있으면 유지". 재실행이 기존 산출물을 덮어쓰지 않는다. + +## 입력 (호스트에서 읽는 진실원) +| 입력 | 용도 | +|---|---| +| 루트 `CLAUDE.md` | 설비·플랫폼·디바이스·시퀀스 등 프로젝트 사실 | +| 소스코드(`src/`,`Machines/` 등) | PLC/모션/IO/Vision 구성, 메인 시퀀스 추출 | +| `README*`, 설계 문서 | 보조 컨텍스트 | +| git 원격/폴더명 | 프로젝트 코드·명 자동 추정 | + +추출 사실 → 산출물 필드 매핑은 [07-fill-mapping.md](07-fill-mapping.md). + +## 데이터 흐름 (호스트 에이전트와 맞물림) +``` +[호스트 CLAUDE.md + 소스] ──읽기──▶ (분석) ──▶ 사실 추출 + │ │ + ▼ ▼ + docs/[Ubisam산출물]/ 트리 생성 ◀── 매핑(07) ── 산출물 필드 채움 + │ + ▼ + _task.md 진행률 갱신 + [작업일지] 기록 +``` +- 스킬은 **온디맨드** 호출(트리거 문구). 호스트 에이전트 루프를 가로채지 않는다. +- 분석/검색은 호스트 에이전트의 도구(Grep/Read)를 그대로 활용. + +## 프로젝트 자동 인식 +1. `project_code`: git 원격명·폴더명·`CLAUDE.md`에서 추정 → 실패 시 사용자에게 질문. +2. `project_name`: `CLAUDE.md` 제목/설비명에서 추정. +3. 추정값은 `_meta/project.yaml`에 기록하고 `(추정)` 표기. 확정 전까지 문서에 `(제안)` 유지. + +## 트리거 문구 +- 생성: "산출물 구조 만들어줘", "Ubisam 트리 스캐폴딩". +- 채움: "설계 산출물 채워줘", "IO 맵 정의서 생성", "프로젝트 분석해서 산출물 채움". +- 갱신: "2.설계 진행률 갱신", "양산 단계 산출물 추가". + +## 안전장치 +- 호스트 파일 수정 요청이 들어오면 거부하고 `docs/` 내 대안 제시. +- 양식 엑셀은 원본 템플릿 복제 후 값만 덮어써 조건부 서식 보존([06-procedure.md](06-procedure.md)). diff --git a/.claude/skills/ubisam-deliverables/references/01-tree.md b/.claude/skills/ubisam-deliverables/references/01-tree.md new file mode 100644 index 0000000..ec09993 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/01-tree.md @@ -0,0 +1,41 @@ +# 01 · 목표 트리 & 배치 규칙 + +> 이 스킬만으로(ref 없이) 어떤 프로젝트에서도 동일 트리를 생성한다. + +## 배치 원칙 +- 산출물은 **소스 루트가 아니라 `docs/[Ubisam산출물]/` 하위**에 둔다. +- 루트의 `.claude/`·`CLAUDE.md`·`RULES.md`는 손대지 않는다. +- 우선순위: 레포 루트 `RULES.md` > 본 산출물 `RULES.MD`(스코프 하위). + +## 트리 + +``` +<소스레포루트>/ +├─ .claude/ CLAUDE.md RULES.md # (기존) 손대지 않음 +└─ docs/ + └─ [Ubisam산출물]/ + ├─ RULES.MD # 산출물 영역 규칙(이 스킬이 생성) + ├─ README.md # 트리 한눈에 + 시작하기 + ├─ _meta/ + │ ├─ project.yaml # → templates/project.yaml.template + │ ├─ status.enum.md # → 02-enums §상태 + │ ├─ task-type.enum.md # → 02-enums §유형 + │ ├─ task-schema.md # → 03-schema §일감 + │ └─ ticket-code.rule.md # → 02-enums §티켓코드 + ├─ 01.분석협의/ _task.md + 사양서/ 회의록_CR/ 마스터플랜/ + ├─ 02.설계/ _task.md + UIUX_기능설계/ 시퀀스_인터록/ IO_DB설계/ 설계리뷰_DR/ + ├─ 03.제작/ _task.md + 테스트결과_CheckList/ + ├─ 04.셋업/ _task.md + HW_IO모션점검/ 파라미터_레시피백업/ 이슈로그_IssueLog/ SAT_인수검사보고서/ + ├─ 05.양산/ _task.md + 운영매뉴얼_HMI/ 최종SW_마스터백업/ 프로젝트완료보고서/ + ├─ 06.CS/ _task.md + ├─ 산출물문서/ README.md + 양식/(빈 양식 드롭존) + ├─ ref/ # 참고자료(읽기전용) — 비어 있어도 무방 + ├─ [분석]/ # 에이전트 분석 산출물(날짜_주제.md) + └─ [작업일지]/ # 일자별 로그(yyyy_mm_dd.md) +``` + +산출물 슬롯 폴더는 비어 있으면 `.gitkeep`을 둔다. 슬롯 상세 매핑은 [04-stage-deliverables.md](04-stage-deliverables.md). + +## 폴더명 규칙 +- 단계: `NN.단계명`(2자리 zero-pad). 협업관리 일감 제목과 동일 어휘. +- 산출물 슬롯: 한글_영문 혼합 가능, 공백 대신 `_`. diff --git a/.claude/skills/ubisam-deliverables/references/02-enums.md b/.claude/skills/ubisam-deliverables/references/02-enums.md new file mode 100644 index 0000000..397b9aa --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/02-enums.md @@ -0,0 +1,56 @@ +# 02 · enum & 티켓코드 (회사 공통 마스터) + +> 임의 신규값 금지. 새 값 발견 시 이 파일을 먼저 갱신 후 사용. +> 스캐폴딩 시 §상태→`status.enum.md`, §유형→`task-type.enum.md`, §티켓코드→`ticket-code.rule.md`로 복사. + +## §상태(Status) — 4종 (전이 순서) + +``` +해야 할 일 → 분석 진행 → 업무 진행중 → 진행 완료 +``` + +| 코드(한글) | 색 | HEX | 칸반 컬럼 | +|---|---|---|---| +| 해야 할 일 | 회색 | `#757575` | Todo | +| 분석 진행 | 청색 | `#1976D2` | Select For dev | +| 업무 진행중 | 파랑 | `#1565C0` | InProgress | +| 진행 완료 | 연두 | `#689F38` | Done | + +컬럼 옵션: "상태 없으면 게시판 비표시", "WIP 초과 강조". + +## §유형(Type) — 9종 + +| 유형 | 색 | HEX | 6단계 매핑(관례) | +|---|---|---|---| +| 오류수정 | 빨강 | `#D32F2F` | — | +| 불합리 | 주황 | `#F57C00` | — | +| 미팅 | 청록 | `#00796B` | 1.분석/협의 | +| 설계 | 연두 | `#689F38` | 2.설계 | +| 개발 | 파랑 | `#1976D2` | — | +| 제작 | 남파랑 | `#283593` | 3.제작 | +| 입고셋업 | 보라 | `#7B1FA2` | 4.셋업 | +| 적용 및 모니터링 | 하늘 | `#0288D1` | 5.양산 | +| 요청대응 | 남색 | `#1A237E` | 6.CS | + +> 뱃지 HTML 템플릿은 [08-design-system.md](08-design-system.md) § 2·3 참조. + +## §우선순위 +- 확인값: `보통`(Normal). 그 외 등급 미확인 — 발견 시 보강. + +## §티켓코드 규칙 + +``` +{프로젝트코드}-{일련번호} +``` +- 프로젝트코드: `_meta/project.yaml › project_code` (예: `LGIT_SI_01_20260601`) +- 일련번호: 4자리 권장, 생성 순 증가. +- 전체 예: `LGIT_SI_01_20260601-1024` + +| 일감 | 일련번호(예) | +|---|---| +| 1.분석/협의 | 1023 | +| 2.설계 | 1024 | +| 3.제작 | 1025 | +| 4.셋업 | 1026 | +| 5.양산 | 1027 | +| 6.CS | 1028 | diff --git a/.claude/skills/ubisam-deliverables/references/03-schema.md b/.claude/skills/ubisam-deliverables/references/03-schema.md new file mode 100644 index 0000000..66a1b3c --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/03-schema.md @@ -0,0 +1,39 @@ +# 03 · 일감 & 프로젝트 스키마 + +> 스캐폴딩 시 §일감→`task-schema.md`, 프로젝트 메타→`project.yaml`(템플릿 사용). + +## §일감(Task) 필드 + +| 필드 | 비고 | +|---|---| +| 제목 | `N.단계명` (예: `2.설계`) | +| 티켓번호 | `{프로젝트코드}-{일련번호}` → [02-enums §티켓코드](02-enums.md) | +| 유형 | [02-enums §유형](02-enums.md) (9종) | +| 상태 | [02-enums §상태](02-enums.md) (4종) | +| 우선순위 | `보통` 등 | +| 담당자 / 참조자 | 이름(직급) 표기 (예: 홍길동(사원)) | +| 진행률 | % | +| Hashtag | 자유 태그 (예: 분석/검토/협의) | +| 등록일 / 수정일 / 종료일 | `yyyy/mm/dd` | +| 예상시간 | `2w 4d 6h 45m` 형식 | +| 수정 버전 | None 가능 | +| 첨부파일 | 최대 2.93GB | +| 체크리스트 | 의견글 / 시간변경이력 / 타임 트래킹 | + +`_task.md` 카드 양식은 [templates/_task.template.md](../templates/_task.template.md). + +## §프로젝트 메타 + +| 필드 | 예시/형식 | +|---|---| +| 프로젝트명 | `{사업부}_{고객사}_{설비명} 운영SW` | +| 종류 | Software / Hardware | +| 프로젝트 형식 | Kanban | +| 코드명 | `{CUST}_{TYPE}_{NN}_{YYYYMMDD}` | +| 사업부 / 팀 | `{사업부}` / `{팀}` | +| PL / 등록자 | `{이름}({직급})` | +| 계약일 / 납기 | `yyyy-mm-dd` | +| 권한 | 책임자 → 참여자 → 열람자 (3단계) | +| 알람 | 실시간 티켓 / 메일 / 댓글 | + +전체 템플릿(토큰 포함): [templates/project.yaml.template](../templates/project.yaml.template). diff --git a/.claude/skills/ubisam-deliverables/references/05-form-schemas.md b/.claude/skills/ubisam-deliverables/references/05-form-schemas.md new file mode 100644 index 0000000..356b8d5 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/05-form-schemas.md @@ -0,0 +1,94 @@ +# 05 · 설계 양식 시트·컬럼 스키마 + +> **재생산 방식 (2026-06-29 확정): 완전 독립 (ref 파일 불필요)** +> xlsx는 `xlsxfill.py` 의 하드코딩 스타일 상수와 `build_02설계_xlsx.py` 의 PtoP 데이터만으로 처음부터(`from scratch`) 생성한다. ref 원본 xlsx 없이도 동일한 외형을 재현한다. +> +> **재생산 도구 (영구 보존)** +> - `xlsxfill.py` — 같은 폴더. LG PRI 스타일 상수 + `make_workbook / write_banner / write_header_row / write_data_rows / group_merge` scratch-build API. +> - `build_02설계_xlsx.py` — 같은 폴더. PtoP 데이터 내장, 단독 실행으로 4개 xlsx 재생산. +> ``` +> cd E:\Work\GIT\LGES-OC5-PtoP-Welder +> set PYTHONUTF8=1 +> py .claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py +> ``` +> - 전제조건: Python 3.9+, openpyxl >= 3.1 (`pip install openpyxl`) +> +> **공통 관례**: ① 배너 B2=제목, B3=근거/부제 ② 미확정 값은 `(제안)` 표기 ③ Aurora 고정 문구 "운영 SW는 LG PRI의 Aurora 플랫폼으로 개발" + +--- + +## 스타일 상수 (xlsxfill.py 하드코딩값) + +| 구분 | 폰트 | 색상(fgColor) | 테두리 | +|---|---|---|---| +| 배너 행(B2) | 맑은 고딕 13pt Bold White(FFFFFF) | FF1F4E78 (진네이비) | medium all | +| 부제 행(B3) | 맑은 고딕 9pt | 없음 | medium L/R/T | +| 헤더 행 | 맑은 고딕 11pt Bold White(FFFFFF) | FF2E75B6 (파랑) | medium 좌우, thin 상하 | +| 데이터 행(홀수) | 맑은 고딕 10pt | FFDDEBF7 (연파랑) | medium 좌우, thin 상하 | +| 데이터 행(짝수) | 맑은 고딕 10pt | FFFFFFFF (흰색) | medium 좌우, thin 상하 | +| 기능명세서 배너 | 맑은 고딕 14pt Bold | 없음 | medium all | +| 기능명세서 헤더 | 맑은 고딕 11pt Bold | **없음** | thin all | +| 기능명세서 데이터 | 맑은 고딕 11pt | 없음 | thin all | +| TactTime 헤더 | 맑은 고딕 11pt | **없음** | thin all | +| TactTime 데이터 | 맑은 고딕 11pt | 없음 | thin all | + +> `write_header_row(fill=None)` — 배경 없음(기능명세서·TactTime 계열). 인자 생략 시 파랑(HEADER_FILL) 기본값. `fill=_UNSET` sentinel으로 구분. +> `TODAY = datetime.date.today()` — 실행일 자동 반영. 이력 셀(Update 컬럼)에 삽입. + +--- + +## A. 모션축·IO 맵 정의서 (슬롯 `02.설계/IO_DB설계/모션축_IO맵_정의서.xlsx`) +**3탭 고정** + +| 탭명 | 헤더(B4~) | 데이터 시작행 | PtoP 특이사항 | +|---|---|---|---| +| 통신·제어 구성 | 구분\|대상\|컨트롤러/디바이스\|통신/버스\|수량·범위\|비고 | B5 | PLC=MXComponent, IO=Ajin, Laser=IOGate, Scanner=RTC | +| 모션축 정의(ACS) | Axis\|축명(논리)\|용도\|컨트롤러/통신\|구동\|행정/범위\|최대속도\|Home방식\|Soft Limit\|동기/단축\|비고 | B5 | X/Y=ACS EtherCAT, Z=Ajin, Scanner θ/XY=RTC. 행정·속도·Home=(제안) | +| I-O 맵(Ajin) | No\|신호명\|Type\|권장모듈\|슬롯/CH\|Active\|설명/인터락\|비고 | B5 | Ajin DI 9~23 / DO 9~17 (`PAIX_PTOP.cs`) | + +## B. PLC-PC 인터페이스 신호 정의서 (슬롯 `02.설계/IO_DB설계/PLC_PC_인터페이스_신호정의서.xlsx`) +**4탭 고정** + +| 탭명 | 헤더(B4~) | 데이터 시작행 | +|---|---|---| +| 통신 사양 | 항목\|내용 | B5 | +| PC→PLC (Write) | No\|Seq/구분\|신호명\|방향\|디바이스(제안)\|Type\|의미 / 트리거\|응답·완료\|비고 | B5 | +| PLC→PC (Read) | 동일 | B5 | +| 핸드셰이크 시퀀스 | No\|Seq\|Step\|주체\|동작 / 신호\|디바이스(제안)\|비고 | B5 | + +- Seq 구분 enum: Status · Alarm · Process · Request · Info · Control · Model · Interlock +- 디바이스: 비트 `M70000~`(PC Write), `M70500~`(PLC Read). 워드 `D70000~`/`D70500~`. +- PtoP 핸드셰이크: PAGE_REQ 단일 게이트 → Recipe 확정 → Page 3회 검증 → 리스트 업로드 → 발진 → 완료. + +## C. 기능명세서 / UI 설계 (슬롯 `02.설계/UIUX_기능설계/기능명세서.xlsx`) +**8탭 고정** (우리 설비에 없는 탭도 양식 유지) + +| 탭명 | 헤더(행5~) | G3 배너 | PtoP 내용 | +|---|---|---|---| +| OverView | no.\|Content | — | SW 개요(Aurora·Win11·.NET·설비·모션·레이저·PLC·비전) | +| MainView | Update\|Funtion Area\|Funtion Name\|NO\|Sub\|Content | "다음은 Main 화면…" | AutoRun 시작/정지·페이지·Resume(bit15)·스캐너캔버스 | +| RecipeView | 동일 | "다음은 Recipe 화면…" | ActivateRecipe·변경이력·모델 편집 | +| ManualView | 동일 | "다음은 Manual 화면…" | ScannerOperation·ModelFinder·Calibration | +| DeviceView | 동일 | "다음은 Device 화면…" | DeviceSetting·PLCBit/Word·IOInterface | +| SettingView | 동일 | "다음은 Setting 화면…" | Equipment·Log·AutoBackup·Alarm·Stream | +| VisionView | 동일 | "다음은 Vision 화면…" | `(해당없음)` — AuroraVision 미사용 | +| Sequence 흐름 | Update\|Funtion Area\|Processing Name\|NO\|Content | — | PAGE_REQ게이트→Recipe→Page→ListUpload→발진→완료→루프백 | + +## D. TactTime 산정서 (슬롯 `02.설계/시퀀스_인터록/TactTime_산정서.xlsx`) +**2탭 고정** + +| 탭명 | 좌측 컬럼 | 우측 컬럼 | PtoP 내용 | +|---|---|---|---| +| PC TactTime | 공정\|Unit\|단위동작\|T/T | Working Process\|상세합계\|기준/참조합계 | SeqAutoRunPTOP.cs 타이밍 (Page gate·Recipe·발진·완료) | +| PLC TactTime | 동일 구조 | — | 미입수 → `(제안)` | + +## E. Flow Chart (슬롯 `02.설계/시퀀스_인터록/`) +- 형식: PPTX 흐름도. 메인 시퀀스: PAGE_REQ 단일게이트 → 리스트 업로드 → 발진 → 완료비트. 분기·인터락 표현. + +## enum 요약(양식 고정값) +- I/O Type: DI · DO · AI · AO +- 신호 Type: BIT · INT · INT32 +- PLC Seq 구분: Status · Alarm · Process · Request · Info · Control · Model · Interlock +- 운영 모드: 수동 · 반자동 · 자동 + +> 재생산 시 항상 `build_02설계_xlsx.py` 를 실행할 것. 스크립트는 cwd=repo root 기준으로 실행. diff --git a/.claude/skills/ubisam-deliverables/references/07-fill-mapping.md b/.claude/skills/ubisam-deliverables/references/07-fill-mapping.md new file mode 100644 index 0000000..01dc077 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/07-fill-mapping.md @@ -0,0 +1,40 @@ +# 07 · 자동 채움 매핑 (프로젝트 분석 → 산출물 필드) + +> "산출물 양식 필드 ← 호스트 레포의 진실원" 규칙. 프로젝트 비종속(휴리스틱) + PTOP 예시. +> 양식 시트·컬럼 골격은 [05-form-schemas.md](05-form-schemas.md), 절차는 [06-procedure.md](06-procedure.md). + +## 추출 휴리스틱 (어떤 프로젝트든) +| 추출 대상 | 탐색 방법(예) | +|---|---| +| 플랫폼/프레임워크 | `CLAUDE.md`, `*.csproj`/`*.sln`, `*.config` | +| PLC 기종·통신 | `grep -ri "melsec\|mx ?component\|slmp\|modbus\|ethernet/ip"` | +| 모션 컨트롤러·축 | `grep -ri "acs\|ajin\|axis\|ethercat\|servo"` | +| I/O 디바이스 | `grep -ri "beckhoff\|EL1\|EL2\|digital ?io\|io ?gate"` | +| Vision | `grep -ri "matrox\|MIL\|halcon\|cognex\|keyence"` | +| 메인 시퀀스 | `grep -ri "Seq\|Sequence\|Recipe\|Auto ?Run"` → 클래스/메서드 | +| 주소/메모리맵 | `*Address*.cs`, `*Map*`, `*.csv` 주소표 | + +## 설계 산출물 채움 매핑 (02.설계) +| 양식·필드 | 진실원(호스트) | +|---|---| +| 통신·제어 구성: 컨트롤러/통신 | 위 휴리스틱(PLC/모션/IO/Vision/Laser) | +| 모션축 정의: Axis·용도·범위 | 모션 설정·축 enum·파라미터 파일 | +| I/O 맵: 신호명·Type·모듈 | IO 정의/매핑 소스, 주소표 | +| PLC-PC 신호: 디바이스(M/D)·트리거 | PLC 인터페이스 주소 클래스(`*PLCController*Address*`) | +| 기능명세서: View·Function | UI/뷰모델 구조, 화면 클래스 | +| TactTime: Unit·단위동작 | 시퀀스 단계·동작 메서드 | + +미발견 값은 `(제안)` 표기 후 "최종 합의/소스 확인 시 확정" 단서. + +## PTOP 예시 (참고값) +- 설비: 각형 배터리 레이저 용접기(ILSOS OC5 PtoP Welder), Aurora 2.0 / .NET 4.8.1 / WPF +- PLC: Mitsubishi Melsec(MX Component) · Laser Scanner: RTC · Laser: IO Gate · Ajin Digital IO +- 메인 시퀀스: `SeqAutoRunPTOP`(Recipe/Page 핸드셰이크 → 용접리스트 업로드 → 발진 → 완료비트) +- PLC 주소: `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs` + +> 이 예시는 PTOP 한정. 다른 프로젝트에선 위 휴리스틱으로 동일 절차를 적용해 값만 교체. + +## 채움 원칙 +- 모든 채운 값에 **출처 주석**(파일·심볼 경로) 부기. +- 상태/유형은 [02-enums.md](02-enums.md) enum 고정. +- 새 디바이스 종류 발견 시 [05-form-schemas.md](05-form-schemas.md) enum에 추가 후 사용. diff --git a/.claude/skills/ubisam-deliverables/references/08-design-system.md b/.claude/skills/ubisam-deliverables/references/08-design-system.md new file mode 100644 index 0000000..95eee28 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/08-design-system.md @@ -0,0 +1,126 @@ +# 08 · Ubisam 산출물 디자인 시스템 + +> 모든 프로젝트의 산출물 MD에 공통 적용하는 HTML 색상 템플릿. +> VS Code Markdown Preview / Typora / GitLab 렌더링 기준. +> 스캐폴딩·채움 어느 단계에서든 이 파일을 참조해 배너·뱃지·진행률을 적용한다. + +--- + +## 색상 팔레트 + +| 구분 | 이름 | 색 코드 | 용도 | +|---|---|---|---| +| 배너 | 진네이비 | `#1F4E78` | 문서 최상단 제목 배너 | +| 헤더 | 파랑 | `#2E75B6` | 배너 하단 부제 바, 섹션 헤더 강조 | +| 상태·해야 할 일 | 회색 | `#757575` | 미착수 | +| 상태·분석 진행 | 청색 | `#1976D2` | 선택됨 | +| 상태·업무 진행중 | 파랑 | `#1565C0` | 진행 중 | +| 상태·진행 완료 | 연두 | `#689F38` | 완료 | +| 유형·오류수정 | 빨강 | `#D32F2F` | — | +| 유형·불합리 | 주황 | `#F57C00` | — | +| 유형·미팅 | 청록 | `#00796B` | 1.분석/협의 | +| 유형·설계 | 연두 | `#689F38` | 2.설계 | +| 유형·개발 | 파랑 | `#1976D2` | — | +| 유형·제작 | 남파랑 | `#283593` | 3.제작 | +| 유형·입고셋업 | 보라 | `#7B1FA2` | 4.셋업 | +| 유형·적용 및 모니터링 | 하늘 | `#0288D1` | 5.양산 | +| 유형·요청대응 | 남색 | `#1A237E` | 6.CS | + +> xlsx 색상과 동일 계열: 배너=`FF1F4E78`(진네이비), 헤더=`FF2E75B6`(파랑), 데이터 홀수=`FFDDEBF7`(연파랑). + +--- + +## 1. 문서 배너 (모든 산출물 MD 1번째 줄) + +```html +
+문서 제목 — 설비명 +
+
+근거: 소스 파일 경로  ·  플랫폼 정보 +
+``` + +--- + +## 2. 상태 뱃지 (값에 맞춰 색 코드 교체) + +```html +해야 할 일 +분석 진행 +업무 진행중 +진행 완료 +``` + +--- + +## 3. 유형 뱃지 + +```html +미팅 +설계 +개발 +제작 +입고셋업 +적용 및 모니터링 +요청대응 +오류수정 +불합리 +``` + +--- + +## 4. 진행률 바 + +```html + **0%** +``` + +--- + +## 5. 섹션 헤더 강조 (산출물 MD 내 시트 구분) + +```markdown +## 시트① 제목 +``` + +--- + +## 6. 미확정 경고 (GitHub Alerts) + +```markdown +> [!NOTE] +> 아래 값은 소스 분석 기반 `(제안)`. 현장 확인 후 확정. +``` + +--- + +## 7. _task.md 완성 템플릿 (디자인 포함) + +단계명·프로젝트명·코드·유형·상태·진행률 토큰 교체 후 사용. + +```markdown +
+{N}.{단계명} — {프로젝트명} +
+
+{프로젝트코드}-{일련번호} +
+ +| 필드 | 값 | +|---|---| +| 유형 | {유형명} | +| 상태 | 해야 할 일 | +| 담당자 / 참조자 | {담당자} / {참조자} | +| 진행률 | **0%** | +| Hashtag | {태그1} / {태그2} | +``` + +--- + +## 적용 규칙 +- **항상 배너 먼저**: 산출물 MD 생성 시 본문 앞에 배너 블록을 삽입한다. +- **뱃지 > 텍스트**: 상태·유형은 텍스트가 아닌 뱃지 HTML로 표기한다. +- **진행률 bar 병기**: `` 태그 + bold 숫자를 함께 쓴다(텍스트 렌더러 폴백). +- **섹션 헤더**: 산출물 내 시트 구분 `##` 제목은 `` 적용. +- **README·_meta enum**: 동일 색상 체계를 그대로 적용한다. diff --git a/.claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py b/.claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py new file mode 100644 index 0000000..e951477 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py @@ -0,0 +1,479 @@ +# -*- coding: utf-8 -*- +""" +build_02설계_xlsx.py — PtoP Welder(가접용접기) 02.설계 산출물 재생산 +──────────────────────────────────────────────────────────────────── +LG PRI 표준 양식(ref 파일 불필요 · 완전 독립 실행). +스타일은 xlsxfill.py 에 하드코딩된 상수를 사용. + +전제조건: + - Python 3.9+ + - openpyxl >= 3.1 (pip install openpyxl) + +실행: + cd # E:\\Work\\GIT\\LGES-OC5-PtoP-Welder + set PYTHONUTF8=1 + py .claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py + +출력: + docs/[Ubisam산출물]/02.설계/IO_DB설계/PLC_PC_인터페이스_신호정의서.xlsx + docs/[Ubisam산출물]/02.설계/IO_DB설계/모션축_IO맵_정의서.xlsx + docs/[Ubisam산출물]/02.설계/UIUX_기능설계/기능명세서.xlsx + docs/[Ubisam산출물]/02.설계/시퀀스_인터록/TactTime_산정서.xlsx +""" +import sys, os, datetime +sys.path.insert(0, os.path.dirname(__file__)) +from xlsxfill import ( + make_workbook, set_col_widths, set_row_height, + write_banner, write_header_row, write_data_rows, group_merge, + BANNER_FONT, BANNER_FILL, BANNER_BORDER, BANNER_ALIGN, + SUBBANNER_FONT, SUBBANNER_BORDER, SUBBANNER_ALIGN, + HEADER_FONT, HEADER_FILL, HEADER_BORDER_FIRST, HEADER_BORDER_MID, HEADER_BORDER_LAST, + DATA_FONT, DATA_FILL_ODD, DATA_FILL_EVEN, + DATA_BORDER_FIRST, DATA_BORDER_MID, DATA_BORDER_LAST, + DATA_ALIGN_L, DATA_ALIGN_C, + FUNC_BANNER_FONT, FUNC_HEADER_FONT, FUNC_HEADER_BORDER, + FUNC_DATA_FONT, FUNC_DATA_BORDER, FUNC_DATA_ALIGN, + TACT_FONT, TACT_BORDER, TACT_ALIGN, TACT_ALIGN_L, + _apply, _border, +) +from openpyxl.styles import Font, PatternFill, Alignment +from openpyxl.utils import get_column_letter as GL + +OUT_DIR = "docs/[Ubisam산출물]/02.설계/" +TODAY = datetime.date.today() # 실행일 자동 반영 + + +# ══════════════════════════════════════════════════════ +# 1. PLC-PC 인터페이스 신호 정의서 +# 탭: 통신 사양 | PC→PLC (Write) | PLC→PC (Read) | 핸드셰이크 시퀀스 +# ══════════════════════════════════════════════════════ +def build_plcpc(): + wb = make_workbook() + OUT = OUT_DIR + "IO_DB설계/PLC_PC_인터페이스_신호정의서.xlsx" + + # ── 통신 사양 ────────────────────────────────── + ws = wb.create_sheet("통신 사양") + set_col_widths(ws, {"A":"3.7","B":24,"C":106}) + write_banner(ws, 2, 2, 3, + "PtoP Welder(가접용접기) ↔ PLC 인터페이스 통신 사양", + "PtoP Welder(가접용접기) — MX Component / 단일 게이트(PAGE_REQ) 핸드셰이크 · 디바이스 주소=제안(M70000/M70500 베이스)") + write_header_row(ws, 4, 2, ["항목", "내용"]) + comm = [ + ["PLC 기종", "Mitsubishi Melsec"], + ["통신 방식", "MX Component (ePLCNetType.MXComponent)"], + ["맵 파일", "CSV (PLCMMapFileType.CSV)"], + ["데이터 갱신", "폴링(10ms) + 이벤트 비트(REQ/ACK)"], + ["디바이스 영역","비트=M, 워드=D (PC Write M70000~/D70000~, PLC Read M70500~/D70500~)"], + ["핸드셰이크", "REQ→처리→ACK/COMPLETE. 단일 트리거 PLC_PAGE_CHANGE_REQ"], + ["좌표·실수", "워드 INT(short). Offset/변위 INT. 스케일 ×1(필요시 ×1000)"], + ["Watchdog", "PLC_LIVE_STATUS(0x00) ↔ PC_LIVE_STATUS(0x00)"], + ["인터락", "Door(0x06)/EMO(0x07)/Heavy(0x05)→정지, HeadTheta(D70510) 발진 인터락"], + ["주의", "주소=제안. 2nd 블록(0x65~)은 양극(P) 예비, 현재 음극만 사용"], + ] + write_data_rows(ws, 5, 2, comm, center_cols={0}) + + # ── PC→PLC (Write) ───────────────────────────── + ws = wb.create_sheet("PC→PLC (Write)") + set_col_widths(ws, {"A":3.7,"B":8,"C":14,"D":28,"E":10,"F":14,"G":8,"H":36,"I":28,"J":22}) + write_banner(ws, 2, 2, 10, + "PC → PLC 신호 (PC Write)", + "PC(Aurora)가 PLC로 보내는 요청·결과·보정값") + HDR = ["No","Seq/구분","신호명","방향","디바이스(제안)","Type","의미 / 트리거","응답·완료","비고"] + write_header_row(ws, 4, 2, HDR) + write_rows = [ + ["0x00","Status","PC_LIVE_STATUS","PC→PLC","M70000","BIT","PC Alive","","Watchdog"], + ["0x01","Status","PC_AUTO_STATUS","PC→PLC","M70001","BIT","Auto 상태","","Enter시 true"], + ["0x02","Status","PC_AUTO_COMPLETE","PC→PLC","M70002","BIT","Auto 완료","",""], + ["0x03","Status","PC_MANUAL_COMPLETE","PC→PLC","M70003","BIT","Manual 완료","",""], + ["0x04","Alarm","PC_LIGHT_ALARM_STATUS","PC→PLC","M70004","BIT","경알람","",""], + ["0x05","Alarm","PC_HEAVY_ALARM_STATUS","PC→PLC","M70005","BIT","중알람","",""], + ["0x06","Status","PC_DOOR_INTERLOCK","PC→PLC","M70006","BIT","Door 인터락","",""], + ["0x07","Status","PC_EMO_STATUS","PC→PLC","M70007","BIT","EMO","",""], + ["0x08","Process","PC_PAGE_CHANGE_COMPLETE","PC→PLC","M70008","BIT","페이지 확정 ACK","","PageNo 일치시 ON"], + ["0x09","Process","PC_RECIPE_CHANGE_COMPLETE","PC→PLC","M70009","BIT","레시피 변경 완료(미감시)","","260618"], + ["0x0F","Process","PC_WELD_READY_ACK","PC→PLC","M70015","BIT","Resume ACK/run","","bit15,260621"], + ["0x10","Process","PC_WELD_TRIGGER_ACK","PC→PLC","M70016","BIT","용접 트리거 ACK","",""], + ["0x11","Status","PC_WELD_BUSY","PC→PLC","M70017","BIT","Laser On 표시","","260626"], + ["0x12","Process","PC_WELD_COMPLETE","PC→PLC","M70018","BIT","용접 완료","",""], + ["0x14","Process","PC_WELD_NG_JUDGE","PC→PLC","M70020","BIT","용접 NG 판정","",""], + ["0x25","Request","PC_RMS_PARAMETER_READ_REQUEST_ACK","PC→PLC","M70037","BIT","RMS read ACK","",""], + ["D0x00","Info","PC_ERROR_CODE","PC→PLC","D70000","INT","에러코드 에코","",""], + ["D0x01","Info","PC_PAGE_NO","PC→PLC","D70001","INT","페이지 에코","",""], + ["D0x02","Info","PC_RECIPE_NO","PC→PLC","D70002","INT","레시피 에코","","260618"], + ["D0x08","Process","PC_WELD_OFFSET_X","PC→PLC","D70008","INT","용접 보정 X","",""], + ["D0x0A","Process","PC_WELD_OFFSET_Y","PC→PLC","D70010","INT","용접 보정 Y","",""], + ["D0x0C","Process","PC_DISPLACEMENT1_MEASUREMENT","PC→PLC","D70012","INT","변위 측정1","",""], + ["D0x0E","Process","PC_DISPLACEMENT2_MEASUREMENT","PC→PLC","D70014","INT","변위 측정2","",""], + ["D0x14~","Info","PC_CELL_ID_01~10","PC→PLC","D70020~","INT(ASCII)","Cell ID 에코","",""], + ["D0x1E~","Info","PC_LOT_ID_01~08","PC→PLC","D70030~","INT(ASCII)","Lot ID 에코","",""], + ["D0x28~","Info","PC_FDC_PAGE1~3_POWER/VELOCITY","PC→PLC","D70040~","INT","FDC 파워/속도","",""], + ["D0x3C~","Info","PC_RMS_ALLOC_START~END","PC→PLC","D70060~97","INT","RMS 할당","",""], + ] + write_data_rows(ws, 5, 2, write_rows, center_cols={0,3,5}) + n = len(write_rows) + group_merge(ws, 3, 5, n, [r[1] for r in write_rows]) + ws.merge_cells(f"E5:E{4+n}") + + # ── PLC→PC (Read) ────────────────────────────── + ws = wb.create_sheet("PLC→PC (Read)") + set_col_widths(ws, {"A":3.7,"B":8,"C":14,"D":28,"E":10,"F":14,"G":8,"H":36,"I":28,"J":22}) + write_banner(ws, 2, 2, 10, + "PLC → PC 신호 (PC Read)", + "PLC가 PC로 보내는 요청·상태·완료") + write_header_row(ws, 4, 2, HDR) + read_rows = [ + ["0x00","Status","PLC_LIVE_STATUS","PLC→PC","M70500","BIT","PLC Alive","Watchdog",""], + ["0x01","Status","PLC_AUTO_STATUS","PLC→PC","M70501","BIT","Auto 상태","",""], + ["0x02","Request","PLC_AUTO_REQ","PLC→PC","M70502","BIT","Auto 요청","PC_AUTO_STATUS",""], + ["0x03","Request","PLC_MANUAL_REQ","PLC→PC","M70503","BIT","Manual 요청","",""], + ["0x04","Alarm","PLC_LIGHT_ALARM_STATUS","PLC→PC","M70504","BIT","경알람","",""], + ["0x05","Alarm","PLC_HEAVY_ALARM_STATUS","PLC→PC","M70505","BIT","중알람","시퀀스 정지",""], + ["0x06","Status","PLC_DOOR_INTERLOCK","PLC→PC","M70506","BIT","Door","인터락",""], + ["0x07","Status","PLC_EMO_STATUS","PLC→PC","M70507","BIT","EMO","인터락",""], + ["0x08","Request","PLC_PAGE_CHANGE_REQ","PLC→PC","M70508","BIT","단일 게이트 트리거","Page+Recipe 확정",""], + ["0x09","Request","PLC_SCOUT_ENABLE","PLC→PC","M70509","BIT","Scout Enable","","260622"], + ["0x0F","Request","PLC_WELD_READY_REQ","PLC→PC","M70515","BIT","Weld_01 Ready→Resume","","bit15,260621"], + ["0x10","Request","PLC_WELD_TRIGGER_REQ","PLC→PC","M70516","BIT","용접 발진 요청","READY_ACK/BUSY",""], + ["0x11","Status","PLC_WELD_WAIT","PLC→PC","M70517","BIT","용접 대기","",""], + ["0x12","Process","PLC_WELD_COMPLETE_ACK","PLC→PC","M70518","BIT","용접 완료 ACK","",""], + ["0x1E","Alarm","PLC_ALARM_RESET","PLC→PC","M70530","BIT","알람 리셋","",""], + ["0x25","Request","PLC_RMS_PARAMETER_READ_REQUEST","PLC→PC","M70537","BIT","RMS read 요청","ACK",""], + ["D0x00","Info","PLC_ERROR_CODE","PLC→PC","D70500","INT","에러코드","",""], + ["D0x01","Info","PLC_PAGE_NO","PLC→PC","D70501","INT","페이지(1-base)","3회 검증",""], + ["D0x02","Info","PLC_RECIPE_NO","PLC→PC","D70502","INT","레시피(11/21/22/31/32)","","260618"], + ["D0x03","Info","PLC_CURRENT_SLOT_NO","PLC→PC","D70503","INT","현재 슬롯","EL/CLEAN",""], + ["D0x0A","Process","HEAD_THETA_POSITION","PLC→PC","D70510","INT","Laser1 Head Theta","발진 인터락",""], + ["D0x14~","Info","PLC_CELL_ID_01~10","PLC→PC","D70520~","INT(ASCII)","Cell ID","",""], + ["D0x1E~","Info","PLC_LOT_ID_01~08","PLC→PC","D70530~","INT(ASCII)","Lot ID","",""], + ] + write_data_rows(ws, 5, 2, read_rows, center_cols={0,3,5}) + n = len(read_rows) + group_merge(ws, 3, 5, n, [r[1] for r in read_rows]) + ws.merge_cells(f"E5:E{4+n}") + + # ── 핸드셰이크 시퀀스 ───────────────────────── + ws = wb.create_sheet("핸드셰이크 시퀀스") + set_col_widths(ws, {"A":3.7,"B":6,"C":16,"D":6,"E":8,"F":48,"G":36,"H":40}) + write_banner(ws, 2, 2, 8, + "PLC ↔ PC 핸드셰이크 시퀀스 (Seq별)", + "요청 → 처리 → 완료/응답 흐름 │ PtoP Welder 단일 게이트(PLC_PAGE_CHANGE_REQ) 기준") + write_header_row(ws, 4, 2, ["No","Seq","Step","주체","동작 / 신호","디바이스(제안)","비고"]) + hs = [ + [1,"PageGate",1,"PLC","Page Change 요청(단일 게이트)","PLC_PAGE_CHANGE_REQ(M70508) Set","Page+Recipe 확정 트리거"], + [2,"PageGate",2,"PC","Recipe 확정/전환","PLC_RECIPE_NO(D70502)→ActivateRecipe→PC_RECIPE_NO(D70002) 에코","0/동일이면 전환 생략"], + [3,"PageGate",3,"PC","Page 3회 검증·확정","PLC_PAGE_NO(D70501) 3회→PC_PAGE_NO 에코→PC_PAGE_CHANGE_COMPLETE(M70008) Set","200ms×3"], + [4,"PageGate",4,"PC","용접 리스트 업로드","레이어 선택 + SeqUploadWeldingList","-"], + [5,"Weld",1,"PLC","발진 요청","PLC_WELD_TRIGGER_REQ(M70516) Set","PreScout(M70509) 옵션"], + [6,"Weld",2,"PC","발진 ACK · BUSY","PC_WELD_TRIGGER_ACK(M70016) Set → PC_WELD_BUSY(M70017) ON","HeadTheta(D70510) 인터락"], + [7,"Weld",3,"PC","실제 발진(Gate ON/OFF)","Scanner.ExecuteList → Gate ON/OFF","laserEmissionGateDelay 15ms"], + [8,"Weld",4,"PC","완료 통지","PC_WELD_BUSY OFF → PC_WELD_COMPLETE(M70018) Set","PostScout(M70509) 옵션"], + [9,"Weld",5,"양측","완료 ACK · Clear","PLC_WELD_COMPLETE_ACK(M70518) → TRIGGER_ACK OFF","루프백(PLC_PAGE_CHANGE_REQ) 대기"], + [10,"Resume",1,"PLC","Weld_01 Ready → Resume 요청","PLC_WELD_READY_REQ(M70515) Set","bit15, Auto→Manual+Resume(가변용접)"], + [11,"Resume",2,"PC","Resume ACK / run","PC_WELD_READY_ACK(M70015) Set","Resume 시 READY_ACK OFF"], + [12,"Interlock",1,"PLC","Door/EMO/Heavy 상태 공유","PLC_DOOR(M70506)/EMO(M70507)/HEAVY(M70505)","이상 시 시퀀스 정지"], + [13,"Status",1,"양측","Alive Watchdog 토글","PC_LIVE(M70000) ↔ PLC_LIVE(M70500)","단절 시 안전정지"], + [14,"Alarm",1,"PC","PC 알람 통지","PC_LIGHT_ALARM(M70004)/HEAVY_ALARM(M70005) Set","중알람 시 시퀀스 정지"], + ] + write_data_rows(ws, 5, 2, hs, center_cols={0,2,3}) + group_merge(ws, 3, 5, len(hs), [r[1] for r in hs]) + + wb.save(OUT); print("✓ PLC-PC:", OUT) + + +# ══════════════════════════════════════════════════════ +# 2. 모션축·IO 맵 정의서 +# 탭: 통신·제어 구성 | 모션축 정의(ACS) | I-O 맵(Ajin) +# ══════════════════════════════════════════════════════ +def build_motio(): + wb = make_workbook() + OUT = OUT_DIR + "IO_DB설계/모션축_IO맵_정의서.xlsx" + + # ── 통신·제어 구성 ───────────────────────────── + ws = wb.create_sheet("통신·제어 구성") + set_col_widths(ws, {"A":4.4,"B":16,"C":24.8,"D":26,"E":20,"F":22,"G":34}) + write_banner(ws, 2, 2, 7, + "PtoP Welder(가접용접기) 통신 · 제어 구성 (PC / Aurora)", + "근거: EquipmentSetup.RegisterDevices() / PAIX_PTOP.cs / architecture.md §4 │ 주소·수량=제안") + write_header_row(ws, 4, 2, ["구분","대상","컨트롤러 / 디바이스","통신 / 버스","수량·범위","비고"]) + write_data_rows(ws, 5, 2, [ + ["PLC통신","외부 신호 제어","Mitsubishi Melsec","MX Component","1","ePLCNetType.MXComponent"], + ["I/O","디지털 IO","Ajin(AJINEXTEK)","필드버스","DI/DO","PAIX_PTOP:IODevice"], + ["Laser","Core/Ring 레이저","IO Gate(TruPulse nano)","DO 게이트","2","ExecuteWelding Gate"], + ["Scanner","레이저 스캐너","RTC LaserScanner","RTC","1","Scanner.ExecuteList"], + ["모션(Servo)","X/Y 축","ACS SPiiPlus","EtherCAT","2축 (제안)","AuroraACS"], + ["모션(Servo)","Z 축","Ajin Motion Board","보드","1축 (제안)","AuroraMotionBoard"], + ["Vision","카메라/측정","MIL 기반","-","-","AuroraVision(현재 미사용)"], + ["상/하위(MES)","호스트 연동","Stream(LBAD/RPC)","TCP","옵션","UseStream 시"], + ], center_cols={0,4}) + + # ── 모션축 정의(ACS) ──────────────────────────── + ws = wb.create_sheet("모션축 정의(ACS)") + set_col_widths(ws, {"A":4.4,"B":7,"C":12,"D":18,"E":22,"F":8,"G":12,"H":10,"I":10,"J":10,"K":10,"L":20}) + write_banner(ws, 2, 2, 12, + "PtoP Welder(가접용접기) 모션축 구성표 (ACS · Ajin) — 제안", + "각 축 Init / 단축·동기축 / Jog·Relative·Absolute │ 행정·속도·Home은 모듈 소스 미보유 → (제안)") + write_header_row(ws, 4, 2, + ["Axis","축 명(논리)","용도","컨트롤러/통신","구동","행정/범위","최대속도","Home 방식","Soft Limit","동기/단축","비고"]) + write_data_rows(ws, 5, 2, [ + ["0","X","셀 위치 X","ACS SPiiPlus/EtherCAT","Servo","(제안)","(제안)","(제안)","(제안)","단축",""], + ["1","Y","셀 위치 Y","ACS SPiiPlus/EtherCAT","Servo","(제안)","(제안)","(제안)","(제안)","단축",""], + ["2","Z","포커스/높이","Ajin Motion Board","Servo","(제안)","(제안)","(제안)","(제안)","단축",""], + ["-","Scanner θ/XY","빔 편향(갈바노)","RTC LaserScanner","Galvano","List Jump/Mark","-","-","보정테이블","-",""], + ], center_cols={0,1,4,5,6,7,8,9}) + ws.cell(12, 3).value = "※ ACS = PC가 EtherCAT으로 직접 제어하는 Servo 축(X/Y). Z는 Ajin Motion Board, Scanner θ/XY는 RTC 갈바노. 행정/속도/Home 미확정." + ws.cell(12, 3).font = Font(name="맑은 고딕", size=9, italic=True) + + # ── I-O 맵 (Ajin) ─────────────────────────────── + ws = wb.create_sheet("I-O 맵(Ajin)") + set_col_widths(ws, {"A":4.4,"B":6,"C":30,"D":6,"E":10,"F":10,"G":8,"H":36,"I":20}) + write_banner(ws, 2, 2, 9, + "PtoP Welder(가접용접기) I/O 맵 (Ajin DI/DO) — 제안", + "근거: PAIX_PTOP.cs ePrismaticDIAddress / ePrismaticDOAddress · DI 9~23 / DO 9~17") + write_header_row(ws, 4, 2, ["No","신호명","Type","권장 모듈","슬롯/CH","Active","설명/인터락","비고"]) + raw_io = [ + (9,"CoreLaser_Laser_Ready","DI","Ajin DI","High","Core 레이저 Ready"), + (10,"CoreLaser_Error_State","DI","Ajin DI","High","Core 레이저 에러"), + (11,"CoreLaser_Laser_Output_State","DI","Ajin DI","High","Core 발진 출력 상태"), + (12,"CoreLaser_Control_Status","DI","Ajin DI","High","Core 제어 상태"), + (13,"RingLaser_Laser_Ready","DI","Ajin DI","High","Ring 레이저 Ready"), + (14,"RingLaser_Error_State","DI","Ajin DI","High","Ring 레이저 에러"), + (15,"RingLaser_Laser_Output_State","DI","Ajin DI","High","Ring 발진 출력 상태"), + (23,"RingLaser_Control_Status","DI","Ajin DI","High","Ring 제어 상태"), + (9,"CoreLaser_Control","DO","Ajin DO","High","Core 레이저 게이트(발진)"), + (10,"CoreLaser_ErrorClearing","DO","Ajin DO","High","Core 에러 클리어"), + (13,"RingLaser_Control","DO","Ajin DO","High","Ring 레이저 게이트(발진)"), + (14,"RingLaser_ErrorClearing","DO","Ajin DO","High","Ring 에러 클리어"), + (17,"EX_Red_Control","DO","Ajin DO","High","EX Red 표시등 제어"), + ] + io_rows = [[i+1, name, typ, mod, f"{typ} Ch{ch}", act, desc, ""] + for i,(ch,name,typ,mod,act,desc) in enumerate(raw_io)] + write_data_rows(ws, 5, 2, io_rows, center_cols={0,2,3,4,5}) + + wb.save(OUT); print("✓ 모션축·IO:", OUT) + + +# ══════════════════════════════════════════════════════ +# 3. 기능명세서 +# 탭: OverView | MainView | RecipeView | ManualView | +# DeviceView | SettingView | VisionView | Sequence 흐름 +# ══════════════════════════════════════════════════════ +def build_funcspec(): + wb = make_workbook() + OUT = OUT_DIR + "UIUX_기능설계/기능명세서.xlsx" + + # FUNC 계열은 배경 없이 thin border만 사용 + tb = lambda: FUNC_DATA_BORDER + + # ── OverView ──────────────────────────────────── + ws = wb.create_sheet("OverView") + set_col_widths(ws, {"A":4.1,"B":7.8,"C":66,"D":18.2,"E":19.3}) + set_row_height(ws, 2, 43.5) + c = ws.cell(2, 2, "PtoP Welder(가접용접기) SW 기능명세서") + _apply(c, FUNC_BANNER_FONT, border=_border("medium","medium","medium","medium"), + alignment=Alignment(horizontal="center",vertical="center",wrap_text=True)) + ws.merge_cells("B2:E2") + # FUNC 헤더: 배경 없음(None), thin border + write_header_row(ws, 4, 2, ["no.","Content"], font=FUNC_HEADER_FONT, fill=None) + ov_items = [ + "운영 SW는 LG PRI의 Aurora 플랫폼으로 개발", + "운영 환경 : Windows 11, .NET Framework", + "개발 환경 : Windows 11, Visual Studio", + "설비 : PtoP Welder(가접용접기) — 셀 P2P 레이저 가접(Tack) 용접", + "모션 : ACS SPiiPlus(X/Y, EtherCAT) + Ajin(Z), 빔 편향 = RTC 갈바노 스캐너", + "레이저 : Core / Ring 2계통, IO Gate(TruPulse nano) 발진", + "PLC : Mitsubishi MELSEC / MX Component, 단일 게이트(PAGE_REQ) 핸드셰이크", + "비전 : MIL 기반(현재 AuroraVision 미사용)", + ] + for i, txt in enumerate(ov_items): + r = 5 + i + ws.cell(r, 2, i+1).font = FUNC_DATA_FONT + ws.cell(r, 2).border = tb() + ws.cell(r, 2).alignment = Alignment(horizontal="center", vertical="center") + ws.cell(r, 3, txt).font = FUNC_DATA_FONT + ws.cell(r, 3).border = tb() + ws.cell(r, 3).alignment = Alignment(horizontal="left", vertical="center", wrap_text=True) + ws.merge_cells(f"C{r}:E{r}") + + # ── View 시트 공통 헬퍼 ────────────────────────── + # 헤더: Update|Funtion Area|Funtion Name|NO|Sub|Content + VIEW_HDR = ["Update","Funtion Area","Funtion Name","NO","Sub","Content"] + def make_view(name, banner_text, rows): + ws = wb.create_sheet(name) + set_col_widths(ws, {"A":4,"B":14,"C":16,"D":24,"E":6,"F":14,"G":48}) + set_row_height(ws, 3, 28) + ws.cell(3, 7, banner_text).font = FUNC_DATA_FONT + ws.cell(3, 7).alignment = Alignment(horizontal="left", vertical="center") + set_row_height(ws, 5, 20) + for j, h in enumerate(VIEW_HDR): + c = ws.cell(5, 2+j, h) + c.font = FUNC_HEADER_FONT + c.border = tb() + c.alignment = Alignment(horizontal="center", vertical="center") + for i, row in enumerate(rows): + r = 6 + i + vals = [TODAY] + row + for j, v in enumerate(vals): + c = ws.cell(r, 2+j, v) + c.font = FUNC_DATA_FONT + c.border = tb() + c.alignment = Alignment( + horizontal="center" if j in {0,3} else "left", + vertical="center", wrap_text=True) + + make_view("MainView","다음은 Main 화면에서 보여야 하는 기능이다.",[ + ["운전","StartSequence/Stop", 1,"SeqAutoRunPTOP", "자동운전 시작/정지"], + ["상태","UpdatePageNum", 2,"EVENT_AUTO_RUN_UPDATE_PAGENUM","현재 페이지 표시"], + ["상태","WorkingPosition", 3,"eWorkingPosition", "용접 진행 위치 표시"], + ["운전","Auto→Manual+Resume", 4,"bit15(PLC_WELD_READY_REQ)", "Resume(가변용접) 기능"], + ["표시","ScannerCanvasCtrlTeaching", 5,"레이어/오브젝트", "스캐너 캔버스"], + ]) + make_view("RecipeView","다음은 Recipe 화면에서 보여야 하는 기능이다.",[ + ["레시피","ActivateRecipe", 1,"RecipeManager", "레시피 선택/활성"], + ["레시피","RecipeChangeHistory", 2,"RchHandler", "변경 이력"], + ["모델", "UCModel", 3,"EquipmentSpecificModel_PTOP","모델 편집"], + ]) + make_view("ManualView","다음은 Manual 화면에서 보여야 하는 기능이다.",[ + ["Manual", "ScannerOperation", 1,"ScannerOperationView","스캐너 수동 조작(발진/Jump/Mark)"], + ["Manual", "ModelFinder", 2,"ModelFinderView", "모델 파인더"], + ["Calibration","ScannerCalibration",3,"(제안)", "스캐너 보정"], + ["Calibration","PixelResolution", 4,"(제안)", "픽셀 해상도"], + ]) + make_view("DeviceView","다음은 Device 화면에서 보여야 하는 기능이다.",[ + ["Device", "DeviceSettingView", 1,"LEVEL3", "디바이스 설정(활성/비활성)"], + ["Monitoring","PLCBitMonitoring", 2,"ePLCDevice.M","PLC 비트 모니터"], + ["Monitoring","PLCWordMonitoring", 3,"ePLCDevice.D","PLC 워드 모니터"], + ["Monitoring","IOInterfaceView", 4,"IODevice", "IO 인터페이스"], + ]) + make_view("SettingView","다음은 Setting 화면에서 보여야 하는 기능이다.",[ + ["Setting","Equipment", 1,"LEVEL1", "장비 설정"], + ["Setting","Log", 2,"-", "로그 설정"], + ["Setting","AutoBackup", 3,"-", "자동 백업"], + ["Alarm", "History/Desc", 4,"-", "알람 이력·설명"], + ["Stream", "ServiceSettingView",5,"UseStream","Stream(상/하위) 사용 시"], + ]) + make_view("VisionView","다음은 Vision 화면에서 보여야 하는 기능이다.",[ + ["Vision","(해당없음)",1,"AuroraVision 미사용","MIL 기반 — 현재 미사용(제안)"], + ]) + + # ── Sequence 흐름: Update|Funtion Area|Processing Name|NO|Content + ws = wb.create_sheet("Sequence 흐름") + set_col_widths(ws, {"A":4,"B":14,"C":16,"D":24,"E":6,"F":60}) + SEQ_HDR = ["Update","Funtion Area","Processing Name","NO","Content"] + set_row_height(ws, 5, 20) + for j,h in enumerate(SEQ_HDR): + c = ws.cell(5, 2+j, h) + c.font = FUNC_HEADER_FONT; c.border = tb() + c.alignment = Alignment(horizontal="center",vertical="center") + seq_data = [ + ["AutoRun","PAGE_REQ Gate", 1,"PLC_PAGE_CHANGE_REQ(M70508) 단일 게이트 트리거"], + ["AutoRun","Recipe 확정", 2,"PLC_RECIPE_NO→ActivateRecipe→PC_RECIPE_NO 에코(0/동일 생략)"], + ["AutoRun","Page 확정", 3,"PLC_PAGE_NO 3회 검증→PC_PAGE_CHANGE_COMPLETE"], + ["AutoRun","List Upload", 4,"레이어 선택 + SeqUploadWeldingList"], + ["AutoRun","발진 요청", 5,"PLC_WELD_TRIGGER_REQ→PC_WELD_TRIGGER_ACK/BUSY(HeadTheta 인터락)"], + ["AutoRun","발진", 6,"Gate ON(15ms)→Scanner.ExecuteList→Gate OFF"], + ["AutoRun","완료", 7,"PC_WELD_COMPLETE→PLC_WELD_COMPLETE_ACK"], + ["AutoRun","루프백", 8,"CheckCycleStop→PAGE_REQ 대기(Resume 시 READY_ACK OFF)"], + ] + for i, row in enumerate(seq_data): + r = 6 + i + vals = [TODAY] + row + for j,v in enumerate(vals): + c = ws.cell(r, 2+j, v) + c.font = FUNC_DATA_FONT; c.border = tb() + c.alignment = Alignment(horizontal="center" if j in {0,3} else "left", + vertical="center", wrap_text=True) + + wb.save(OUT); print("✓ 기능명세서:", OUT) + + +# ══════════════════════════════════════════════════════ +# 4. TactTime 산정서 +# 탭: PLC TactTime | PC TactTime +# ══════════════════════════════════════════════════════ +def build_tact(): + wb = make_workbook() + OUT = OUT_DIR + "시퀀스_인터록/TactTime_산정서.xlsx" + + def tact_cell(ws, r, c, v, align="center"): + cell = ws.cell(r, c, v) + cell.font = TACT_FONT; cell.border = TACT_BORDER + cell.alignment = Alignment(horizontal=align, vertical="center", wrap_text=True) + return cell + + # ── PLC TactTime (미입수) ────────────────────── + ws = wb.create_sheet("PLC TactTime") + set_col_widths(ws, {"B":18,"C":19.5,"D":49.6,"E":10,"F":10.8}) + set_row_height(ws, 3, 53) + tact_cell(ws, 3, 2, "PLC Main 동작 (PtoP Welder) — 미입수") + ws.merge_cells("B3:E3") + write_header_row(ws, 4, 2, ["공정","Unit","단위동작","T/T"], font=TACT_FONT, fill=None) + set_row_height(ws, 4, 22) + set_row_height(ws, 5, 40) + tact_cell(ws, 5, 2, "(미입수)", "center") + tact_cell(ws, 5, 3, "PLC 시퀀스 사양 입수 후 작성", "left") + tact_cell(ws, 5, 4, "셀 로딩 / 언로딩 / 페이지 인덱싱 T/T — PLC 측 단위동작 미보유(제안)", "left") + tact_cell(ws, 5, 5, "미입수", "center") + + # ── PC TactTime ──────────────────────────────── + ws = wb.create_sheet("PC TactTime") + set_col_widths(ws, {"A":11.7,"B":18,"C":38.1,"D":75.8,"E":8,"F":10.9,"G":12.2,"H":42,"I":12,"J":13}) + set_row_height(ws, 3, 53) + tact_cell(ws, 3, 2, "Main 동작 (PtoP Welder · 자동, 값=제안 / 출처: SeqAutoRunPTOP.cs)", "left") + ws.merge_cells("B3:E3") + tact_cell(ws, 3, 8, "구간 합계") + ws.merge_cells("H3:J3") + # 헤더 행 높이 + set_row_height(ws, 4, 22) + for col, h in zip([2,3,4,5], ["공정","Unit","단위동작","T/T"]): + tact_cell(ws, 4, col, h) + for col, h in zip([8,9,10], ["Working Process","상세합계","기준/참조합계"]): + tact_cell(ws, 4, col, h) + + L = [ + ("Page/Recipe 게이트","핸드셰이크","Page Change 요청 대기","가변"), + ("Page/Recipe 게이트","핸드셰이크","Recipe 확정/전환","~0~수백"), + ("Page/Recipe 게이트","핸드셰이크","Page 3회 검증 (200ms×3)","~600"), + ("Page/Recipe 게이트","핸드셰이크","용접리스트 업로드","가변"), + ("발진 준비","WeldingStart","발진 요청 대기","가변"), + ("발진 준비","WeldingStart","준비 응답 WaitWeldingStart(200+5)","~205"), + ("발진","ExecuteWelding","Gate ON delay(laserEmissionGateDelayMs)","15"), + ("발진","ExecuteWelding","실제 발진 Scanner.ExecuteList","~285"), + ("발진","ExecuteWelding","Gate OFF + BUSY OFF","~50"), + ("발진","ExecuteWelding","Jump+Correction 복귀 Thread.Sleep(500)","~500"), + ("완료","WeldComplete","완료비트 송신 WriteWeldComplete(50+5)","~55"), + ("옵션","Scout","Pre/Post Scout (M70509 ON 시)","+100/회"), + ] + R = [ + ("Page/Recipe 게이트","가변(폴링 10ms, timeout 60s)","-"), + ("발진 준비","~205",""), + ("발진(1발 기준)","~850 (15+285+50+500)",""), + ("완료","~55",""), + ("Scout(옵션)","+100/회",""), + ("1 사이클(가변 제외)","≈1110+","제안"), + ] + for i, (grp, unit, action, tt) in enumerate(L): + r = 5 + i + set_row_height(ws, r, 30) + tact_cell(ws, r, 2, grp, "left") + tact_cell(ws, r, 3, unit, "left") + tact_cell(ws, r, 4, action, "left") + tact_cell(ws, r, 5, tt, "center") + for i, (proc, detail, ref_) in enumerate(R): + r = 5 + i + tact_cell(ws, r, 8, proc, "left") + tact_cell(ws, r, 9, detail, "center") + tact_cell(ws, r, 10, ref_, "center") + # 공정 그룹 병합 + group_merge(ws, 2, 5, len(L), [r[0] for r in L]) + group_merge(ws, 3, 5, len(L), [r[0]+"|"+r[1] for r in L]) + + wb.save(OUT); print("✓ TactTime:", OUT) + + +# ══════════════════════════════════════════════════════ +if __name__ == "__main__": + build_plcpc() + build_motio() + build_funcspec() + build_tact() + print("\n✅ 02.설계 xlsx 4종 재생산 완료 (ref 파일 불필요).") diff --git a/.claude/skills/ubisam-deliverables/references/xlsxfill.py b/.claude/skills/ubisam-deliverables/references/xlsxfill.py new file mode 100644 index 0000000..1c0b213 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/references/xlsxfill.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +""" +xlsxfill.py — LG PRI 표준 양식 xlsx 헬퍼 (ref 파일 불필요 · 완전 독립) + +스타일 상수는 ref(공소수정기·FMM) 파일에서 직접 추출한 값으로 하드코딩됨. +모든 파일 구조(배너·헤더·데이터·그룹병합)를 openpyxl만으로 재현한다. + +사용법: + import sys + sys.path.insert(0, os.path.dirname(__file__)) + from xlsxfill import ( + make_workbook, set_col_widths, set_row_height, + write_banner, write_header_row, write_data_rows, group_merge, + BANNER_FONT, BANNER_FILL, HEADER_FONT, HEADER_FILL, + DATA_FONT, DATA_FILL_ODD, DATA_FILL_EVEN, + FUNC_BANNER_FONT, FUNC_HEADER_FONT, FUNC_DATA_FONT, + TACT_FONT, TACT_BORDER, _apply, _border, + ) + +전제조건: Python 3.9+, openpyxl >= 3.1 +실행: set PYTHONUTF8=1 && py build_02설계_xlsx.py +""" +import openpyxl +from openpyxl import Workbook +from openpyxl.styles import Font, PatternFill, Border, Side, Alignment, GradientFill +from openpyxl.utils import get_column_letter +from openpyxl.utils.cell import range_boundaries + +# ───────────────────────────────────────────── +# 스타일 상수 (ref 파일 직접 추출값) +# ───────────────────────────────────────────── +_F = Font +_P = PatternFill +_B = Border +_S = Side +_A = Alignment + +def _border(left=None, right=None, top=None, bottom=None): + def s(v): return _S(border_style=v) if v else _S() + return _B(left=s(left), right=s(right), top=s(top), bottom=s(bottom)) + +# 배너 행 (B2) — 진네이비 배경, 흰 굵은 글자 +BANNER_FONT = _F(name="맑은 고딕", size=13, bold=True, color="FFFFFFFF") +BANNER_FILL = _P("solid", fgColor="FF1F4E78") +BANNER_BORDER = _border("medium","medium","medium","medium") +BANNER_ALIGN = _A(horizontal="center", vertical="center", wrap_text=True) + +# 부제 행 (B3) — 테두리만 +SUBBANNER_FONT = _F(name="맑은 고딕", size=9) +SUBBANNER_BORDER = _border("medium","medium",None,None) +SUBBANNER_ALIGN = _A(horizontal="left", vertical="center", wrap_text=True) + +# 헤더 행 (B4/B5 등) — 파랑 배경, 흰 굵은 글자 +HEADER_FONT = _F(name="맑은 고딕", size=11, bold=True, color="FFFFFFFF") +HEADER_FILL = _P("solid", fgColor="FF2E75B6") +HEADER_BORDER_FIRST = _border("medium","thin","thin","thin") +HEADER_BORDER_MID = _border("thin","thin","thin","thin") +HEADER_BORDER_LAST = _border("thin","medium","thin","thin") +HEADER_ALIGN = _A(horizontal="center", vertical="center", wrap_text=True) + +# 데이터 행 — 교대 배경(연파랑) + 흰 배경 +DATA_FONT = _F(name="맑은 고딕", size=10) +DATA_FILL_ODD = _P("solid", fgColor="FFDDEBF7") # 연파랑 (홀수행) +DATA_FILL_EVEN = _P("solid", fgColor="FFFFFFFF") # 흰색 (짝수행) +DATA_BORDER_FIRST = _border("medium","thin","thin","thin") +DATA_BORDER_MID = _border("thin","thin","thin","thin") +DATA_BORDER_LAST = _border("thin","medium","thin","thin") +DATA_ALIGN_L = _A(horizontal="left", vertical="center", wrap_text=True) +DATA_ALIGN_C = _A(horizontal="center", vertical="center", wrap_text=True) + +# 기능명세서 전용 스타일 (FUNC ref: 맑은 고딕, 14pt 배너) +FUNC_BANNER_FONT = _F(name="맑은 고딕", size=14, bold=True) +FUNC_HEADER_FONT = _F(name="맑은 고딕", size=11, bold=True) +FUNC_HEADER_BORDER = _border("thin","thin","thin","thin") +FUNC_DATA_FONT = _F(name="맑은 고딕", size=11) +FUNC_DATA_BORDER = _border("thin","thin","thin","thin") +FUNC_DATA_ALIGN = _A(horizontal="left", vertical="center", wrap_text=True) + +# TactTime 전용 (단순 thin 테두리, 색상 없음) +TACT_FONT = _F(name="맑은 고딕", size=11) +TACT_BORDER = _border("thin","thin","thin","thin") +TACT_ALIGN = _A(horizontal="center", vertical="center", wrap_text=True) +TACT_ALIGN_L= _A(horizontal="left", vertical="center", wrap_text=True) + + +# ───────────────────────────────────────────── +# 헬퍼 함수 +# ───────────────────────────────────────────── + +_UNSET = object() # write_header_row fill/font 기본값 sentinel + + +def make_workbook(): + wb = Workbook() + wb.remove(wb.active) # 기본 Sheet 제거 + return wb + + +def set_col_widths(ws, widths: dict): + """widths: {'A':4.4, 'B':16.0, ...}""" + for col, w in widths.items(): + ws.column_dimensions[col].width = w + + +def set_row_height(ws, row, height): + ws.row_dimensions[row].height = height + + +def _apply(cell, font=None, fill=None, border=None, alignment=None): + if font: cell.font = font + if fill: cell.fill = fill + if border: cell.border = border + if alignment: cell.alignment = alignment + + +def write_banner(ws, row, col_start, col_end, title, subtitle=None, + banner_h=26, sub_h=24): + """배너 2행(제목+부제)을 쓰고 병합한다.""" + set_row_height(ws, row, banner_h) + # 제목 + c = ws.cell(row, col_start, title) + _apply(c, BANNER_FONT, BANNER_FILL, BANNER_BORDER, BANNER_ALIGN) + L = get_column_letter + if col_end > col_start: + ws.merge_cells(f"{L(col_start)}{row}:{L(col_end)}{row}") + # 우측 셀 border 유지 + ws.cell(row, col_end).border = _border(right="medium", top="medium", bottom="medium") + if subtitle is not None: + set_row_height(ws, row+1, sub_h) + c2 = ws.cell(row+1, col_start, subtitle) + _apply(c2, SUBBANNER_FONT, border=SUBBANNER_BORDER, alignment=SUBBANNER_ALIGN) + if col_end > col_start: + ws.merge_cells(f"{L(col_start)}{row+1}:{L(col_end)}{row+1}") + ws.cell(row+1, col_end).border = _border(right="medium") + + +def write_header_row(ws, row, col_start, headers, height=22, + font=_UNSET, fill=_UNSET): + """헤더 행 작성. 첫/마지막 열 medium 테두리. + fill=None 을 넘기면 배경 없음(흰색). 생략하면 HEADER_FILL(파랑) 적용. + font=None 을 넘기면 폰트 미설정. 생략하면 HEADER_FONT 적용. + """ + set_row_height(ws, row, height) + _font = HEADER_FONT if font is _UNSET else font + _fill = HEADER_FILL if fill is _UNSET else fill + n = len(headers) + for i, h in enumerate(headers): + col = col_start + i + c = ws.cell(row, col, h) + if i == 0: + border = HEADER_BORDER_FIRST + elif i == n-1: + border = HEADER_BORDER_LAST + else: + border = HEADER_BORDER_MID + _apply(c, _font, _fill, border, HEADER_ALIGN) + + +def write_data_rows(ws, data_start, col_start, rows, + center_cols=None, height=None, + font=None, odd_fill=True): + """ + rows: list[list] 각 행의 값. + center_cols: set of 0-based 인덱스 → center align. 나머지 left. + odd_fill: True=홀/짝 교대 배경, False=배경 없음. + """ + n_cols = max((len(r) for r in rows), default=0) + center_cols = center_cols or set() + for i, rowvals in enumerate(rows): + r = data_start + i + if height: + set_row_height(ws, r, height) + is_odd = (i % 2 == 0) + fill = (DATA_FILL_ODD if is_odd else DATA_FILL_EVEN) if odd_fill else None + for j in range(n_cols): + col = col_start + j + c = ws.cell(r, col) + c.value = rowvals[j] if j < len(rowvals) else None + if j == 0: + border = DATA_BORDER_FIRST + elif j == n_cols-1: + border = DATA_BORDER_LAST + else: + border = DATA_BORDER_MID + align = DATA_ALIGN_C if j in center_cols else DATA_ALIGN_L + _apply(c, font or DATA_FONT, fill, border, align) + + +def group_merge(ws, col, data_start, n, key_rows): + """동일 key_rows 값의 연속 행을 col열에서 수직 병합.""" + i = 0 + while i < n: + k = key_rows[i] + if k is None: + i += 1 + continue + j = i + 1 + while j < n and key_rows[j] == k: + j += 1 + if j - i > 1: + L = get_column_letter(col) + ws.merge_cells(f"{L}{data_start+i}:{L}{data_start+j-1}") + i = j diff --git a/.claude/skills/ubisam-deliverables/templates/_task.template.md b/.claude/skills/ubisam-deliverables/templates/_task.template.md new file mode 100644 index 0000000..35c1a14 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/templates/_task.template.md @@ -0,0 +1,32 @@ +
+{N}.{단계명} — {프로젝트명} +
+
+{프로젝트코드}-{일련번호} +
+ +| 필드 | 값 | +|---|---| +| 유형 | {유형명} | +| 상태 | 해야 할 일 | +| 담당자 / 참조자 | {담당자} / {참조자} | +| 진행률 | **0%** | +| Hashtag | {태그1} / {태그2} | + +## 산출물 정의 +> 세부 × 산출물 × 슬롯 표. 단계별 산출물은 04-stage-deliverables 참조. + +| 세부 | 산출물 | 슬롯 | +|---|---|---| +| {세부} | {산출물} | `{슬롯}/` | + +## 첨부 / 템플릿 +- {파일명 또는 양식 템플릿 위치} + + diff --git a/.claude/skills/ubisam-deliverables/templates/project.yaml.template b/.claude/skills/ubisam-deliverables/templates/project.yaml.template new file mode 100644 index 0000000..c732b59 --- /dev/null +++ b/.claude/skills/ubisam-deliverables/templates/project.yaml.template @@ -0,0 +1,31 @@ +# 프로젝트 메타 (템플릿) — {...} 토큰을 실제 값으로 교체 +# 출처: UBISAM 협업관리 › 프로젝트 설정 › 기본정보 / 대상자 + +project_name: "{사업부}_{고객사}_{설비명} 운영SW" +project_kind: "Software" # Software | Hardware +project_code: "{CUST}_{TYPE}_{NN}_{YYYYMMDD}" # 예: LGIT_SI_01_20260601 +board_type: "Kanban" +status: "진행중" + +org: + division: "{사업부}" + team: "{팀}" + +people: + registrant: "{등록자} ({직급})" + pl: "{PL} ({직급})" + +schedule: + contract_date: "{YYYY-MM-DD}" + due_date: "{YYYY-MM-DD}" + +# 권한 3단계: 책임자 → 참여자 → 열람자 +roles: + owners: ["{책임자}"] + participants: ["{참여자1}", "{참여자2}"] + viewers: ["{열람자…}"] + +alarm: + realtime_ticket: true + mail: true + comment: true diff --git a/.claude/skills/ubisam-format-map/SKILL.md b/.claude/skills/ubisam-format-map/SKILL.md new file mode 100644 index 0000000..94407d9 --- /dev/null +++ b/.claude/skills/ubisam-format-map/SKILL.md @@ -0,0 +1,78 @@ +--- +name: ubisam-format-map +description: Ubisam 협업관리 양식의 공통 enum·스키마·단계↔산출물 슬롯 매핑 레퍼런스. 상태(4종)·일감유형(9종)·진행 6단계(분석/협의·설계·제작·셋업·양산·CS)·일감/프로젝트 필드 스키마·티켓 코드 규칙을 제공. Ubisam 양식으로 산출물을 재생산·재배치·검증하거나, 타사 양식 문서를 Ubisam 단계로 매핑할 때 반드시 이 스킬을 먼저 읽을 것. "Ubisam 양식", "협업관리", "일감 6단계", "산출물 슬롯", "상태/유형 enum" 언급 시 사용. +--- + +# Ubisam 협업관리 양식 맵 (Format Map) + +타사 양식 산출물을 Ubisam 협업관리 양식으로 **재생산·재배치·검증**할 때의 단일 기준 사전이다. +근거 원본(스크린샷 분석)은 아래를 참조하며, 값이 갱신되면 **이 파일과 원본을 함께** 고친다. + +- `docs/[Ubisam산출물]/[분석]/_ubisam-협업관리-용어집(skill-ref).md` — enum 사전(원본) +- `docs/[Ubisam산출물]/[분석]/2026_06_29_협업관리-프로젝트관리-분석.md` — 단계별 산출물 정의 상세 +- `docs/[Ubisam산출물]/ref/` — 화면 캡처 14장 + +> 본문은 요약 기준만 담는다. 단계별 산출물 표 전문이 필요하면 위 분석 문서를 열어라. + +## 1. 상태(Status) enum — 4종 (전이 순서) + +``` +해야 할 일 → 분석 진행 → 업무 진행중 → 진행 완료 +``` +| 상태 | 색(ARGB) | 칸반 컬럼 | +|---|---|---| +| 해야 할 일 | FFD9D9D9 (회색) | Todo | +| 분석 진행 | FF8FAADC→청색 | Select For dev | +| 업무 진행중 | FF8FAADC (파랑) | InProgress | +| 진행 완료 | FFA9D08E (연두) | Done | + +상태 문자열은 위 4개만 사용한다. 임의 변형(예: "완료", "진행중") 금지 — 칸반/조건부서식 매칭이 깨진다. + +## 2. 일감 유형(Type) enum — 9종 + +오류수정 · 불합리 · 미팅 · 설계 · 개발 · 제작 · 입고셋업 · 적용 및 모니터링 · 요청대응 + +## 3. 진행 6단계 ↔ 기본 일감유형 ↔ 산출물 슬롯 + +| # | 단계 | 기본 유형 | 핵심 산출물(슬롯) | +|---|---|---|---| +| 1 | 분석/협의 | 미팅 | 사양서, CR회의록, 마스터플랜, URS 분석/제어 시나리오 검토 | +| 2 | 설계 | 설계 | HMI 화면정의서·UI 스토리보드·기능명세서 / 시퀀스 다이어그램·인터록 정의서 / 통신맵(Memory·IO·Register)·DB 스키마 / D/R 회의록 | +| 3 | 제작 | 제작 | 개발환경·핵심모듈·UI/메인 시퀀스·시뮬레이션 / 단위·통합 테스트 결과 보고서(Check-List) | +| 4 | 셋업 | 입고셋업 | HW IO/모션 점검 시트 / 파라미터·레시피 백업 / 이슈 로그(Dry Run·Tack Time) / MES 연동 / SAT 결과서 | +| 5 | 양산 | 적용 및 모니터링 | 운영 가이드·매뉴얼(HMI·알람조치) / S/W 최적화 / 최종 버전 동결·마스터 백업 / 프로젝트 완료 보고서 | +| 6 | CS | 요청대응 | 요청대응 이력(가동 후 CS) | + +**타사 양식 → 슬롯 매핑 원칙**: 원본 문서의 목적(요구분석/설계정의/테스트/셋업/운영매뉴얼)을 위 슬롯 키워드와 대조해 단계를 결정한다. 한 문서가 여러 단계에 걸치면 주(主) 목적 단계에 배치하고 비고에 보조 단계를 병기한다. + +## 4. 일감 필드 스키마 + +| 필드 | 규칙 | +|---|---| +| 제목 | `N.단계명` (예: `2.설계`) | +| 티켓번호 | `{프로젝트코드}-{일련번호}` (예: `LGIT_SI_01_20260601-1024`) | +| 유형 | §2 enum 1개 | +| 상태 | §1 enum 1개 | +| 우선순위 | 보통(=Normal) 기본 | +| 담당자/참조자 | `이름(직급)` | +| 진행률 | % | +| 등록일/수정일/종료일 | yyyy/mm/dd (또는 yyyy-mm-dd) | +| 예상시간 | `2w 4d 6h 45m` 형식 | +| Hashtag | 자유 태그 | +| 첨부파일 | 최대 2.93GB | + +## 5. 프로젝트 메타 스키마 + +프로젝트명 · 종류(Software) · 프로젝트형식(Kanban) · 코드명 · 사업부/팀 · PL · 등록자 · 계약일/납기 · 권한(책임자→참여자→열람자). + +이 프로젝트(시범 대상) 고정값: +- 프로젝트명 `LGIT_구미_FMM 공소수정기 운영SW`, 코드명 `LGIT_SI_01_20260601` +- 사업부/팀 `로봇자동화사업부 / 로봇팀`, PL `김영중 부장`, 계약일 `2026-03-25`, 납기 `2026-10-30` + +## 6. PTOP 채움 시 공통 사실 + +설비: 각형 배터리 레이저 용접기(ILSOS OC5 PtoP Welder), Aurora 2.0 / .NET 4.8.1 / WPF. +주요 디바이스: PLC(Mitsubishi Melsec·MX Component), Laser Scanner(RTC), Laser(IO Gate), Ajin Digital IO. +메인 시퀀스: `SeqAutoRunPTOP`. PLC 인터페이스: `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs`. + +> 재생산 산출물의 **출처(코드 경로/커밋)**를 항상 명시한다 — 검증가가 근거 추적에 사용한다. diff --git a/.claude/skills/ubisam-ppt-make/assets/Ubisam_logo.gif b/.claude/skills/ubisam-ppt-make/assets/Ubisam_logo.gif new file mode 100644 index 0000000..3c22428 Binary files /dev/null and b/.claude/skills/ubisam-ppt-make/assets/Ubisam_logo.gif differ diff --git a/.claude/skills/ubisam-ppt-make/assets/Ubisam_logo_transparent.png b/.claude/skills/ubisam-ppt-make/assets/Ubisam_logo_transparent.png new file mode 100644 index 0000000..42d4217 Binary files /dev/null and b/.claude/skills/ubisam-ppt-make/assets/Ubisam_logo_transparent.png differ diff --git a/.claude/skills/ubisam-ppt-make/examples/showcase.json b/.claude/skills/ubisam-ppt-make/examples/showcase.json new file mode 100644 index 0000000..4ca0c3c --- /dev/null +++ b/.claude/skills/ubisam-ppt-make/examples/showcase.json @@ -0,0 +1,394 @@ +{ + "doc_title": "P-PRJ BFHB SW 설계서", + "slides": [ + { + "type": "cover", + "tag": "SW DESIGN DOCUMENT", + "title": "P-PRJ BFHB SW 설계서", + "subtitles": [ + "인도향 P-Project · HFHB (Button Flex Hot Bar 열압착 본딩기)", + "A device that compresses a button flex by applying pressure and temperature to a point" + ], + "team": "시스템 사업팀", + "contacts": [ + "조정묵 과장 jojeongmuk@ubisam.com", + "남재근 대리 namjaegeun@ubisam.com" + ] + }, + { + "type": "history", + "rows": [ + [ + "1", + "1.0", + "2025.04.28", + "조정묵", + "초안 작성" + ], + [ + "2", + "1.1", + "2025.04.29", + "조정묵", + "UI 안, Class 내용 추가" + ], + [ + "3", + "1.2", + "2025.05.09", + "조정묵", + "250425_엑추에이터 정리.xlsx 내용 반영" + ], + [ + "4", + "1.3", + "2025.05.15", + "조정묵", + "[DFM]_LG PRI_V57_BFHB_1.2 내용 반영" + ], + [ + "5", + "1.4", + "2025.05.21", + "조정묵", + "[DFM]_LG PRI_V57_BFHB_1.7 내용 반영" + ] + ] + }, + { + "type": "agenda", + "items": [ + { + "no": "01", + "title": "System Overview", + "sub": "시스템 구성도" + }, + { + "no": "02", + "title": "UI Concept", + "sub": "Main / Teaching UI 구성" + }, + { + "no": "03", + "title": "Sequence Flowchart", + "sub": "Loading · Bonding · Unloading · PDCA" + }, + { + "no": "04", + "title": "Class 관계도", + "sub": "Class Diagram" + }, + { + "no": "05", + "title": "축 구성", + "sub": "Axis Configuration" + }, + { + "no": "06", + "title": "IO 구성", + "sub": "I/O Map" + } + ] + }, + { + "type": "divider", + "no": "01", + "title": "System Overview", + "subtitle": "시스템 구성도" + }, + { + "type": "content", + "no": "02.", + "title": "UI Concept", + "subtitle": "Main UI 구성 1", + "panels": [ + { + "title": "HotBar 측정 값", + "body": "선형 그래프 (최근 생산 100개 표시)" + }, + { + "title": "IO Monitoring", + "body": "조작 버튼 / 상태 표시" + }, + { + "title": "CPK", + "body": "막대형 그래프" + }, + { + "title": "OK 판정률", + "body": "도넛 그래프 (Bali 수신 + 현재공정 판정)" + } + ] + }, + { + "type": "flow", + "no": "03.", + "title": "Sequence Flowchart", + "subtitle": "[Loading]", + "steps": [ + { + "title": "Wait (Is Loader Empty)", + "code": "SendMachine_Ready", + "kind": "proc" + }, + { + "title": "Is UpStream Ready", + "code": "CHECK_UPSTREAM", + "kind": "dec" + }, + { + "title": "Wait (Is Board Available)", + "code": "BoardAvailable", + "kind": "proc" + }, + { + "title": "Loader Conveyor On", + "code": "SendStartTransport", + "kind": "proc" + }, + { + "title": "Is TransportError", + "code": "TransportError", + "kind": "dec" + } + ], + "branch": { + "title": "Conveyor Stop", + "sub": "Sequence Stop" + } + }, + { + "type": "table", + "no": "06.", + "title": "IO 구성", + "subtitle": "I/O Map", + "headers": [ + "No.", + "Type", + "Address", + "Signal Name", + "Description" + ], + "widths": [ + 0.8, + 1.4, + 1.7, + 3.3, + 4.2 + ], + "center_cols": [ + 0, + 1, + 2 + ], + "rows": [ + [ + "1", + "DI", + "X00.0", + "Start_Button", + "사이클 시작 입력" + ], + [ + "2", + "DI", + "X00.1", + "Stop_Button", + "사이클 정지 입력" + ], + [ + "3", + "DI", + "X00.2", + "EMG_Stop", + "비상 정지" + ], + [ + "4", + "DO", + "Y10.0", + "Tower_Lamp_R", + "경광등 적색" + ], + [ + "5", + "DO", + "Y10.1", + "Conveyor_Run", + "컨베이어 구동" + ], + [ + "6", + "DO", + "Y10.2", + "Stopper1_Up", + "스토퍼1 상승" + ] + ] + }, + { + "type": "divider", + "no": "07", + "title": "Operation Manual", + "subtitle": "조작 매뉴얼" + }, + { + "type": "screen_guide", + "no": "07.", + "title": "화면 설명", + "subtitle": "Main 운전 화면", + "caption": "Main 운전 화면 캡처", + "items": [ + { + "title": "운전 상태 표시", + "desc": "현재 사이클/공정 상태 램프", + "pos": [ + 0.5, + 0.5 + ] + }, + { + "title": "측정값 그래프", + "desc": "Load Cell · 온도 실시간 추이", + "pos": [ + 3.0, + 1.5 + ] + }, + { + "title": "판정 결과", + "desc": "OK/NG 도넛 그래프", + "pos": [ + 5.5, + 2.5 + ] + }, + { + "title": "조작 버튼", + "desc": "Start / Stop / Reset", + "pos": [ + 1.5, + 3.8 + ] + }, + { + "title": "알람 표시줄", + "desc": "최근 발생 알람 메시지", + "pos": [ + 4.0, + 4.3 + ] + } + ] + }, + { + "type": "procedure", + "no": "07.", + "title": "조작 절차", + "subtitle": "운전 시작 순서", + "steps": [ + { + "title": "전원 ON 및 초기화", + "desc": "메인 전원 인가 후 [Reset] 버튼으로 장비 초기화" + }, + { + "title": "원점 복귀", + "desc": "[Home] 버튼 선택 → 전 축 원점 복귀 완료 확인" + }, + { + "title": "레시피 선택", + "desc": "생산 모델에 맞는 레시피 로드 및 파라미터 확인" + }, + { + "title": "자동 운전 시작", + "desc": "[Auto] 모드 전환 후 [Start] 버튼으로 사이클 시작" + }, + { + "title": "운전 종료", + "desc": "[Stop] 후 사이클 완료 대기 → 전원 OFF" + } + ] + }, + { + "type": "icon_ref", + "no": "07.", + "title": "버튼 설명", + "subtitle": "Main UI 조작 버튼", + "items": [ + { + "icon": "▶", + "name": "Start", + "func": "자동 운전 시작" + }, + { + "icon": "■", + "name": "Stop", + "func": "사이클 정지" + }, + { + "icon": "↺", + "name": "Reset", + "func": "알람 해제 / 초기화" + }, + { + "icon": "⌂", + "name": "Home", + "func": "전 축 원점 복귀" + }, + { + "icon": "≡", + "name": "Recipe", + "func": "레시피 관리 화면" + }, + { + "icon": "⚙", + "name": "Setup", + "func": "설정 / 파라미터" + } + ] + }, + { + "type": "alarm", + "no": "07.", + "title": "알람 및 조치 방법", + "subtitle": "주요 알람 코드", + "notice": "알람 발생 시 원인 확인 후 아래 조치 방법을 순서대로 수행하십시오.", + "rows": [ + [ + "AL-001", + "비상정지 입력", + "EMG 버튼 눌림", + "안전 확인 후 EMG 해제 → Reset" + ], + [ + "AL-012", + "원점 복귀 실패", + "센서 미감지 / 간섭", + "축 위치 확인 후 수동 원점 재시도" + ], + [ + "AL-024", + "Load Cell 통신 이상", + "케이블 단선 / 노이즈", + "커넥터 체결 확인 후 전원 재투입" + ], + [ + "AL-031", + "온도 상한 초과", + "Heater 과열", + "설정 온도 확인 → 냉각 후 재가동" + ] + ] + }, + { + "type": "closing", + "title": "Thank You", + "message": "문의 사항은 아래 담당자에게 연락 바랍니다.", + "team": "시스템 사업팀", + "contacts": [ + "조정묵 과장 jojeongmuk@ubisam.com", + "남재근 대리 namjaegeun@ubisam.com" + ] + } + ] +} \ No newline at end of file diff --git a/.claude/skills/ubisam-ppt-make/생성파일/test_bg.pptx b/.claude/skills/ubisam-ppt-make/생성파일/test_bg.pptx new file mode 100644 index 0000000..e2322d3 Binary files /dev/null and b/.claude/skills/ubisam-ppt-make/생성파일/test_bg.pptx differ diff --git a/.claude/skills/ubisam-ppt-make/생성파일/test_wm.pptx b/.claude/skills/ubisam-ppt-make/생성파일/test_wm.pptx new file mode 100644 index 0000000..00c66d8 Binary files /dev/null and b/.claude/skills/ubisam-ppt-make/생성파일/test_wm.pptx differ diff --git a/.claude/skills/ui_layout_image/SKILL.md b/.claude/skills/ui_layout_image/SKILL.md new file mode 100644 index 0000000..859f8a3 --- /dev/null +++ b/.claude/skills/ui_layout_image/SKILL.md @@ -0,0 +1,68 @@ +--- +name: ui_layout_image +description: UI 소스코드(예 C# WPF XAML, WinForms, Qt, HTML/CSS, Android/Flutter)를 읽어 화면의 레이아웃 구조를 분석하고, 스크린샷 없이 그리드/컨테이너 구조를 그대로 도식(wireframe mockup) 이미지로 생성해 저장한다. 필요 시 PPT/매뉴얼에 삽입. "화면 구성 이미지로 만들어줘", "XAML 보고 레이아웃 그려줘", "코드 보고 화면 도식/목업 만들어 매뉴얼에 넣어줘", "스크린샷 말고 코드로 화면 그려줘" 요청 시 사용. +--- + +# ui_layout_image — 코드 기반 화면 레이아웃 도식 생성 스킬 + +**실행 중인 앱 스크린샷이 아니라**, UI 소스코드의 레이아웃 정의(컨테이너/그리드/컨트롤)를 읽어 +화면 구성을 도식(wireframe mockup) 이미지로 렌더링한다. 매뉴얼·산출물·문서에 삽입하기 위함. + +## 언제 쓰나 +- 앱을 띄울 수 없거나(하드웨어/시뮬 불가), 스크린샷이 부적절할 때 +- 코드만으로 "이 화면이 이렇게 생겼다"를 문서/매뉴얼에 보여줄 때 +- 여러 화면을 일관된 스타일의 도식으로 빠르게 만들 때 + +## 입력(args) 해석 +- 대상 화면(들)의 소스 파일 경로 또는 화면 이름. 비어 있으면 무엇을 그릴지 한 줄로 물어본다. +- 선택 옵션: 스타일(dark HMI / light), 출력 경로, 삽입 대상(PPTX 경로·슬라이드). + +## 동작 절차 + +### 1) UI 소스 식별 및 레이아웃 파싱 (프레임워크별) +대상 언어/프레임워크를 판별하고 **레이아웃 트리**(중첩 컨테이너 + 컨트롤 + 라벨/바인딩)를 추출한다. + +| 프레임워크 | 읽을 것 | 핵심 레이아웃 단서 | +|-----------|--------|-------------------| +| **C# WPF** (이 프로젝트 기본) | `*.xaml` | `Grid`(RowDefinitions/ColumnDefinitions, `*`/`auto`/비율), `GroupBox.Header`, `StackPanel`, `UniformGrid(Rows/Columns)`, `Button.Content`, `TextBlock.Text`, `usercontrols:UCKeyValue KeyText/Unit`, `Grid.Row/Column/ColumnSpan` | +| C# WinForms | `*.Designer.cs` | `Controls.Add`, `.Location`, `.Size`, `.Dock`, `.Text` | +| Qt | `*.ui`(XML) | ``, ``, `` | +| HTML/CSS | `*.html`/`*.css`/JSX | div 구조, flex/grid, 라벨 텍스트 | +| Android | `res/layout/*.xml` | `LinearLayout`/`ConstraintLayout`, `android:text` | +| Flutter | `*.dart` | `Row`/`Column`/`Grid`/`Container`, `Text(...)` | + +- 비율(`2*:1*` 등), 행/열 수, 헤더 텍스트, 버튼/입력칸/표 컬럼을 **코드에서 그대로** 가져온다. 임의로 지어내지 않는다. +- 바인딩(`{Binding ...}`)은 라벨/예시값으로 치환(예: "Process Running", "1234")하되 의미를 보존한다. + +### 2) 도식 이미지 렌더링 (Python · matplotlib) +- 실행: Windows에선 `py`(파이썬 런처). 라이브러리: `matplotlib`, `Pillow` 사용 가능. +- 한글 폰트: `C:\Windows\Fonts\malgun.ttf` 를 `font_manager`로 등록. +- 좌표계: `ax.set_ylim(H, 0)` 로 좌상단 원점(화면 좌표계와 동일). +- 스타일 기본값 = **dark HMI** (이 프로젝트 UI와 일치): + - 배경 `#2D2D30`, 패널 `#3E3E42`, 테두리 `#6E6E72`, 헤더바 `#007ACC`, 글자 `#DDDDDD`, + 강조버튼 보라 `#7E57C2`/파랑 `#2D6CA2`, 입력칸 `#252528`. +- 컴포넌트 헬퍼: `panel(header)`(둥근 박스+헤더바), `kv(key,val)`(키-값 입력칸), `button(text,color)`, `label`. +- **그리드 구조를 그대로**: 컬럼 비율 → 폭 배분, 행 정의(auto/`*`) → 높이 배분, GroupBox → panel, UniformGrid → 균등 분할. +- 참고 구현(이 프로젝트 실제 사용): `UnTrackedLocalFiles/`의 `gen_mockups.py`, `gen_menubar.py` 패턴. + +### 3) 저장 +- 기본 출력: `UnTrackedLocalFiles/hmi_mockups/.png` (git 제외 위치). 다른 위치 지정 시 그곳에. +- 생성 후 Read 도구로 PNG를 열어 **시각 검증**(겹침/라벨 누락 확인). 문제 있으면 수정 후 재생성. + +### 4) (선택) 문서/PPT 삽입 +- PPTX: `python-pptx`로 대상 슬라이드의 기존 스크린샷(PICTURE)·번호 콜아웃(예 "타원*")을 제거하고 + 같은 위치/크기에 `add_picture`. 빈 콜아웃 박스도 정리. +- 검증: LibreOffice(`soffice --headless --convert-to pdf`) → PyMuPDF(`fitz`)로 PNG 렌더 후 Read로 확인. + +### 5) 보고 +- 생성한 이미지 경로, 반영한 화면/소스, 삽입 위치를 한 줄로 요약. 코드와 다르게 추정한 부분은 명시. + +## 주의 +- 반드시 **소스코드 기준**. 실제 값이 불명확하면 라벨/예시값으로 두고 "(예시)" 표기. +- 원본 문서(PPT/Excel) 수정 전 백업(`UnTrackedLocalFiles/...backup`). 서식 보존. +- 생성 스크립트·중간 렌더는 `UnTrackedLocalFiles/`(커밋 제외)에 둔다. 산출물(이미지가 삽입된 문서)만 커밋. + +## 호출 예시 +- `/ui_layout_image UCMainAutoRun.xaml 화면 도식 만들어줘` +- `/ui_layout_image Manual/Calibration 화면들 dark 스타일로 그려서 HMI Manual.pptx 슬라이드에 넣어줘` +- 자연어: "스크린샷 말고 XAML 그리드 구조 보고 화면 레이아웃 이미지로 만들어서 매뉴얼에 넣어줘" diff --git a/.claude/skills/ui_layout_image/render_helpers.py b/.claude/skills/ui_layout_image/render_helpers.py new file mode 100644 index 0000000..2c92727 --- /dev/null +++ b/.claude/skills/ui_layout_image/render_helpers.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +"""ui_layout_image 스킬용 렌더 헬퍼 (dark HMI 스타일). +사용 예: + py render_helpers.py # 데모 1장 생성 +실전에서는 import 하여 panel/kv/button/label 로 화면별 도식을 그린다. +""" +import os +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +from matplotlib.patches import FancyBboxPatch, Rectangle +import matplotlib.font_manager as fm + +# ---- 한글 폰트 ---- +FONT = r"C:\Windows\Fonts\malgun.ttf" +try: + fp = fm.FontProperties(fname=FONT) + plt.rcParams["font.family"] = fp.get_name() +except Exception: + pass +plt.rcParams["axes.unicode_minus"] = False + +# ---- dark HMI 팔레트 (프로젝트 UI와 일치) ---- +BG = "#2D2D30"; PANEL = "#3E3E42"; BORDER = "#6E6E72"; HEADER = "#007ACC" +TXT = "#DDDDDD"; PURPLE = "#7E57C2"; BLUE = "#2D6CA2"; GREEN = "#2E7D32"; GRAY = "#555558" +INPUT = "#252528"; VALCOLOR = "#9CDCFE" + + +def new_canvas(w, h): + fig = plt.figure(figsize=(w / 100, h / 100), dpi=100) + ax = fig.add_axes([0, 0, 1, 1]) + ax.set_xlim(0, w); ax.set_ylim(h, 0); ax.axis("off") + ax.add_patch(Rectangle((0, 0), w, h, facecolor=BG, edgecolor="none")) + return fig, ax + + +def panel(ax, x, y, w, h, header=None, fc=PANEL): + ax.add_patch(FancyBboxPatch((x + 2, y + 2), w - 4, h - 4, boxstyle="round,pad=2", + facecolor=fc, edgecolor=BORDER, linewidth=1.2)) + if header: + ax.add_patch(Rectangle((x + 4, y + 4), w - 8, 22, facecolor=HEADER, edgecolor="none")) + ax.text(x + 12, y + 15, header, color="white", fontsize=9, fontweight="bold", va="center") + return x + 8, y + 30 + + +def label(ax, x, y, text, size=8.5, color=TXT, bold=False, ha="left"): + ax.text(x, y, text, color=color, fontsize=size, va="center", ha=ha, + fontweight=("bold" if bold else "normal")) + + +def kv(ax, x, y, w, key, val=""): + ax.text(x + w * 0.46, y, key, color=TXT, fontsize=8, va="center", ha="right") + ax.add_patch(Rectangle((x + w * 0.5, y - 9), w * 0.48, 18, facecolor=INPUT, edgecolor=BORDER, lw=0.6)) + if val: + ax.text(x + w * 0.52, y, val, color=VALCOLOR, fontsize=8, va="center", ha="left") + + +def button(ax, x, y, w, h, text, color=BLUE): + ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle="round,pad=1.5", facecolor=color, edgecolor="none")) + ax.text(x + w / 2, y + h / 2, text, color="white", fontsize=8.5, fontweight="bold", ha="center", va="center") + + +def save(fig, path): + os.makedirs(os.path.dirname(path), exist_ok=True) + fig.savefig(path, facecolor=BG) + plt.close(fig) + + +if __name__ == "__main__": + fig, ax = new_canvas(600, 300) + panel(ax, 10, 10, 580, 280, "Demo Panel") + kv(ax, 30, 60, 300, "Key", "Value") + button(ax, 30, 90, 120, 28, "Run", PURPLE) + save(fig, os.path.join(os.path.dirname(__file__), "_demo.png")) + print("demo saved")