Compare commits
2 Commits
31534ff1bb
...
e34ed97a3a
| Author | SHA1 | Date | |
|---|---|---|---|
| e34ed97a3a | |||
| 08a60d2605 |
42
.claude/skills/doyeon_note/SKILL.md
Normal file
42
.claude/skills/doyeon_note/SKILL.md
Normal file
@ -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/작업일지/<yyyy_mm_dd>.md`
|
||||
|
||||
3. **내용 정리**: 사용자가 `args`로 전달한 내용을 작업일지 항목으로 정리한다.
|
||||
- 단순 한 줄이면 그대로 한 항목으로.
|
||||
- 여러 작업/생각이 섞여 있으면 자연스럽게 불릿(`-`)으로 나눈다.
|
||||
- 사용자가 적은 의도/뉘앙스는 보존하고, 임의로 내용을 지어내지 않는다.
|
||||
|
||||
4. **파일 기록**:
|
||||
- 파일이 **없으면** 아래 템플릿으로 새로 만든다.
|
||||
- 파일이 **있으면** 기존 내용을 Read한 뒤, 맨 아래에 새 항목을 이어 붙인다(append). 기존 내용은 절대 지우지 않는다.
|
||||
- 같은 날 여러 번 호출되면 시각(`HH:MM`) 소제목을 붙여 구분한다.
|
||||
|
||||
5. 기록 후, 어떤 파일에 무엇을 추가했는지 한 줄로 사용자에게 알린다.
|
||||
|
||||
## 새 파일 템플릿
|
||||
|
||||
```markdown
|
||||
# 작업일지 — yyyy-mm-dd
|
||||
|
||||
## HH:MM
|
||||
- (항목)
|
||||
```
|
||||
|
||||
## 주의
|
||||
|
||||
- `args`가 비어 있으면 무엇을 기록할지 사용자에게 한 줄로 물어본다.
|
||||
- 날짜/파일명 형식은 반드시 `yyyy_mm_dd`를 지킨다.
|
||||
- 작업일지는 사용자의 기록이므로, 내용을 요약·각색하지 말고 사용자의 표현을 최대한 살린다.
|
||||
79
.claude/skills/ptop-deliverables/SKILL.md
Normal file
79
.claude/skills/ptop-deliverables/SKILL.md
Normal file
@ -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` 상태·진행률을 현황 반영해 갱신.
|
||||
적용 디자인: 상태 뱃지 + 유형 뱃지 + `<progress>` 진행률 바.
|
||||
|
||||
### STEP 4 — 분석 로그 기록
|
||||
`[분석]/YYYY_MM_DD_PTOP설계산출물-분석.md` 작성.
|
||||
내용: 소스→필드 매핑 표 + 미확정 항목 목록 + 진실원 인덱스.
|
||||
|
||||
## 불변 규칙
|
||||
- **비침습**: `docs/[Ubisam산출물]/` 밖은 읽기만. 소스·CLAUDE.md 수정 금지.
|
||||
- **MD 200줄 상한**: 초과 시 분할 + 상대경로 링크.
|
||||
- **출처 명시**: 채운 값마다 `— 출처: 파일명:심볼` 주석.
|
||||
- **미확정 표기**: 주소·행정·속도 등 미확정값 → `(제안)`.
|
||||
- **멱등**: 재실행 시 배너·뱃지는 교체, 테이블 데이터는 행 추가/갱신.
|
||||
- **Aurora 고정 문구**: "운영 SW는 LG PRI의 Aurora 플랫폼으로 개발한다."
|
||||
126
.claude/skills/ptop-deliverables/references/design-system.md
Normal file
126
.claude/skills/ptop-deliverables/references/design-system.md
Normal file
@ -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
|
||||
<div style="background:#1F4E78;color:#fff;padding:8px 16px;border-radius:4px 4px 0 0;margin-bottom:0">
|
||||
<strong style="font-size:1.1em">문서 제목</strong>
|
||||
</div>
|
||||
<div style="background:#2E75B6;color:#fff;padding:4px 16px;border-radius:0 0 4px 4px;font-size:.85em;margin-bottom:12px">
|
||||
근거: 소스 경로 · Aurora 2.0 / .NET 4.8.1 / WPF
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 상태 뱃지
|
||||
|
||||
`_task.md` 상태 필드에 사용. 값에 맞춰 색 코드 교체.
|
||||
|
||||
```html
|
||||
<!-- 해야 할 일 -->
|
||||
<span style="background:#757575;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">해야 할 일</span>
|
||||
|
||||
<!-- 분석 진행 -->
|
||||
<span style="background:#1976D2;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">분석 진행</span>
|
||||
|
||||
<!-- 업무 진행중 -->
|
||||
<span style="background:#1565C0;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">업무 진행중</span>
|
||||
|
||||
<!-- 진행 완료 -->
|
||||
<span style="background:#689F38;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">진행 완료</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 유형 뱃지
|
||||
|
||||
```html
|
||||
<span style="background:#00796B;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">미팅</span>
|
||||
<span style="background:#689F38;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">설계</span>
|
||||
<span style="background:#283593;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">제작</span>
|
||||
<span style="background:#7B1FA2;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">입고셋업</span>
|
||||
<span style="background:#0288D1;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">적용 및 모니터링</span>
|
||||
<span style="background:#1A237E;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">요청대응</span>
|
||||
<span style="background:#1976D2;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">개발</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 진행률 바
|
||||
|
||||
`<progress>` 태그 + 텍스트 병기 (렌더러 호환).
|
||||
|
||||
```html
|
||||
<progress value="85" max="100" style="width:160px;vertical-align:middle"></progress> <strong>85%</strong>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. _task.md 전체 템플릿
|
||||
|
||||
```markdown
|
||||
<div style="background:#1F4E78;color:#fff;padding:8px 16px;border-radius:4px 4px 0 0">
|
||||
<strong>N.단계명</strong> — LGES OC5 PtoP Welder
|
||||
</div>
|
||||
<div style="background:#2E75B6;color:#fff;padding:3px 16px;border-radius:0 0 4px 4px;font-size:.85em;margin-bottom:12px">
|
||||
LGES_PTOP_01_20260605-XXXX
|
||||
</div>
|
||||
|
||||
| 필드 | 값 |
|
||||
|---|---|
|
||||
| 유형 | <span style="background:COLOR;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">유형명</span> |
|
||||
| 상태 | <span style="background:COLOR;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">상태명</span> |
|
||||
| 담당자 / 참조자 | {미정} / {미정} |
|
||||
| 진행률 | <progress value="N" max="100" style="width:160px;vertical-align:middle"></progress> **N%** |
|
||||
| Hashtag | 태그1 / 태그2 |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 산출물 섹션 헤더
|
||||
|
||||
산출물 MD 내 시트 구분 헤더.
|
||||
|
||||
```markdown
|
||||
## <span style="color:#2E75B6">시트① 제목</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 미확정·제안 강조
|
||||
|
||||
```markdown
|
||||
> [!NOTE]
|
||||
> 아래 값은 소스 enum offset 기반 `(제안)`. PLC 맵 CSV 입수 후 확정.
|
||||
```
|
||||
105
.claude/skills/ptop-deliverables/references/source-map.md
Normal file
105
.claude/skills/ptop-deliverables/references/source-map.md
Normal file
@ -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% | 미착수 |
|
||||
|
||||
> 이 값은 스킬 실행 시점 기준. 재실행 시 소스·폴더 현황 재확인 후 갱신.
|
||||
49
.claude/skills/ubisam-deliverable/SKILL.md
Normal file
49
.claude/skills/ubisam-deliverable/SKILL.md
Normal file
@ -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/` 산출물을 기반으로 변경분만 갱신한다.
|
||||
36
.claude/skills/ubisam-deliverables/README.md
Normal file
36
.claude/skills/ubisam-deliverables/README.md
Normal file
@ -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 고정(상태/유형) · 출처 명시 · 조건부 서식 보존 · 레포 루트 보호.
|
||||
71
.claude/skills/ubisam-deliverables/SKILL.md
Normal file
71
.claude/skills/ubisam-deliverables/SKILL.md
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
name: ubisam-deliverables
|
||||
description: >-
|
||||
Ubisam 협업관리(칸반) 구조에 맞춰 프로젝트 산출물을 docs/[Ubisam산출물]/ 트리로
|
||||
구성·관리한다. "산출물 구조 만들기", "Ubisam 양식으로 채우기", "설계/셋업/양산 산출물
|
||||
정리", "산출물 트리 스캐폴딩" 같은 요청에 사용. 6단계(분석·설계·제작·셋업·양산·CS)
|
||||
폴더·_meta enum·일감 카드·LGPRI 양식 채움 절차를 제공.
|
||||
---
|
||||
|
||||
# Ubisam 산출물 관리 SKILL
|
||||
|
||||
UBISAM 협업관리는 *프로젝트(칸반 보드 1개) → 일감(6단계) → 단계별 산출물* 로 운영된다.
|
||||
이 스킬은 그 구조를 **소스 레포의 `docs/[Ubisam산출물]/` 하위 파일 트리로 미러링**하고,
|
||||
LGPRI 제공 양식을 프로젝트 내용으로 채워 관리한다.
|
||||
|
||||
## 언제 쓰나
|
||||
- 새 프로젝트에 산출물 트리를 깔 때(스캐폴딩).
|
||||
- `산출물문서/양식/`에 들어온 빈 양식을 프로젝트 내용으로 채울 때.
|
||||
- 단계별 일감 상태/진행률을 갱신할 때.
|
||||
|
||||
## 참조 맵 (모두 200줄 이내, 필요한 것만 읽기)
|
||||
| # | 파일 | 용도 |
|
||||
|---|---|---|
|
||||
| 0 | [references/00-harness.md](references/00-harness.md) | **하네스**: 호스트 레포 맞물림·비침습 계약 (먼저 읽기) |
|
||||
| 1 | [references/01-tree.md](references/01-tree.md) | 목표 트리·배치 규칙·우선순위 |
|
||||
| 2 | [references/02-enums.md](references/02-enums.md) | 상태 4종·유형 9종·티켓코드 규칙 |
|
||||
| 3 | [references/03-schema.md](references/03-schema.md) | 일감 필드·프로젝트 메타 스키마 |
|
||||
| 4 | [references/04-stage-deliverables.md](references/04-stage-deliverables.md) | 6단계↔산출물 슬롯 매핑 |
|
||||
| 5 | [references/05-form-schemas.md](references/05-form-schemas.md) | 설계 양식 시트·컬럼 스키마 |
|
||||
| 6 | [references/06-procedure.md](references/06-procedure.md) | 분석→채움 절차·조건서식 보존 |
|
||||
| 7 | [references/07-fill-mapping.md](references/07-fill-mapping.md) | 프로젝트 분석→산출물 필드 자동 채움 매핑 |
|
||||
| **8** | [**references/08-design-system.md**](references/08-design-system.md) | **색상 팔레트·배너·뱃지·진행률 HTML 템플릿** |
|
||||
| **9** | [**references/09-pptx-integration.md**](references/09-pptx-integration.md) | **PPTX 생성 — ubisam-ppt-make 연동 절차·슬라이드 매핑** |
|
||||
| T | [templates/](templates/) | `_task` 카드·`project.yaml` 템플릿 |
|
||||
|
||||
## 실행 순서 (오케스트레이션)
|
||||
|
||||
### 0. 하네스 진입 (항상 먼저)
|
||||
[00-harness.md](references/00-harness.md)의 비침습 계약 확인 → 호스트 `CLAUDE.md`·소스에서 프로젝트 사실 읽기 → `project_code`/`project_name` 자동 추정(실패 시 질문).
|
||||
|
||||
### A. 스캐폴딩 (트리 생성)
|
||||
1. 소스 레포 루트의 `.claude/`·`CLAUDE.md`·`RULES.md`는 **건드리지 않는다**.
|
||||
2. `docs/[Ubisam산출물]/` 아래에 [01-tree.md](references/01-tree.md)의 트리를 생성:
|
||||
- `_meta/`(enum·스키마) ← [02](references/02-enums.md)·[03](references/03-schema.md) 내용으로 채움.
|
||||
- `01.분석협의 ~ 06.CS/` 6단계 폴더 + 각 `_task.md`(템플릿: [templates/_task.template.md](templates/_task.template.md)).
|
||||
- 각 단계 산출물 슬롯 하위폴더 + `.gitkeep` ← [04](references/04-stage-deliverables.md).
|
||||
- `산출물문서/`(+`양식/` 드롭존), `ref/`, `[분석]/`, `[작업일지]/`.
|
||||
3. `RULES.MD`(산출물 영역 규칙) + `README.md`(트리 한눈에 + 단계별 현황 뱃지 표)를 루트에 작성.
|
||||
4. `_meta/project.yaml`의 `{...}` 토큰은 프로젝트 정보로 교체. 모르면 그대로 두고 사용자에게 질문.
|
||||
5. **디자인 적용**: 모든 `_task.md`와 `README.md`에 [08-design-system.md](references/08-design-system.md) § 7 템플릿 적용.
|
||||
|
||||
### B. 양식 채움 (산출물 생성)
|
||||
[06-procedure.md](references/06-procedure.md) 절차를 따른다: 분석 → 타설비 잔재 식별 → 프로젝트 내용 채움(출처 명시) → 슬롯 배치 → `_task.md` 진행률 갱신 → `[작업일지]` 기록.
|
||||
- **자동 채움**: 호스트 분석 사실을 [07-fill-mapping.md](references/07-fill-mapping.md) 매핑으로 산출물 필드에 기입.
|
||||
- **디자인 적용**: 생성하는 모든 MD에 [08-design-system.md](references/08-design-system.md) 배너·뱃지·섹션헤더 삽입.
|
||||
- 양식의 시트·컬럼 골격은 [05-form-schemas.md](references/05-form-schemas.md) 참조(ref 불필요).
|
||||
- 상태/유형 값은 [02-enums.md](references/02-enums.md) enum만 사용(임의 신규값 금지).
|
||||
|
||||
### C. PPTX 생성 (ubisam-ppt-make 연동)
|
||||
[09-pptx-integration.md](references/09-pptx-integration.md) 절차를 따른다.
|
||||
- **2.설계 채움 완료** → `SW_설계서.pptx` (선택): MD 표/시퀀스 → JSON spec → `build_deck.py`
|
||||
- **5.양산 채움 or 명시 요청** → `<설비명>_HMI_매뉴얼.pptx` (필수): View별 기능·절차·알람 → JSON spec → `build_deck.py`
|
||||
- 마스터 배경(#F4F7FA + accent 바)은 `build_deck.py`가 자동 적용.
|
||||
|
||||
## 불변 규칙 (항상 지킴)
|
||||
- **MD 200줄 상한**: 새 문서가 길어지면 분할하고 상호 링크(`[[...]]`/상대경로)로 매핑.
|
||||
- **enum 고정**: 상태·유형은 회사 공통 마스터. 새 값 발견 시 `_meta/*.enum.md` 먼저 갱신.
|
||||
- **출처 명시**: 채운 내용은 원본 파일·코드 경로를 문서에 남긴다.
|
||||
- **디자인 의무**: 생성하는 모든 산출물 MD에 [08-design-system.md](references/08-design-system.md) 배너·뱃지 적용. 텍스트 단독 표기 금지.
|
||||
- **xlsx 재생산**: `references/build_02설계_xlsx.py` + `xlsxfill.py`로 완전 독립 생성(ref 불필요). 스타일 상수는 xlsxfill.py에 하드코딩. `set PYTHONUTF8=1 && py build_02설계_xlsx.py` 한 줄 실행.
|
||||
- **루트 보호**: 레포 루트 `RULES.md` > 본 산출물 `RULES.MD`(스코프 하위).
|
||||
47
.claude/skills/ubisam-deliverables/references/00-harness.md
Normal file
47
.claude/skills/ubisam-deliverables/references/00-harness.md
Normal file
@ -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)).
|
||||
41
.claude/skills/ubisam-deliverables/references/01-tree.md
Normal file
41
.claude/skills/ubisam-deliverables/references/01-tree.md
Normal file
@ -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). 협업관리 일감 제목과 동일 어휘.
|
||||
- 산출물 슬롯: 한글_영문 혼합 가능, 공백 대신 `_`.
|
||||
56
.claude/skills/ubisam-deliverables/references/02-enums.md
Normal file
56
.claude/skills/ubisam-deliverables/references/02-enums.md
Normal file
@ -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 |
|
||||
39
.claude/skills/ubisam-deliverables/references/03-schema.md
Normal file
39
.claude/skills/ubisam-deliverables/references/03-schema.md
Normal file
@ -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).
|
||||
@ -0,0 +1,39 @@
|
||||
# 04 · 6단계 ↔ 산출물 슬롯 매핑
|
||||
|
||||
> 각 단계 폴더 = 협업관리 일감 1개. 슬롯 = 산출물 보관 하위폴더. 비면 `.gitkeep`.
|
||||
|
||||
| # | 폴더 | 유형 | 슬롯(하위폴더) | 산출물 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `01.분석협의/` | 미팅 | `사양서/` `회의록_CR/` `마스터플랜/` | 사양서(URS)·CR회의록·마스터플랜 |
|
||||
| 2 | `02.설계/` | 설계 | `UIUX_기능설계/` `시퀀스_인터록/` `IO_DB설계/` `설계리뷰_DR/` | 기능명세서·시퀀스/인터록·통신/IO맵·DB스키마·D/R회의록 |
|
||||
| 3 | `03.제작/` | 제작 | `테스트결과_CheckList/` | 단위/통합 테스트 결과 보고서 |
|
||||
| 4 | `04.셋업/` | 입고셋업 | `HW_IO모션점검/` `파라미터_레시피백업/` `이슈로그_IssueLog/` `SAT_인수검사보고서/` | IO/모션 점검·파라미터/레시피 백업·이슈로그·SAT 결과서 |
|
||||
| 5 | `05.양산/` | 적용 및 모니터링 | `운영매뉴얼_HMI/` `최종SW_마스터백업/` `프로젝트완료보고서/` | 운영매뉴얼(HMI/알람)·최종SW 백업·완료 보고서 / **`*.pptx`** (ubisam-ppt-make Step C) |
|
||||
| 6 | `06.CS/` | 요청대응 | (슬롯 없음, 건별 누적) | CS 대응 이력 |
|
||||
|
||||
## 단계별 세부 산출물 정의(채움 기준)
|
||||
|
||||
### 1.분석/협의
|
||||
- 요구사항 분석: 고객 사양서(URS) 분석 / 제어 시나리오 검토 → CR회의록 / 마스터플랜.
|
||||
|
||||
### 2.설계
|
||||
- UI/UX·기능: HMI 화면 정의서·UI 스토리보드·기능 명세서.
|
||||
- 시퀀스·인터록: 제어 시퀀스 다이어그램 & 인터록 정의서.
|
||||
- I/O·DB: 통신 맵(Memory/IO/Register)·DB 스키마 정의서.
|
||||
- 설계 리뷰: D/R 회의록.
|
||||
- 양식 시트 골격 → [05-form-schemas.md](05-form-schemas.md).
|
||||
|
||||
### 3.제작
|
||||
- 개발 환경 구축 / 핵심 모듈 / UI·메인 시퀀스 / 시뮬레이션 검증 / 단위·통합 테스트(Check-List).
|
||||
|
||||
### 4.셋업
|
||||
- HW IO/모션 점검 시트 · 파라미터/레시피 백업 · 이슈 로그(Issue Log) · 호스트(MES) 연동 · SAT 결과서.
|
||||
|
||||
### 5.양산
|
||||
- Ramp-up 교육 · 안정화 모니터링(운영 매뉴얼·알람 조치서) · S/W 최적화 · 최종 버전 동결(마스터 백업) · 프로젝트 완료 보고서.
|
||||
|
||||
### 6.CS
|
||||
- 운영 단계 요청 대응 이력(요청/조치/결과) 누적.
|
||||
|
||||
## 타사 산출물 이관 가이드(예)
|
||||
- 제작사양서·시퀀스 → `01`, IO/PLC 인터페이스·기능명세서·FlowChart → `02`, HMI Manual·알람조치 → `05`.
|
||||
@ -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 기준으로 실행.
|
||||
@ -0,0 +1,39 @@
|
||||
# 06 · 분석 → 채움 절차
|
||||
|
||||
> 입력: `산출물문서/양식/`(빈 양식) 또는 타설비 산출물. 출력: 단계 슬롯에 채운 산출물 + 갱신된 `_task.md` + 로그.
|
||||
|
||||
## 절차
|
||||
1. **분석**: 원본 구조 파악(시트/슬라이드/컬럼). 결과를 `[분석]/<yyyy_mm_dd>_<주제>-분석.md`에 기록.
|
||||
- 엑셀 구조 추출(파이썬·Office 없이): `unzip -p file.xlsx xl/workbook.xml`(시트명), `xl/sharedStrings.xml`(라벨).
|
||||
2. **양식 식별**: 타설비 템플릿 잔재 분리. 채울 슬롯 결정 → [04-stage-deliverables.md](04-stage-deliverables.md).
|
||||
- 시트·컬럼 골격은 [05-form-schemas.md](05-form-schemas.md) 기준(ref 없어도 재현 가능).
|
||||
3. **채움**: 이 프로젝트 사실로 값 작성. **출처(원본 파일·코드 경로) 명시**.
|
||||
- 미확정 디바이스주소·축범위는 `(제안)` 표기.
|
||||
4. **디자인 적용**: [08-design-system.md](08-design-system.md) 배너·뱃지·섹션헤더를 삽입한다.
|
||||
- MD 최상단: 배너(§1) → 부제 바 순.
|
||||
- 상태·유형 필드: 텍스트 대신 뱃지(§2·3) 사용.
|
||||
- 진행률: `<progress>` 바(§4) 병기.
|
||||
- 산출물 시트 구분 `##` 헤더: `<span style="color:#2E75B6">` 적용(§5).
|
||||
5. **배치**: 해당 `NN.단계명/<슬롯>/`에 저장.
|
||||
6. **PPTX 생성**: [09-pptx-integration.md](09-pptx-integration.md) 참고.
|
||||
- 2.설계 채움 완료 → `SW_설계서.pptx` (선택)
|
||||
- 5.양산 채움 or 명시 요청 → `<설비명>_HMI_매뉴얼.pptx` (필수)
|
||||
- MD 내용 → JSON spec → `ubisam-ppt-make/build_deck.py` → 슬롯에 저장
|
||||
- 임시 spec JSON 생성 후 pptx 완성 즉시 삭제
|
||||
7. **카드 갱신**: `_task.md`의 상태·진행률·첨부 매핑 갱신(PPTX 파일명 포함, [02-enums.md](02-enums.md)).
|
||||
8. **기록**: `[작업일지]/<오늘>.md`에 한 일·다음 TODO.
|
||||
|
||||
## xlsx 재생산 (from scratch · ref 불필요)
|
||||
- `references/xlsxfill.py` — LG PRI 스타일 상수 하드코딩 + scratch-build API.
|
||||
- `references/build_02설계_xlsx.py` — PtoP 데이터 내장, 4종 xlsx 자동 생성.
|
||||
- 실행: cwd=repo root, `set PYTHONUTF8=1 && py .claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py`
|
||||
- ref xlsx 없어도 동일 외형 재현. 스타일 상수 변경 시 xlsxfill.py만 수정.
|
||||
|
||||
## MD 분할 규칙
|
||||
- 모든 산출/분석 MD는 **200줄 이내**. 초과 시 주제별 분할 + 상호 링크(상대경로)로 매핑.
|
||||
- 인덱스 문서(예: [분석] 폴더 README)에 1줄 포인터로 목록 유지.
|
||||
|
||||
## 자기완결 보장 (ref 없이 동작)
|
||||
- 이 스킬은 양식 골격을 [05-form-schemas.md](05-form-schemas.md)에 내장한다.
|
||||
- `ref/`가 비어 있어도 트리·enum·양식 골격을 동일하게 생성한다.
|
||||
- 새 양식 입수 시: 골격을 추출해 05 파일에 추가 → 이후 ref 없이 재현 가능.
|
||||
@ -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에 추가 후 사용.
|
||||
@ -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
|
||||
<div style="background:#1F4E78;color:#fff;padding:8px 16px;border-radius:4px 4px 0 0">
|
||||
<strong>문서 제목</strong> — 설비명
|
||||
</div>
|
||||
<div style="background:#2E75B6;color:#fff;padding:3px 16px;border-radius:0 0 4px 4px;font-size:.85em;margin-bottom:12px">
|
||||
근거: 소스 파일 경로 · 플랫폼 정보
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 상태 뱃지 (값에 맞춰 색 코드 교체)
|
||||
|
||||
```html
|
||||
<span style="background:#757575;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">해야 할 일</span>
|
||||
<span style="background:#1976D2;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">분석 진행</span>
|
||||
<span style="background:#1565C0;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">업무 진행중</span>
|
||||
<span style="background:#689F38;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">진행 완료</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 유형 뱃지
|
||||
|
||||
```html
|
||||
<span style="background:#00796B;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">미팅</span>
|
||||
<span style="background:#689F38;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">설계</span>
|
||||
<span style="background:#1976D2;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">개발</span>
|
||||
<span style="background:#283593;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">제작</span>
|
||||
<span style="background:#7B1FA2;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">입고셋업</span>
|
||||
<span style="background:#0288D1;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">적용 및 모니터링</span>
|
||||
<span style="background:#1A237E;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">요청대응</span>
|
||||
<span style="background:#D32F2F;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">오류수정</span>
|
||||
<span style="background:#F57C00;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">불합리</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 진행률 바
|
||||
|
||||
```html
|
||||
<progress value="0" max="100" style="width:160px;vertical-align:middle"></progress> **0%**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 섹션 헤더 강조 (산출물 MD 내 시트 구분)
|
||||
|
||||
```markdown
|
||||
## <span style="color:#2E75B6">시트① 제목</span>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 미확정 경고 (GitHub Alerts)
|
||||
|
||||
```markdown
|
||||
> [!NOTE]
|
||||
> 아래 값은 소스 분석 기반 `(제안)`. 현장 확인 후 확정.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. _task.md 완성 템플릿 (디자인 포함)
|
||||
|
||||
단계명·프로젝트명·코드·유형·상태·진행률 토큰 교체 후 사용.
|
||||
|
||||
```markdown
|
||||
<div style="background:#1F4E78;color:#fff;padding:8px 16px;border-radius:4px 4px 0 0">
|
||||
<strong>{N}.{단계명}</strong> — {프로젝트명}
|
||||
</div>
|
||||
<div style="background:#2E75B6;color:#fff;padding:3px 16px;border-radius:0 0 4px 4px;font-size:.85em;margin-bottom:12px">
|
||||
{프로젝트코드}-{일련번호}
|
||||
</div>
|
||||
|
||||
| 필드 | 값 |
|
||||
|---|---|
|
||||
| 유형 | <span style="background:{유형색};color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">{유형명}</span> |
|
||||
| 상태 | <span style="background:#757575;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">해야 할 일</span> |
|
||||
| 담당자 / 참조자 | {담당자} / {참조자} |
|
||||
| 진행률 | <progress value="0" max="100" style="width:160px;vertical-align:middle"></progress> **0%** |
|
||||
| Hashtag | {태그1} / {태그2} |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 적용 규칙
|
||||
- **항상 배너 먼저**: 산출물 MD 생성 시 본문 앞에 배너 블록을 삽입한다.
|
||||
- **뱃지 > 텍스트**: 상태·유형은 텍스트가 아닌 뱃지 HTML로 표기한다.
|
||||
- **진행률 bar 병기**: `<progress>` 태그 + bold 숫자를 함께 쓴다(텍스트 렌더러 폴백).
|
||||
- **섹션 헤더**: 산출물 내 시트 구분 `##` 제목은 `<span style="color:#2E75B6">` 적용.
|
||||
- **README·_meta enum**: 동일 색상 체계를 그대로 적용한다.
|
||||
@ -0,0 +1,96 @@
|
||||
# 09 · PPTX 통합 — ubisam-ppt-make 연동
|
||||
|
||||
> `ubisam-deliverables` Step C에서 호출. MD 산출물 데이터를 JSON spec으로 변환해
|
||||
> `ubisam-ppt-make/build_deck.py`로 PPTX를 생성한다.
|
||||
> 디자인 시스템은 build_deck.py 마스터 배경 포함 자동 적용.
|
||||
|
||||
---
|
||||
|
||||
## 생성 타이밍
|
||||
|
||||
| 트리거 | 생성 PPTX | 저장 슬롯 |
|
||||
|---|---|---|
|
||||
| 2.설계 채움 완료 | SW 설계서 (선택) | `02.설계/설계리뷰_DR/SW_설계서.pptx` |
|
||||
| 5.양산 채움 or 명시 요청 | 운영매뉴얼_HMI | `05.양산/운영매뉴얼_HMI/<설비명>_HMI_매뉴얼.pptx` |
|
||||
| 명시 요청 ("pptx 만들어줘") | 해당 단계 산출물 | 해당 슬롯 |
|
||||
|
||||
---
|
||||
|
||||
## 실행 절차
|
||||
|
||||
```
|
||||
1. 해당 단계 MD 파일 읽기
|
||||
2. 아래 매핑표로 slide 오브젝트 목록 작성
|
||||
3. JSON spec 생성 → 스크래치 파일(임시)로 저장
|
||||
4. python 실행:
|
||||
py .claude/skills/ubisam-ppt-make/build_deck.py \
|
||||
--input <spec>.json --output <슬롯경로>.pptx
|
||||
5. 임시 spec 파일 삭제
|
||||
6. _task.md 첨부 목록 갱신
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A. 2.설계 → SW 설계서 (선택)
|
||||
|
||||
소스 MD → slide 타입 매핑:
|
||||
|
||||
| MD 소스 | slide type | 비고 |
|
||||
|---|---|---|
|
||||
| — | `cover` | doc_tag="SW DESIGN DOCUMENT" |
|
||||
| — | `history` | 버전/변경이력 (사용자 입력 or 빈 행) |
|
||||
| 각 산출물 목록 | `agenda` | 최대 6항목 |
|
||||
| 기능명세서 §메뉴구조 | `table` | headers: 메인메뉴/권한/서브뷰/기능 |
|
||||
| 기능명세서 §View별기능 | `table` | View별 1슬라이드씩 |
|
||||
| 시퀀스_인터록_정의서 §mermaid 노드 | `flow` | steps: 노드명+코드+kind |
|
||||
| PLC신호정의서 §PC→PLC | `table` | headers: No/신호명/디바이스/Type/의미 |
|
||||
| PLC신호정의서 §PLC→PC | `table` | 동일 구조 |
|
||||
| 모션축_IO맵 §통신구성 | `table` | |
|
||||
| 모션축_IO맵 §I/O맵 DI/DO | `table` | |
|
||||
| TactTime §PC TactTime | `table` | |
|
||||
| — | `closing` | |
|
||||
|
||||
---
|
||||
|
||||
## B. 5.양산 → 운영매뉴얼_HMI (필수)
|
||||
|
||||
| MD/소스 | slide type | 비고 |
|
||||
|---|---|---|
|
||||
| — | `cover` | doc_tag="HMI OPERATION MANUAL" |
|
||||
| — | `history` | |
|
||||
| View 목록 | `agenda` | AutoRun/Recipe/Manual/Calibration/Monitoring/Alarm |
|
||||
| 각 View | `divider` | View명 섹션 구분 |
|
||||
| 각 View §기능목록 | `screen_guide` | image 없으면 caption만 |
|
||||
| 각 View §조작절차 | `procedure` | steps: title+desc |
|
||||
| 알람 목록 | `alarm` | rows: 코드/알람명/원인/조치 |
|
||||
| — | `closing` | |
|
||||
|
||||
---
|
||||
|
||||
## spec JSON 최소 골격
|
||||
|
||||
```json
|
||||
{
|
||||
"doc_title": "{설비명} SW 설계서",
|
||||
"slides": [
|
||||
{"type": "cover", "tag": "SW DESIGN DOCUMENT",
|
||||
"title": "{설비명} SW 설계서",
|
||||
"subtitles": ["{설비 설명}"],
|
||||
"team": "로봇자동화사업부",
|
||||
"contacts": ["{담당자}"]},
|
||||
{"type": "history", "rows": []},
|
||||
{"type": "agenda", "items": []},
|
||||
{"type": "closing", "title": "Thank You",
|
||||
"team": "로봇자동화사업부", "contacts": []}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> `background_color` 키 생략 → build_deck.py 기본값 `#F4F7FA` 자동 적용.
|
||||
|
||||
---
|
||||
|
||||
## 불변 규칙
|
||||
- spec JSON은 임시파일로 생성 후 pptx 완성 즉시 삭제 (작업 완료 시 정리).
|
||||
- PPTX 저장 후 `_task.md` 첨부 목록에 파일명 추가.
|
||||
- `ubisam-ppt-make/SKILL.md` §Verification 조건부 검증 권장 (PowerPoint 설치 시).
|
||||
479
.claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py
Normal file
479
.claude/skills/ubisam-deliverables/references/build_02설계_xlsx.py
Normal file
@ -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 <repo_root> # 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 파일 불필요).")
|
||||
203
.claude/skills/ubisam-deliverables/references/xlsxfill.py
Normal file
203
.claude/skills/ubisam-deliverables/references/xlsxfill.py
Normal file
@ -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
|
||||
@ -0,0 +1,32 @@
|
||||
<div style="background:#1F4E78;color:#fff;padding:8px 16px;border-radius:4px 4px 0 0">
|
||||
<strong>{N}.{단계명}</strong> — {프로젝트명}
|
||||
</div>
|
||||
<div style="background:#2E75B6;color:#fff;padding:3px 16px;border-radius:0 0 4px 4px;font-size:.85em;margin-bottom:12px">
|
||||
{프로젝트코드}-{일련번호}
|
||||
</div>
|
||||
|
||||
| 필드 | 값 |
|
||||
|---|---|
|
||||
| 유형 | <span style="background:{유형색};color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">{유형명}</span> |
|
||||
| 상태 | <span style="background:#757575;color:#fff;padding:1px 10px;border-radius:3px;font-size:.85em">해야 할 일</span> |
|
||||
| 담당자 / 참조자 | {담당자} / {참조자} |
|
||||
| 진행률 | <progress value="0" max="100" style="width:160px;vertical-align:middle"></progress> **0%** |
|
||||
| Hashtag | {태그1} / {태그2} |
|
||||
|
||||
## 산출물 정의
|
||||
> 세부 × 산출물 × 슬롯 표. 단계별 산출물은 04-stage-deliverables 참조.
|
||||
|
||||
| 세부 | 산출물 | 슬롯 |
|
||||
|---|---|---|
|
||||
| {세부} | {산출물} | `{슬롯}/` |
|
||||
|
||||
## 첨부 / 템플릿
|
||||
- {파일명 또는 양식 템플릿 위치}
|
||||
|
||||
<!--
|
||||
사용법:
|
||||
- {…} 토큰을 프로젝트 값으로 교체.
|
||||
- 유형색: 08-design-system.md §색상 팔레트에서 해당 유형 색 코드 사용.
|
||||
- 상태/유형 값은 02-enums.md enum만 사용.
|
||||
- 양식 시트 골격이 필요하면 05-form-schemas.md 참조.
|
||||
-->
|
||||
@ -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
|
||||
78
.claude/skills/ubisam-format-map/SKILL.md
Normal file
78
.claude/skills/ubisam-format-map/SKILL.md
Normal file
@ -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`.
|
||||
|
||||
> 재생산 산출물의 **출처(코드 경로/커밋)**를 항상 명시한다 — 검증가가 근거 추적에 사용한다.
|
||||
134
.claude/skills/ubisam-ppt-make/SKILL.md
Normal file
134
.claude/skills/ubisam-ppt-make/SKILL.md
Normal file
@ -0,0 +1,134 @@
|
||||
---
|
||||
name: ubisam-ppt-make
|
||||
description: Build a .pptx deck in the Ubisam SW 설계서 design system (extracted from P-PRJ_BFHB_UBISAM). UbiSam logo on every page, fixed fonts (한글=굴림, 영문=Arial), blue #4A8DCB accent. Use when the user wants a Ubisam-style design document deck or runs /ubisam-ppt-make.
|
||||
---
|
||||
|
||||
# ubisam-ppt-make — Ubisam SW 설계서 deck builder
|
||||
|
||||
Deterministic PPT builder following the design system extracted from
|
||||
`P-PRJ_BFHB_UBISAM_Ver1.4.pptx` and the user's edited layout showcase
|
||||
(UbiSam logo placed on every page). Takes a JSON spec and emits a `.pptx`
|
||||
via python-pptx.
|
||||
|
||||
## When to use
|
||||
|
||||
- The user types `/ubisam-ppt-make`
|
||||
- Requests like "Ubisam 설계서 PPT 만들어줘" / "유비삼 스타일 덱" / "this format으로"
|
||||
- New design-document presentations that must keep Ubisam branding
|
||||
|
||||
## Design system (fixed)
|
||||
|
||||
- Slide: 16:9 (13.333 × 7.5 in)
|
||||
- **슬라이드 마스터 배경** (모든 pptx 자동 적용):
|
||||
- 캔버스: `BG_SOFT` `#F4F7FA` (연회색) — `_apply_master_background()` 에서 마스터+레이아웃 전체 설정
|
||||
- 하단 accent 바: `#4A8DCB` h=0.15in @ y=7.35in (마스터 공통)
|
||||
- cover·divider·closing은 자체 full rect로 덮음; 나머지 레이아웃은 상속
|
||||
- spec JSON에서 `"background_color": "RRGGBB"` 로 재정의 가능
|
||||
- Accent: `#4A8DCB` (blue), dark `#2F5E8C`; warn `#E53E3E`
|
||||
- Ink `#262A2E`, sub-gray `#6B7278`, soft panel `#F4F7FA`
|
||||
- **Font rule (do not change): 한글=굴림, 영문=Arial** — applied to every run
|
||||
- **UbiSam logo on every page**: cover = bottom-right, all others = top-right
|
||||
- Header pattern: `0X.` badge + title + KO subtitle + split accent bar
|
||||
- Footer: `Ubisam | <doc_title>` + page number (content pages only)
|
||||
|
||||
## 12 layout types (`slide.type`)
|
||||
|
||||
Document layouts:
|
||||
|
||||
| type | purpose | key fields |
|
||||
|------|---------|-----------|
|
||||
| `cover` | 표지 | `tag, title, subtitles[], team, contacts[]` |
|
||||
| `history` | 변경 이력 표 | `rows[] = [No,Version,날짜,변경자,내용]` |
|
||||
| `agenda` | 목차 그리드(≤6) | `items[] = {no,title,sub}` |
|
||||
| `divider` | 섹션 간지(큰 숫자) | `no, title, subtitle` |
|
||||
| `content` | 4분할 패널 | `no, title, subtitle, panels[] = {title,body}` |
|
||||
| `flow` | 시퀀스 플로우차트 | `no, title, subtitle, steps[]={title,code,kind}, branch{title,sub}` |
|
||||
| `table` | 스펙/IO 표 | `no, title, headers[], widths[], center_cols[], rows[][]` |
|
||||
| `closing` | 마무리 | `title, message, team, contacts[]` |
|
||||
|
||||
Manual layouts (HMI Manual / 27번):
|
||||
|
||||
| type | purpose | key fields |
|
||||
|------|---------|-----------|
|
||||
| `screen_guide` | 화면 설명(캡처+번호 콜아웃) | `no, title, caption, image(PNG경로), items[]={title,desc,pos:[x,y]}` |
|
||||
| `procedure` | 조작 절차(번호 단계) | `no, title, steps[]={title,desc}` |
|
||||
| `icon_ref` | 버튼/아이콘 설명(3열 카드, ≤6) | `no, title, items[]={icon,name,func}` |
|
||||
| `alarm` | 알람·조치(주의 배너+표) | `no, title, notice, rows[]=[코드,알람명,원인,조치]` |
|
||||
|
||||
`kind` for flow steps: `"proc"` (process box) or `"dec"` (decision, blue outline).
|
||||
`pos` in `screen_guide` items = callout marker offset (inch) inside the capture box.
|
||||
Page numbers auto-increment over content/manual pages; cover/divider/closing excluded.
|
||||
`divider`/`closing` use a white background with a `#0070C0` / `#2F5E8C` block.
|
||||
|
||||
## Choosing a layout for each piece of data
|
||||
|
||||
Match every chunk of source/analysis data to the layout whose intent fits it —
|
||||
do not force everything into `table`. Full rules + disambiguation in
|
||||
[`reference/layout_selection.md`](reference/layout_selection.md). Cheat sheet:
|
||||
|
||||
- ordered **human** steps → `procedure` · machine sequence **with branches** → `flow`
|
||||
- 2–4 facets, one line each → `content` · repeating rows/cols → `table`
|
||||
- annotate ONE screenshot → `screen_guide` · button/icon legend → `icon_ref`
|
||||
- fault → cause → action (+safety) → `alarm` · revision log → `history`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Gather info** — topic, doc title, sections, slide contents, contacts.
|
||||
If the user gives no content, build the sample showcase (all layouts).
|
||||
2. **Classify each data chunk → layout** using the guide above, then
|
||||
**write the spec JSON** — see `examples/showcase.json` and the type tables.
|
||||
Reuse the original BFHB sample text only when the user wants the showcase.
|
||||
3. **Run the builder**:
|
||||
```bash
|
||||
cd <skill-dir>
|
||||
python build_deck.py --input spec.json --output "생성파일/<name>.pptx"
|
||||
# or, sample showcase:
|
||||
python build_deck.py --output "생성파일/Ubisam_layout_showcase.pptx"
|
||||
```
|
||||
4. **Verify** — render to PNG and eyeball every slide (see Verification).
|
||||
5. **Report** — output path + slide summary.
|
||||
|
||||
## Spec schema (minimal)
|
||||
|
||||
```json
|
||||
{
|
||||
"doc_title": "P-PRJ BFHB SW 설계서",
|
||||
"slides": [
|
||||
{ "type": "cover", "title": "...", "subtitles": ["..."],
|
||||
"team": "시스템 사업팀", "contacts": ["이름 직급 email"] },
|
||||
{ "type": "divider", "no": "01", "title": "...", "subtitle": "..." },
|
||||
{ "type": "content", "no": "02.", "title": "...", "subtitle": "...",
|
||||
"panels": [ {"title": "...", "body": "..."} ] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
A full working spec is in [`examples/showcase.json`](examples/showcase.json).
|
||||
|
||||
## Verification (required, project rule 13)
|
||||
|
||||
Export every slide to PNG and inspect; check no overflow/overlap, logo on
|
||||
every page, and 한글=굴림 / 영문=Arial on all runs.
|
||||
|
||||
```powershell
|
||||
# PowerPoint COM export (Windows, PowerPoint installed)
|
||||
$ppt = New-Object -ComObject PowerPoint.Application
|
||||
$pres = $ppt.Presentations.Open($pptx, $true, $false, $false)
|
||||
$i=1; foreach ($s in $pres.Slides) { $s.Export("$dir/slide$i.png","PNG",1280,720); $i++ }
|
||||
$pres.Close(); $ppt.Quit()
|
||||
```
|
||||
|
||||
Font audit (every `rPr` must show `ea=굴림`, `latin=Arial`):
|
||||
```bash
|
||||
python -c "import zipfile,re;from collections import Counter;z=zipfile.ZipFile('deck.pptx');\
|
||||
[print(Counter(re.findall(r'<a:ea typeface=\"([^\"]*)\"',z.read(n).decode('utf-8','ignore')))) \
|
||||
for n in z.namelist() if re.match(r'ppt/slides/slide\\d+',n)]"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- python-pptx sets only the latin font via `run.font.name`; `_set_font()` also
|
||||
writes `<a:ea>`/`<a:cs>` so Korean renders in 굴림. Always go through it.
|
||||
- Logo asset: `assets/Ubisam_logo_transparent.png`. On dark divider/closing the
|
||||
colored logo has lower contrast but matches the user's original placement.
|
||||
- Keep `.md` files under 200 lines (project rule 4); split if it grows.
|
||||
BIN
.claude/skills/ubisam-ppt-make/assets/Ubisam_logo.gif
Normal file
BIN
.claude/skills/ubisam-ppt-make/assets/Ubisam_logo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
739
.claude/skills/ubisam-ppt-make/build_deck.py
Normal file
739
.claude/skills/ubisam-ppt-make/build_deck.py
Normal file
@ -0,0 +1,739 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
ubisam-ppt-make — Ubisam SW 설계서 deck builder.
|
||||
|
||||
Design system extracted from P-PRJ_BFHB_UBISAM_Ver1.4.pptx and the user's
|
||||
edited layout_showcase (UbiSam logo on every page).
|
||||
|
||||
Font rule (fixed): 한글=굴림, 영문=Arial.
|
||||
|
||||
Usage:
|
||||
python build_deck.py # build sample showcase
|
||||
python build_deck.py --input spec.json --output deck.pptx
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
from pptx.dml.color import RGBColor
|
||||
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
||||
from pptx.enum.shapes import MSO_SHAPE
|
||||
from pptx.oxml.ns import qn
|
||||
from pptx.parts.image import ImagePart
|
||||
from pptx.opc.constants import RELATIONSHIP_TYPE as RT
|
||||
from pptx.opc.packuri import PackURI
|
||||
from lxml import etree
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
LOGO = os.path.join(HERE, "assets", "Ubisam_logo_transparent.png")
|
||||
|
||||
# ---- design tokens ----
|
||||
ACCENT = RGBColor(0x4A, 0x8D, 0xCB)
|
||||
ACCENT_D = RGBColor(0x2F, 0x5E, 0x8C)
|
||||
INK = RGBColor(0x26, 0x2A, 0x2E)
|
||||
SUB = RGBColor(0x6B, 0x72, 0x78)
|
||||
LINE = RGBColor(0xD9, 0xDD, 0xE1)
|
||||
BG_SOFT = RGBColor(0xF4, 0xF7, 0xFA)
|
||||
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
||||
WARN = RGBColor(0xE5, 0x3E, 0x3E)
|
||||
PALE = RGBColor(0xDD, 0xEA, 0xF6)
|
||||
BLUE2 = RGBColor(0x00, 0x70, 0xC0) # divider/closing block blue
|
||||
|
||||
KFONT = "굴림"
|
||||
EFONT = "Arial"
|
||||
|
||||
EMU_W = Inches(13.333)
|
||||
EMU_H = Inches(7.5)
|
||||
|
||||
_NS_P = "http://schemas.openxmlformats.org/presentationml/2006/main"
|
||||
_NS_A = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||
_NS_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||
|
||||
|
||||
# ---------- master background ----------
|
||||
def _apply_master_background(prs):
|
||||
"""슬라이드 마스터에 흰 배경 + Ubisam 로고 워터마크 + 하단 accent 바 적용.
|
||||
모든 슬라이드가 상속. cover·divider·closing은 자체 rect로 덮음."""
|
||||
master = prs.slide_master
|
||||
spTree = master.shapes._spTree
|
||||
|
||||
# 1. 배경: WHITE
|
||||
try:
|
||||
master.background.fill.solid()
|
||||
master.background.fill.fore_color.rgb = WHITE
|
||||
except Exception:
|
||||
pass
|
||||
for layout in prs.slide_layouts:
|
||||
try:
|
||||
layout.background.fill.solid()
|
||||
layout.background.fill.fore_color.rgb = WHITE
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. Ubisam 로고 워터마크 (하단 전체 폭, 15% 투명도)
|
||||
if os.path.exists(LOGO):
|
||||
try:
|
||||
with open(LOGO, "rb") as f:
|
||||
blob = f.read()
|
||||
pkg = master.part._package
|
||||
img_part = ImagePart(
|
||||
PackURI("/ppt/media/ubisam_master_wm.png"), "image/png", pkg, blob
|
||||
)
|
||||
rId = master.part.relate_to(img_part, RT.IMAGE)
|
||||
W, H = int(EMU_W), int(Inches(2.5))
|
||||
T = int(EMU_H) - H
|
||||
wm_xml = (
|
||||
f'<p:pic xmlns:p="{_NS_P}" xmlns:a="{_NS_A}" xmlns:r="{_NS_R}">'
|
||||
f'<p:nvPicPr>'
|
||||
f'<p:cNvPr id="9002" name="UbisamWatermark"/>'
|
||||
f'<p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr>'
|
||||
f'<p:nvPr/></p:nvPicPr>'
|
||||
f'<p:blipFill>'
|
||||
f'<a:blip r:embed="{rId}"><a:alphaModFix amt="15000"/></a:blip>'
|
||||
f'<a:stretch><a:fillRect/></a:stretch>'
|
||||
f'</p:blipFill>'
|
||||
f'<p:spPr>'
|
||||
f'<a:xfrm><a:off x="0" y="{T}"/>'
|
||||
f'<a:ext cx="{W}" cy="{H}"/></a:xfrm>'
|
||||
f'<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>'
|
||||
f'<a:noFill/>'
|
||||
f'</p:spPr>'
|
||||
f'</p:pic>'
|
||||
)
|
||||
spTree.append(etree.fromstring(wm_xml.encode("utf-8")))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. 하단 accent 바
|
||||
hex_c = f"{ACCENT[0]:02X}{ACCENT[1]:02X}{ACCENT[2]:02X}"
|
||||
bar_xml = (
|
||||
f'<p:sp xmlns:p="{_NS_P}" xmlns:a="{_NS_A}" xmlns:r="{_NS_R}">'
|
||||
f'<p:nvSpPr><p:cNvPr id="9001" name="AccentBar"/>'
|
||||
f'<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr/></p:nvSpPr>'
|
||||
f'<p:spPr>'
|
||||
f'<a:xfrm><a:off x="0" y="{int(Inches(7.35))}"/>'
|
||||
f'<a:ext cx="{int(EMU_W)}" cy="{int(Inches(0.15))}"/></a:xfrm>'
|
||||
f'<a:prstGeom prst="rect"><a:avLst/></a:prstGeom>'
|
||||
f'<a:solidFill><a:srgbClr val="{hex_c}"/></a:solidFill>'
|
||||
f'<a:ln><a:noFill/></a:ln>'
|
||||
f'</p:spPr>'
|
||||
f'<p:txBody><a:bodyPr/><a:lstStyle/><a:p/></p:txBody>'
|
||||
f'</p:sp>'
|
||||
)
|
||||
try:
|
||||
spTree.append(etree.fromstring(bar_xml.encode("utf-8")))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# ---------- text / shape helpers ----------
|
||||
def _set_font(run, size=None, bold=None, color=None, italic=None):
|
||||
f = run.font
|
||||
if size is not None:
|
||||
f.size = Pt(size)
|
||||
if bold is not None:
|
||||
f.bold = bold
|
||||
if italic is not None:
|
||||
f.italic = italic
|
||||
if color is not None:
|
||||
f.color.rgb = color
|
||||
f.name = EFONT
|
||||
rPr = run._r.get_or_add_rPr()
|
||||
for tag in ("a:ea", "a:cs"):
|
||||
el = rPr.find(qn(tag))
|
||||
if el is None:
|
||||
el = rPr.makeelement(qn(tag), {})
|
||||
rPr.append(el)
|
||||
el.set("typeface", KFONT if tag == "a:ea" else EFONT)
|
||||
|
||||
|
||||
def add_text(slide, l, t, w, h, lines, align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP):
|
||||
tb = slide.shapes.add_textbox(l, t, w, h)
|
||||
tf = tb.text_frame
|
||||
tf.word_wrap = True
|
||||
tf.vertical_anchor = anchor
|
||||
for m in ("margin_left", "margin_right", "margin_top", "margin_bottom"):
|
||||
setattr(tf, m, 0)
|
||||
for i, spec in enumerate(lines):
|
||||
text, size, bold, color = spec[0], spec[1], spec[2], spec[3]
|
||||
a = spec[4] if len(spec) > 4 else align
|
||||
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
|
||||
p.alignment = a
|
||||
r = p.add_run()
|
||||
r.text = text
|
||||
_set_font(r, size=size, bold=bold, color=color)
|
||||
return tb
|
||||
|
||||
|
||||
def rect(slide, l, t, w, h, fill=None, line_color=None, line_w=None,
|
||||
shape=MSO_SHAPE.RECTANGLE):
|
||||
sp = slide.shapes.add_shape(shape, l, t, w, h)
|
||||
sp.shadow.inherit = False
|
||||
if fill is None:
|
||||
sp.fill.background()
|
||||
else:
|
||||
sp.fill.solid()
|
||||
sp.fill.fore_color.rgb = fill
|
||||
if line_color is None:
|
||||
sp.line.fill.background()
|
||||
else:
|
||||
sp.line.color.rgb = line_color
|
||||
sp.line.width = Pt(line_w or 0.75)
|
||||
if sp.has_text_frame:
|
||||
sp.text_frame.word_wrap = True
|
||||
return sp
|
||||
|
||||
|
||||
def blank_slide(prs):
|
||||
return prs.slides.add_slide(prs.slide_layouts[6])
|
||||
|
||||
|
||||
def add_logo(slide, where="top", left=None, top=None, height=None):
|
||||
"""UbiSam logo on every page. cover=bottom-right, others=top-right.
|
||||
left/top/height override the top-right default (in inches)."""
|
||||
if not os.path.exists(LOGO):
|
||||
return
|
||||
if where == "bottom":
|
||||
slide.shapes.add_picture(LOGO, Inches(10.55), Inches(6.35), height=Inches(0.62))
|
||||
else:
|
||||
slide.shapes.add_picture(LOGO, Inches(10.55 if left is None else left),
|
||||
Inches(0.5 if top is None else top),
|
||||
height=Inches(0.55 if height is None else height))
|
||||
|
||||
|
||||
def header(slide, no, title_en, title_ko=None):
|
||||
if no:
|
||||
rect(slide, Inches(0.55), Inches(0.42), Inches(0.62), Inches(0.46), fill=ACCENT)
|
||||
add_text(slide, Inches(0.55), Inches(0.42), Inches(0.62), Inches(0.46),
|
||||
[(no, 16, True, WHITE, PP_ALIGN.CENTER)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
tx = 1.32 if no else 0.55
|
||||
add_text(slide, Inches(tx), Inches(0.40), Inches(8.6), Inches(0.5),
|
||||
[(title_en, 22, True, INK)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
if title_ko:
|
||||
add_text(slide, Inches(tx + 0.02), Inches(0.86), Inches(8.6), Inches(0.32),
|
||||
[(title_ko, 12, False, SUB)])
|
||||
rect(slide, Inches(0.55), Inches(1.22), Inches(11.4), Inches(0.035), fill=ACCENT)
|
||||
rect(slide, Inches(0.55), Inches(1.22), Inches(2.2), Inches(0.035), fill=ACCENT_D)
|
||||
|
||||
|
||||
def footer(slide, page, doc_title):
|
||||
add_text(slide, Inches(0.55), Inches(7.05), Inches(7.0), Inches(0.3),
|
||||
[("Ubisam | " + doc_title, 9, False, SUB)])
|
||||
if page is not None:
|
||||
add_text(slide, Inches(11.0), Inches(7.05), Inches(1.78), Inches(0.3),
|
||||
[(str(page), 9, False, SUB, PP_ALIGN.RIGHT)])
|
||||
|
||||
|
||||
def num_badge(slide, l, t, dia, n, fill=ACCENT, color=WHITE, size=14):
|
||||
rect(slide, Inches(l), Inches(t), Inches(dia), Inches(dia), fill=fill, shape=MSO_SHAPE.OVAL)
|
||||
add_text(slide, Inches(l), Inches(t), Inches(dia), Inches(dia),
|
||||
[(str(n), size, True, color, PP_ALIGN.CENTER)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
|
||||
|
||||
# ---------- layouts ----------
|
||||
def L_cover(prs, d):
|
||||
s = blank_slide(prs)
|
||||
rect(s, 0, 0, EMU_W, EMU_H, fill=WHITE)
|
||||
rect(s, 0, 0, Inches(0.28), EMU_H, fill=ACCENT)
|
||||
rect(s, Inches(0.28), 0, Inches(0.06), EMU_H, fill=ACCENT_D)
|
||||
rect(s, Inches(0.9), Inches(1.7), Inches(2.4), Inches(0.42), fill=BG_SOFT)
|
||||
add_text(s, Inches(0.9), Inches(1.7), Inches(2.4), Inches(0.42),
|
||||
[(d.get("tag", "SW DESIGN DOCUMENT"), 11, True, ACCENT, PP_ALIGN.CENTER)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_text(s, Inches(0.9), Inches(2.35), Inches(10.0), Inches(1.2),
|
||||
[(d["title"], 40, True, INK)])
|
||||
subs = d.get("subtitles", [])
|
||||
add_text(s, Inches(0.92), Inches(3.55), Inches(10.5), Inches(0.9),
|
||||
[(t, 15 if i == 0 else 12, False, SUB) for i, t in enumerate(subs)])
|
||||
rect(s, Inches(0.92), Inches(4.7), Inches(3.2), Inches(0.03), fill=LINE)
|
||||
contacts = d.get("contacts", [])
|
||||
cl = [(d.get("team", "시스템 사업팀"), 13, True, INK)]
|
||||
cl += [(c, 11, False, SUB) for c in contacts]
|
||||
add_text(s, Inches(0.92), Inches(4.95), Inches(8.0), Inches(1.4), cl)
|
||||
add_logo(s, "bottom")
|
||||
return s
|
||||
|
||||
|
||||
def L_history(prs, d):
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "00."), d.get("title", "변경 이력"), d.get("subtitle", "Revision History"))
|
||||
cols = [("No.", 0.55, 0.55), ("Version", 1.4, 1.1), ("변경일자", 2.5, 1.5),
|
||||
("변경자", 4.0, 1.3), ("변경 내용", 5.3, 6.65)]
|
||||
rows = d.get("rows", [])
|
||||
top, rh = 1.7, 0.5
|
||||
rect(s, Inches(0.55), Inches(top), Inches(11.4), Inches(rh), fill=ACCENT)
|
||||
for label, x, w in cols:
|
||||
add_text(s, Inches(x), Inches(top), Inches(w), Inches(rh),
|
||||
[(label, 12, True, WHITE, PP_ALIGN.CENTER)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
for i, row in enumerate(rows):
|
||||
y = top + rh + i * rh
|
||||
if i % 2 == 1:
|
||||
rect(s, Inches(0.55), Inches(y), Inches(11.4), Inches(rh), fill=BG_SOFT)
|
||||
for (label, x, w), val in zip(cols, row):
|
||||
al = PP_ALIGN.LEFT if label == "변경 내용" else PP_ALIGN.CENTER
|
||||
xx = x + 0.1 if label == "변경 내용" else x
|
||||
add_text(s, Inches(xx), Inches(y), Inches(w), Inches(rh),
|
||||
[(str(val), 11, False, INK, al)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
rect(s, Inches(0.55), Inches(y + rh), Inches(11.4), Inches(0.012), fill=LINE)
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_agenda(prs, d):
|
||||
s = blank_slide(prs)
|
||||
header(s, "", d.get("title", "Contents"), d.get("subtitle", "목차"))
|
||||
items = d.get("items", [])
|
||||
cw, ch = 5.55, 1.45
|
||||
gx, gy = 0.55, 1.75
|
||||
for i, it in enumerate(items[:6]):
|
||||
r, c = divmod(i, 2)
|
||||
x = gx + c * (cw + 0.3)
|
||||
y = gy + r * (ch + 0.25)
|
||||
rect(s, Inches(x), Inches(y), Inches(cw), Inches(ch), fill=BG_SOFT)
|
||||
rect(s, Inches(x), Inches(y), Inches(0.08), Inches(ch), fill=ACCENT)
|
||||
add_text(s, Inches(x + 0.25), Inches(y + 0.18), Inches(1.2), Inches(1.0),
|
||||
[(it.get("no", ""), 34, True, ACCENT)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_text(s, Inches(x + 1.55), Inches(y + 0.3), Inches(cw - 1.7), Inches(0.9),
|
||||
[(it.get("title", ""), 17, True, INK), (it.get("sub", ""), 11, False, SUB)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_divider(prs, d):
|
||||
s = blank_slide(prs)
|
||||
rect(s, 0, 0, EMU_W, EMU_H, fill=WHITE)
|
||||
rect(s, 0, Inches(3.88), EMU_W, Inches(3.62), fill=BLUE2)
|
||||
add_text(s, Inches(0.9), Inches(1.3), Inches(6.0), Inches(2.2),
|
||||
[(d.get("no", "01"), 130, True, BLUE2)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
rect(s, Inches(0.95), Inches(4.05), Inches(2.4), Inches(0.06), fill=WHITE)
|
||||
add_text(s, Inches(0.95), Inches(4.25), Inches(10.0), Inches(0.84),
|
||||
[(d.get("title", ""), 30, True, WHITE), (d.get("subtitle", ""), 20, False, PALE)])
|
||||
add_logo(s, left=11.3, top=0.55, height=0.5)
|
||||
return s
|
||||
|
||||
|
||||
def L_content(prs, d):
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "02."), d.get("title", ""), d.get("subtitle"))
|
||||
panels = d.get("panels", [])
|
||||
pw, ph = 5.55, 2.35
|
||||
gx, gy = 0.55, 1.65
|
||||
for i, p in enumerate(panels[:4]):
|
||||
r, c = divmod(i, 2)
|
||||
x = gx + c * (pw + 0.3)
|
||||
y = gy + r * (ph + 0.25)
|
||||
rect(s, Inches(x), Inches(y), Inches(pw), Inches(ph), fill=WHITE, line_color=LINE, line_w=1.0)
|
||||
rect(s, Inches(x), Inches(y), Inches(pw), Inches(0.42), fill=BG_SOFT)
|
||||
add_text(s, Inches(x + 0.2), Inches(y), Inches(pw - 0.4), Inches(0.42),
|
||||
[(p.get("title", ""), 13, True, ACCENT_D)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_text(s, Inches(x + 0.2), Inches(y + 0.42), Inches(pw - 0.4), Inches(ph - 0.42),
|
||||
[("〔 " + p.get("body", "") + " 〕", 11, False, SUB, PP_ALIGN.CENTER)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_flow(prs, d):
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "03."), d.get("title", "Sequence Flowchart"), d.get("subtitle"))
|
||||
steps = d.get("steps", [])
|
||||
branch = d.get("branch")
|
||||
cx, bw = 4.55, 3.0
|
||||
y, gap = 1.6, 0.46
|
||||
prev_bottom, dec_y = None, None
|
||||
for title, code, kind in [(s2.get("title", ""), s2.get("code", ""), s2.get("kind", "proc")) for s2 in steps]:
|
||||
h = 0.58 if kind == "dec" else 0.52
|
||||
if kind == "dec" and dec_y is None:
|
||||
dec_y = y
|
||||
lc = ACCENT if kind == "dec" else SUB
|
||||
rect(s, Inches(cx), Inches(y), Inches(bw), Inches(h), fill=WHITE,
|
||||
line_color=lc, line_w=1.5 if kind == "dec" else 1.0,
|
||||
shape=MSO_SHAPE.ROUNDED_RECTANGLE)
|
||||
add_text(s, Inches(cx), Inches(y + 0.04), Inches(bw), Inches(h - 0.04),
|
||||
[(title, 12, True, INK, PP_ALIGN.CENTER), (code, 9, False, ACCENT_D, PP_ALIGN.CENTER)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
if prev_bottom is not None:
|
||||
ln = s.shapes.add_connector(2, Inches(cx + bw / 2), Inches(prev_bottom),
|
||||
Inches(cx + bw / 2), Inches(y))
|
||||
ln.line.color.rgb = ACCENT
|
||||
ln.line.width = Pt(1.5)
|
||||
prev_bottom = y + h
|
||||
y += h + gap
|
||||
if branch and dec_y is not None:
|
||||
bh = 0.58
|
||||
by = dec_y
|
||||
rect(s, Inches(1.7), Inches(by), Inches(1.9), Inches(bh), fill=BG_SOFT,
|
||||
line_color=SUB, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE)
|
||||
add_text(s, Inches(1.7), Inches(by), Inches(1.9), Inches(bh),
|
||||
[(branch.get("title", ""), 11, True, WARN, PP_ALIGN.CENTER),
|
||||
(branch.get("sub", ""), 9, False, SUB, PP_ALIGN.CENTER)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
ln = s.shapes.add_connector(2, Inches(cx), Inches(by + bh / 2), Inches(3.6), Inches(by + bh / 2))
|
||||
ln.line.color.rgb = WARN
|
||||
ln.line.width = Pt(1.25)
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def _fill_cell(cell, text, size, bold, color, center=False):
|
||||
cell.vertical_anchor = MSO_ANCHOR.MIDDLE
|
||||
cell.margin_left = Inches(0.08)
|
||||
cell.margin_right = Inches(0.08)
|
||||
cell.margin_top = Inches(0.02)
|
||||
cell.margin_bottom = Inches(0.02)
|
||||
tf = cell.text_frame
|
||||
tf.word_wrap = True
|
||||
p = tf.paragraphs[0]
|
||||
p.alignment = PP_ALIGN.CENTER if center else PP_ALIGN.LEFT
|
||||
r = p.add_run()
|
||||
r.text = str(text)
|
||||
_set_font(r, size=size, bold=bold, color=color)
|
||||
|
||||
|
||||
def L_table(prs, d):
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "06."), d.get("title", ""), d.get("subtitle"))
|
||||
headers = d.get("headers", [])
|
||||
widths = d.get("widths") or [11.4 / len(headers)] * len(headers)
|
||||
data = d.get("rows", [])
|
||||
center_cols = set(d.get("center_cols", []))
|
||||
rows = len(data) + 1
|
||||
tbl = s.shapes.add_table(rows, len(headers), Inches(0.55), Inches(1.7),
|
||||
Inches(11.4), Inches(0.5 * rows)).table
|
||||
for c, w in enumerate(widths):
|
||||
tbl.columns[c].width = Inches(w)
|
||||
for c, hd in enumerate(headers):
|
||||
cell = tbl.cell(0, c)
|
||||
cell.fill.solid()
|
||||
cell.fill.fore_color.rgb = ACCENT
|
||||
_fill_cell(cell, hd, 12, True, WHITE, center=True)
|
||||
for r, row in enumerate(data, start=1):
|
||||
for c, val in enumerate(row):
|
||||
cell = tbl.cell(r, c)
|
||||
cell.fill.solid()
|
||||
cell.fill.fore_color.rgb = BG_SOFT if r % 2 == 0 else WHITE
|
||||
_fill_cell(cell, val, 11, False, INK, center=(c in center_cols))
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_closing(prs, d):
|
||||
s = blank_slide(prs)
|
||||
rect(s, 0, 0, EMU_W, EMU_H, fill=WHITE)
|
||||
rect(s, 0, Inches(5.0), EMU_W, Inches(2.5), fill=ACCENT_D)
|
||||
add_text(s, Inches(0.83), Inches(1.8), Inches(11.0), Inches(1.62),
|
||||
[(d.get("title", "Thank You"), 96, True, BLUE2)])
|
||||
if d.get("message"):
|
||||
add_text(s, Inches(0.95), Inches(3.5), Inches(11.0), Inches(0.5),
|
||||
[(d["message"], 14, False, INK)])
|
||||
cl = [(d.get("team", "시스템 사업팀"), 14, True, WHITE)]
|
||||
cl += [(c, 12, False, PALE) for c in d.get("contacts", [])]
|
||||
add_text(s, Inches(0.95), Inches(5.35), Inches(11.0), Inches(1.4), cl)
|
||||
add_logo(s, left=10.55, top=0.55, height=0.6)
|
||||
return s
|
||||
|
||||
|
||||
# ---------- manual layouts ----------
|
||||
REGION = RGBColor(0xFF, 0x00, 0x00) # red region-highlight border (matches manual edit)
|
||||
|
||||
|
||||
def _img_px_size(path):
|
||||
try:
|
||||
from PIL import Image
|
||||
with Image.open(path) as im:
|
||||
return im.size
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _fit_image(slide, path, bx, by, bw, bh):
|
||||
"""Add picture fitted (aspect-preserved, centered) inside box (inches).
|
||||
Returns the placed rect (left, top, w, h) in inches."""
|
||||
pic = slide.shapes.add_picture(path, Inches(bx), Inches(by), width=Inches(bw))
|
||||
if pic.height > Inches(bh):
|
||||
sp = pic._element
|
||||
sp.getparent().remove(sp)
|
||||
pic = slide.shapes.add_picture(path, Inches(bx), Inches(by), height=Inches(bh))
|
||||
pic.left = Inches(bx) + (Inches(bw) - pic.width) // 2
|
||||
pic.top = Inches(by) + (Inches(bh) - pic.height) // 2
|
||||
return (pic.left / 914400.0, pic.top / 914400.0,
|
||||
pic.width / 914400.0, pic.height / 914400.0)
|
||||
|
||||
|
||||
def _norm_boxes(it):
|
||||
"""Return list of [x,y,w,h] pixel boxes from item's box/boxes field."""
|
||||
b = it.get("boxes") or it.get("box")
|
||||
if not b:
|
||||
return []
|
||||
return b if isinstance(b[0], (list, tuple)) else [b]
|
||||
|
||||
|
||||
def L_screen_guide(prs, d):
|
||||
"""화면 설명: 캡처 영역(좌) + 번호 콜아웃(우). 이미지 위 영역 박스+번호 마커 지원."""
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "07."), d.get("title", "화면 설명"), d.get("subtitle"))
|
||||
sx, sy, sw, sh_ = 0.55, 1.7, 7.3, 5.0
|
||||
rect(s, Inches(sx), Inches(sy), Inches(sw), Inches(sh_), fill=BG_SOFT, line_color=LINE, line_w=1.0)
|
||||
items = d.get("items", [])
|
||||
img = d.get("image")
|
||||
if img and os.path.exists(img):
|
||||
il, it_, iw, ih = _fit_image(s, img, sx + 0.05, sy + 0.05, sw - 0.1, sh_ - 0.1)
|
||||
px = _img_px_size(img)
|
||||
if px:
|
||||
scale = iw / px[0] # inches per pixel (aspect preserved)
|
||||
mdia = 0.30
|
||||
for i, item in enumerate(items):
|
||||
for j, bx in enumerate(_norm_boxes(item)):
|
||||
rl, rt = il + bx[0] * scale, it_ + bx[1] * scale
|
||||
rw, rh = bx[2] * scale, bx[3] * scale
|
||||
rect(s, Inches(rl), Inches(rt), Inches(rw), Inches(rh),
|
||||
fill=None, line_color=REGION, line_w=1.5)
|
||||
if j == 0: # number marker at first box's top-left corner
|
||||
num_badge(s, rl, rt, mdia, i + 1, size=12)
|
||||
else:
|
||||
add_text(s, Inches(sx), Inches(sy), Inches(sw), Inches(sh_),
|
||||
[("〔 " + d.get("caption", "화면 캡처 영역") + " 〕", 12, False, SUB, PP_ALIGN.CENTER)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
rx, rw = 8.15, 4.65
|
||||
n = max(len(items), 1)
|
||||
rh = min(1.0, (sh_ + 0.0) / n)
|
||||
for i, it in enumerate(items):
|
||||
pos = it.get("pos")
|
||||
if pos:
|
||||
num_badge(s, sx + pos[0], sy + pos[1], 0.34, i + 1, size=12)
|
||||
ly = sy + i * rh
|
||||
num_badge(s, rx, ly + 0.03, 0.34, i + 1, size=12)
|
||||
add_text(s, Inches(rx + 0.5), Inches(ly - 0.02), Inches(rw - 0.5), Inches(rh),
|
||||
[(it.get("title", ""), 13, True, INK), (it.get("desc", ""), 11, False, SUB)])
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_procedure(prs, d):
|
||||
"""조작 절차: 세로 번호 단계."""
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "07."), d.get("title", "조작 절차"), d.get("subtitle"))
|
||||
steps = d.get("steps", [])
|
||||
y0, y1 = 1.75, 6.8
|
||||
n = max(len(steps), 1)
|
||||
rh = min(1.1, (y1 - y0) / n)
|
||||
for i, st in enumerate(steps):
|
||||
y = y0 + i * rh
|
||||
if i % 2 == 1:
|
||||
rect(s, Inches(0.55), Inches(y), Inches(11.4), Inches(rh - 0.12), fill=BG_SOFT)
|
||||
num_badge(s, 0.75, y + (rh - 0.12 - 0.5) / 2, 0.5, i + 1, size=18)
|
||||
add_text(s, Inches(1.55), Inches(y), Inches(10.3), Inches(rh - 0.12),
|
||||
[(st.get("title", ""), 15, True, INK), (st.get("desc", ""), 12, False, SUB)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_icon_ref(prs, d):
|
||||
"""버튼/아이콘 설명: 카드 그리드(3열)."""
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "07."), d.get("title", "버튼 설명"), d.get("subtitle"))
|
||||
items = d.get("items", [])
|
||||
cw, ch, gx, gy, gp = 3.6, 2.35, 0.55, 1.75, 0.3
|
||||
for i, it in enumerate(items[:6]):
|
||||
r, c = divmod(i, 3)
|
||||
x = gx + c * (cw + gp)
|
||||
y = gy + r * (ch + 0.3)
|
||||
rect(s, Inches(x), Inches(y), Inches(cw), Inches(ch), fill=WHITE, line_color=LINE, line_w=1.0)
|
||||
rect(s, Inches(x + (cw - 1.1) / 2), Inches(y + 0.3), Inches(1.1), Inches(1.1),
|
||||
fill=BG_SOFT, line_color=ACCENT, line_w=1.0, shape=MSO_SHAPE.ROUNDED_RECTANGLE)
|
||||
add_text(s, Inches(x + (cw - 1.1) / 2), Inches(y + 0.3), Inches(1.1), Inches(1.1),
|
||||
[(it.get("icon", "ICON"), 11, True, ACCENT, PP_ALIGN.CENTER)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_text(s, Inches(x + 0.15), Inches(y + 1.5), Inches(cw - 0.3), Inches(0.35),
|
||||
[(it.get("name", ""), 13, True, INK, PP_ALIGN.CENTER)])
|
||||
add_text(s, Inches(x + 0.15), Inches(y + 1.85), Inches(cw - 0.3), Inches(0.45),
|
||||
[(it.get("func", ""), 10, False, SUB, PP_ALIGN.CENTER)])
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
def L_alarm(prs, d):
|
||||
"""알람·조치: 주의 배너 + 표."""
|
||||
s = blank_slide(prs)
|
||||
header(s, d.get("no", "07."), d.get("title", "알람 및 조치 방법"), d.get("subtitle"))
|
||||
rect(s, Inches(0.55), Inches(1.65), Inches(11.4), Inches(0.55),
|
||||
fill=RGBColor(0xFF, 0xF4, 0xD6), line_color=YEL if False else RGBColor(0xF0, 0xB4, 0x29), line_w=1.0)
|
||||
rect(s, Inches(0.7), Inches(1.78), Inches(0.95), Inches(0.3), fill=WARN, shape=MSO_SHAPE.ROUNDED_RECTANGLE)
|
||||
add_text(s, Inches(0.7), Inches(1.78), Inches(0.95), Inches(0.3),
|
||||
[("주의", 11, True, WHITE, PP_ALIGN.CENTER)], anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_text(s, Inches(1.8), Inches(1.65), Inches(10.0), Inches(0.55),
|
||||
[(d.get("notice", "알람 발생 시 아래 조치 방법을 순서대로 수행하십시오."), 12, True, INK)],
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
headers = d.get("headers", ["코드", "알람명", "발생 원인", "조치 방법"])
|
||||
widths = d.get("widths") or [1.4, 3.0, 3.5, 3.5]
|
||||
data = d.get("rows", [])
|
||||
center_cols = set(d.get("center_cols", [0]))
|
||||
rows = len(data) + 1
|
||||
tbl = s.shapes.add_table(rows, len(headers), Inches(0.55), Inches(2.45),
|
||||
Inches(11.4), Inches(0.5 * rows)).table
|
||||
for c, w in enumerate(widths):
|
||||
tbl.columns[c].width = Inches(w)
|
||||
for c, hd in enumerate(headers):
|
||||
cell = tbl.cell(0, c)
|
||||
cell.fill.solid()
|
||||
cell.fill.fore_color.rgb = ACCENT
|
||||
_fill_cell(cell, hd, 12, True, WHITE, center=True)
|
||||
for r, row in enumerate(data, start=1):
|
||||
for c, val in enumerate(row):
|
||||
cell = tbl.cell(r, c)
|
||||
cell.fill.solid()
|
||||
cell.fill.fore_color.rgb = BG_SOFT if r % 2 == 0 else WHITE
|
||||
_fill_cell(cell, val, 11, False, INK, center=(c in center_cols))
|
||||
add_logo(s)
|
||||
footer(s, d.get("page"), d.get("doc_title", ""))
|
||||
return s
|
||||
|
||||
|
||||
BUILDERS = {
|
||||
"cover": L_cover, "history": L_history, "agenda": L_agenda, "divider": L_divider,
|
||||
"content": L_content, "flow": L_flow, "table": L_table, "closing": L_closing,
|
||||
"screen_guide": L_screen_guide, "procedure": L_procedure,
|
||||
"icon_ref": L_icon_ref, "alarm": L_alarm,
|
||||
}
|
||||
|
||||
|
||||
def build(spec, out_path):
|
||||
prs = Presentation()
|
||||
prs.slide_width = EMU_W
|
||||
prs.slide_height = EMU_H
|
||||
# 슬라이드 마스터 배경 적용 (흰 배경 + Ubisam 워터마크 + accent 바)
|
||||
_apply_master_background(prs)
|
||||
doc_title = spec.get("doc_title", "")
|
||||
page = 0
|
||||
for sl in spec["slides"]:
|
||||
t = sl["type"]
|
||||
if t not in BUILDERS:
|
||||
raise ValueError("unknown slide type: " + t)
|
||||
sl.setdefault("doc_title", doc_title)
|
||||
if t in ("history", "agenda", "content", "flow", "table",
|
||||
"screen_guide", "procedure", "icon_ref", "alarm"):
|
||||
page += 1
|
||||
sl.setdefault("page", page)
|
||||
BUILDERS[t](prs, sl)
|
||||
prs.save(out_path)
|
||||
return len(prs.slides._sldIdLst)
|
||||
|
||||
|
||||
def sample_spec():
|
||||
return {
|
||||
"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"]},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--input", help="spec JSON path (omit for sample showcase)")
|
||||
ap.add_argument("--output", default="Ubisam_layout_showcase.pptx")
|
||||
args = ap.parse_args()
|
||||
if args.input:
|
||||
with open(args.input, encoding="utf-8") as f:
|
||||
spec = json.load(f)
|
||||
else:
|
||||
spec = sample_spec()
|
||||
n = build(spec, args.output)
|
||||
print("saved:", os.path.abspath(args.output), "slides:", n)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
394
.claude/skills/ubisam-ppt-make/examples/showcase.json
Normal file
394
.claude/skills/ubisam-ppt-make/examples/showcase.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
87
.claude/skills/ubisam-ppt-make/reference/layout_selection.md
Normal file
87
.claude/skills/ubisam-ppt-make/reference/layout_selection.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Layout selection guide
|
||||
|
||||
How to pick the right layout for a given piece of source/analysis data when
|
||||
building a Ubisam SW 설계서 / HMI Manual deck. Read this before writing the spec.
|
||||
|
||||
## Slide master background (유비샘 산출물 고정)
|
||||
|
||||
`build()` 호출 시 자동으로 슬라이드 마스터에 배경을 적용한다. 모든 pptx에 공통 적용.
|
||||
|
||||
| 요소 | 값 | 적용 범위 |
|
||||
|---|---|---|
|
||||
| 캔버스 배경색 | `BG_SOFT` #F4F7FA (연회색) | 전체 슬라이드 마스터 + 모든 레이아웃 |
|
||||
| 하단 accent 바 | `ACCENT` #4A8DCB (blue), h=0.15in | 마스터 공통 장식 (y=7.35in) |
|
||||
|
||||
**상속 규칙**: 개별 레이아웃에서 full-slide `rect(fill=WHITE)` 를 그리면 마스터 배경을 덮는다.
|
||||
- 배경 **덮음** (자체 흰색 rect): `cover` · `divider` · `closing`
|
||||
- 배경 **상속** (BG_SOFT 노출): `history` · `agenda` · `content` · `flow` · `table` · `screen_guide` · `procedure` · `icon_ref` · `alarm`
|
||||
|
||||
spec JSON에서 `"background_color": "RRGGBB"` 키로 배경색 재정의 가능 (기본 = F4F7FA).
|
||||
|
||||
|
||||
## Quick decision table
|
||||
|
||||
| If the data is… | use | not |
|
||||
|------------------|-----|-----|
|
||||
| Document identity (title, 장비명, 담당자) | `cover` | — |
|
||||
| Revision log (버전·날짜·작성자·변경내용) | `history` | `table` |
|
||||
| List of sections / TOC | `agenda` | — |
|
||||
| A chapter / section break | `divider` | — |
|
||||
| 2–4 parallel sub-areas, each 1 line | `content` | `table` |
|
||||
| Machine/SW execution sequence **with conditions/branches** | `flow` | `procedure` |
|
||||
| Reference data in rows × columns (IO, 축, 스펙, 파라미터) | `table` | `content` |
|
||||
| One screenshot to annotate by region | `screen_guide` | `icon_ref` |
|
||||
| Ordered **human actions**, no branching | `procedure` | `flow` |
|
||||
| Set of discrete buttons/icons (name + function, ≤6) | `icon_ref` | `table` |
|
||||
| Faults with cause → action (+ safety) | `alarm` | `table` |
|
||||
| Closing / contact | `closing` | — |
|
||||
|
||||
## Per-layout: what data belongs
|
||||
|
||||
### Document layouts
|
||||
|
||||
- **cover** — The deliverable's identity. Fields: doc tag, 설계서/매뉴얼 title,
|
||||
장비 한/영 설명, team, 담당자 contacts. One per deck, first slide.
|
||||
- **history** — Revision rows `[No, Version, 날짜, 변경자, 변경내용]`. Use only
|
||||
for the change log; any other 5-column data → `table`.
|
||||
- **agenda** — Up to 6 top-level sections `{no, title(EN), sub(KO)}`. Mirrors the
|
||||
`divider` numbers used later.
|
||||
- **divider** — Section break: big `no` + EN title + KO subtitle. No body data.
|
||||
- **content** — 2–4 panels `{title, body}`, each panel one parallel facet
|
||||
(e.g. UI 화면 영역, 기능 블록). Body is a one-line description, NOT a list of
|
||||
rows. If you have ≥5 items or columnar data, switch to `table`.
|
||||
- **flow** — Sequence steps `{title, code, kind}` where `code` is the
|
||||
method/condition name and `kind` is `proc` or `dec` (decision). Add `branch`
|
||||
for the fail/stop path. Use for control sequences, state machines, 공정 흐름.
|
||||
If there are no conditions/branches and the actor is a person → `procedure`.
|
||||
- **table** — Any structured reference data: IO map, 축 구성, 통신 스펙, 레시피
|
||||
파라미터. Define `headers`, `widths`, `center_cols`. Keep cells short.
|
||||
- **closing** — Thank-you + contacts. Last slide.
|
||||
|
||||
### Manual layouts (HMI Manual / 27번)
|
||||
|
||||
- **screen_guide** — Exactly ONE screen, annotated. `caption` = what the capture
|
||||
shows; `items[]` = numbered callouts `{title, desc, pos:[x,y]}` where `pos` is
|
||||
the marker offset (inch) inside the capture box. Use when regions of a single
|
||||
image need labels. Many separate images → multiple `screen_guide` slides.
|
||||
- **procedure** — Ordered operator steps `{title, desc}`. Each step is an action
|
||||
the user performs in order (전원 ON → 원점 복귀 → …). No machine codes, no
|
||||
branches (those belong in `flow`). Aim for 3–6 steps.
|
||||
- **icon_ref** — ≤6 discrete controls `{icon, name, func}`. One card per button/
|
||||
icon with its function. For long control lists with extra columns → `table`.
|
||||
- **alarm** — Troubleshooting rows `[코드, 알람명, 발생원인, 조치방법]` plus a
|
||||
`notice` safety banner. Use for alarm/fault handling; pairs each fault with its
|
||||
remedy. Generic specs without cause→action → `table`.
|
||||
|
||||
## Disambiguation rules (the common mistakes)
|
||||
|
||||
1. **procedure vs flow** — Person doing steps in order = `procedure`. Machine/SW
|
||||
executing with `if`/branches/return-to = `flow`.
|
||||
2. **content vs table** — ≤4 facets each described in a sentence = `content`.
|
||||
Repeating records with the same columns = `table`.
|
||||
3. **icon_ref vs table** — Each item is a single control with one function =
|
||||
`icon_ref`. Items have multiple attributes/columns = `table`.
|
||||
4. **alarm vs table** — Rows pair a problem with its corrective action (needs the
|
||||
safety banner) = `alarm`. Pure reference rows = `table`.
|
||||
5. **screen_guide vs icon_ref** — Annotating positions on one image =
|
||||
`screen_guide`. Listing controls independent of an image = `icon_ref`.
|
||||
BIN
.claude/skills/ubisam-ppt-make/생성파일/test_bg.pptx
Normal file
BIN
.claude/skills/ubisam-ppt-make/생성파일/test_bg.pptx
Normal file
Binary file not shown.
BIN
.claude/skills/ubisam-ppt-make/생성파일/test_wm.pptx
Normal file
BIN
.claude/skills/ubisam-ppt-make/생성파일/test_wm.pptx
Normal file
Binary file not shown.
68
.claude/skills/ui_layout_image/SKILL.md
Normal file
68
.claude/skills/ui_layout_image/SKILL.md
Normal file
@ -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) | `<layout class="QGridLayout/QVBoxLayout">`, `<widget>`, `<property name="text">` |
|
||||
| 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/<screen>.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 그리드 구조 보고 화면 레이아웃 이미지로 만들어서 매뉴얼에 넣어줘"
|
||||
75
.claude/skills/ui_layout_image/render_helpers.py
Normal file
75
.claude/skills/ui_layout_image/render_helpers.py
Normal file
@ -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")
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@ -36,8 +36,14 @@ bin/
|
||||
/.git.zip
|
||||
/.gitignore
|
||||
/UnTrackedLocalFiles
|
||||
.claude/
|
||||
.claude/*
|
||||
!.claude/skills/
|
||||
.serena/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# 로컬 전용 Aurora 에이전트 레퍼런스 (커밋 제외)
|
||||
/docs/Aurora에이전트레퍼런스모음/
|
||||
|
||||
# Ubisam 산출물 — 로컬 전용, GIT 추적 제외
|
||||
/docs/\[Ubisam산출물\]/
|
||||
|
||||
23
docs/작업일지/2026_06_25.md
Normal file
23
docs/작업일지/2026_06_25.md
Normal file
@ -0,0 +1,23 @@
|
||||
# 작업일지 — 2026-06-25
|
||||
|
||||
## 1. 레시피별 Front/Down 레이저 인터락 추가 (`041c155`)
|
||||
|
||||
- **목적**: Use Interlock ON 시 ① 안전구역 게이트(기존)는 유지하면서, ② **레시피별 방향(Front/Down) 차단**(신규)을 추가.
|
||||
- **저장**: Recipe 폴더의 `Recipe_Laser_Interlock.json`(Front/Down = 0/1)에 보관. 경로는 `FieldHelper.json`과 동일 소스(`DB.Dao.SystemRecipe.WorkRecipeFile`) 사용.
|
||||
- **적용 범위**: Auto/Manual 양쪽. Manual은 ①+② 모두 신규 적용.
|
||||
- **stale 방지**: VM 체크박스는 레시피 전환 시 Background 우선순위로 재로드하여 이전 레시피 값 잔존 방지.
|
||||
- **변경 파일**:
|
||||
- `WeldingCommon/Settings/RecipeLaserInterlock.cs`(신규): Front/Down 인터락 모델/입출력
|
||||
- `Sequences/SeqAutoRunPTOP.cs` / `Sequences/Manual/SeqComponentTeachingRun.cs`: 방향 차단 게이트 추가
|
||||
- `Views/Recipes/UCScannerObjectTeachingControlBox.xaml` + `…ViewModel.cs`: Front/Down 체크박스 UI + 레시피 전환 재로드
|
||||
- `WeldingCommon/WeldingCommon.csproj`: 신규 파일 Compile Include 등록
|
||||
|
||||
## 2. 용어정리(가접 용접) 별도 파일 분리 (`8bbac47`)
|
||||
|
||||
- 200줄 규칙 준수를 위해 `architecture.md §8` 용어정리 본문을 `docs/용어정리.md`로 분리하고 `@import`로 연결.
|
||||
|
||||
## 3. SW 점검 결과 — SW Version 시트 최신 Source 변경분 반영 (`48befb6`)
|
||||
|
||||
- `d131ad9` 이후 소스(.cs/.xaml/.csv) 커밋 6항목 추가 반영:
|
||||
용접 전/후 Scout 핸드셰이크, Auto 발진 보정테이블/셀 offset, 레시피 스캐너 토글 창·Field 버튼, 카메라 각도 기반 Laser offset.
|
||||
- 헤더 요약 갱신: 총 14→20, 정상 9→12, 확인중 5→8, 점검일자 06-24.
|
||||
71
docs/작업일지/2026_06_29.md
Normal file
71
docs/작업일지/2026_06_29.md
Normal file
@ -0,0 +1,71 @@
|
||||
# 작업일지 — 2026-06-29
|
||||
|
||||
## 1. 02.설계 xlsx 4종 — 완전 독립 빌드로 전환
|
||||
|
||||
### 배경
|
||||
이전 방식(ref xlsx 클론 후 덮어쓰기)은 `D:\Work\Processing\...` ref 파일이 없는 PC에서 실행 불가. ubisam-qa 검증에서 API 불일치(import 단계 즉시 실패) + ref 의존 2건 Critical 판정.
|
||||
|
||||
### 수행 내용
|
||||
- **`xlsxfill.py` 재작성**: openpyxl 완전 독립. 스타일 상수 하드코딩.
|
||||
- 배너: 맑은 고딕 13pt Bold White / `#1F4E78` (진네이비) / medium border
|
||||
- 헤더: 맑은 고딕 11pt Bold White / `#2E75B6` (파랑)
|
||||
- 데이터: 맑은 고딕 10pt / 홀수=`#DDEBF7`·짝수=흰색 교대
|
||||
- API: `make_workbook / write_banner / write_header_row / write_data_rows / group_merge`
|
||||
- **`build_02설계_xlsx.py` 전면 재작성**: `load_workbook(REF_DIR)` 제거. 4종 xlsx를 xlsxfill scratch-build API로 처음부터 생성.
|
||||
- PLC-PC 신호정의서(4탭) / 모션축·IO맵(3탭) / 기능명세서(8탭) / TactTime(2탭)
|
||||
- 실행: `set PYTHONUTF8=1 && py build_02설계_xlsx.py` (cwd=repo root)
|
||||
- **`05-form-schemas.md` 갱신**: "ref 클론" → "완전 독립" 방식, 스타일 상수표(색상·폰트·테두리·행높이) 추가.
|
||||
|
||||
### 검증 결과
|
||||
xlsx 4종 전 항목 통과: 시트 구성, 필수 키워드(M70000/M70500/PAGE_REQ/ACS/Ajin/Aurora/SeqAutoRunPTOP), 잔재 0건, 테두리/배경색 정의 정상.
|
||||
|
||||
---
|
||||
|
||||
## 2. 05.양산 운영매뉴얼_HMI PPTX 3종 생산
|
||||
|
||||
### 소스 파일 (Doc/산출물문서/ — 값 확정 원본 보존)
|
||||
| 파일 | 슬라이드 |
|
||||
|---|---|
|
||||
| `25~26. 셋팅 기준서(SOP)_IP Setting_Setting_...` | 6 |
|
||||
| `27. HMI Manual_UBISAM_260622_PTOP.pptx` | 15 |
|
||||
| `27. HMI Manual_UBISAM_가접용접_매뉴얼_추가_260626_PTOP.pptx` | 8 |
|
||||
|
||||
### 방법
|
||||
1. PowerShell COM으로 소스 슬라이드 PNG 렌더링(1920×1080)
|
||||
2. `/ubisam-ppt-make` 스킬 + `screen_guide` 레이아웃으로 Ubisam 양식 래핑
|
||||
3. 표지(PtoP Welder 브랜딩) + 변경이력 + 섹션 간지 + 슬라이드별 screen_guide + 마무리
|
||||
|
||||
### 출력 (`docs/[Ubisam산출물]/05.양산/운영매뉴얼_HMI/`)
|
||||
| 파일 | 슬라이드 | 크기 |
|
||||
|---|---|---|
|
||||
| `셋팅기준서_SOP.pptx` | 10 | 1.2 MB |
|
||||
| `HMI_운영매뉴얼_기본.pptx` | 19 | 2.9 MB |
|
||||
| `HMI_운영매뉴얼_가접용접.pptx` | 12 | 2.1 MB |
|
||||
|
||||
- 담당자: 노도연 / otteryeonny@ubisam.com / 로봇자동화사업부
|
||||
- 폰트: 굴림(한글)/Arial(영문) 준수, Ubisam 로고 임베딩 확인
|
||||
- `05.양산/_task.md` 진행률 0% → 40%
|
||||
|
||||
---
|
||||
|
||||
## 3. 스킬 업데이트 — ubisam-deliverables
|
||||
|
||||
- `xlsxfill.py`, `build_02설계_xlsx.py`, `05-form-schemas.md` 개정. ref 파일 없이 어느 PC에서든 재생산 가능.
|
||||
- ubisam-qa 2회 검증(02.설계 xlsx, 05.양산 PPTX) 실시.
|
||||
|
||||
---
|
||||
|
||||
## 4. 사용자 정보 업데이트
|
||||
|
||||
- 이메일: `otteryeonny@ubisam.com` (수정, 구 `ubisam.dy241104@gmail.com`)
|
||||
- 소속: 로봇자동화사업부
|
||||
- PPTX 3종 연락처 반영 완료.
|
||||
|
||||
---
|
||||
|
||||
## 잔여·후속 작업
|
||||
|
||||
- `HMI_운영매뉴얼_기본.pptx`: 소스 PPTX 19슬라이드 중 COM 렌더링 15장(4장 누락 가능성). 실물 확인 후 필요시 보완.
|
||||
- 가접 용접 시나리오 추가분(가접 용접 후속 매뉴얼) — 현장 추가 시나리오 발생 시 소스 PPTX 교체 후 재빌드.
|
||||
- 02.설계: 모션축 행정/속도/Home 파라미터 미확정 → PLC/모션 축 확정 후 `build_02설계_xlsx.py` 내 `(제안)` 값 갱신.
|
||||
- 04.셋업 산출물(HW_IO모션점검, 파라미터_레시피백업) 미작성.
|
||||
48
docs/작업일지/2026_06_30.md
Normal file
48
docs/작업일지/2026_06_30.md
Normal file
@ -0,0 +1,48 @@
|
||||
# 작업일지 — 2026-06-30
|
||||
|
||||
## 1. 스킬 최종 검토 및 개선
|
||||
|
||||
### 배경
|
||||
커밋 전 스킬 파일 전체 재검토 요청. `xlsxfill.py`·`build_02설계_xlsx.py` 코드 품질 개선 및 `.gitignore` 구조 수정.
|
||||
|
||||
### xlsxfill.py 개선
|
||||
- **`fill=None` sentinel 처리 버그 수정**: `fill or HEADER_FILL` 패턴이 `fill=None` 을 "파랑 기본값"으로 처리하던 문제.
|
||||
- `_UNSET = object()` sentinel 추가.
|
||||
- `write_header_row(fill=_UNSET)` → 생략 시 `HEADER_FILL`(파랑), `fill=None` 명시 시 배경 없음.
|
||||
- 기능명세서·TactTime 계열의 헤더 덮어쓰기 패턴 제거.
|
||||
|
||||
### build_02설계_xlsx.py 개선
|
||||
- `TODAY = datetime.date.today()` — 하드코딩(2026-06-29) → 실행일 자동 반영.
|
||||
- `build_funcspec`: 중복 `from openpyxl.styles import Border, Side` + `thin()/tb()` 로컬 정의 제거 → `tb = lambda: FUNC_DATA_BORDER` 단순화.
|
||||
- `build_funcspec` OverView: `write_header_row` 후 덮어쓰기 패턴 제거(`fill=None` 직접 전달).
|
||||
- `build_tact`: 중복 로컬 함수 제거, `TACT_BORDER` 직접 사용, 헤더 덮어쓰기 패턴 제거.
|
||||
|
||||
### 05-form-schemas.md 개선
|
||||
- 스타일 상수 표에 "기능명세서 헤더", "TactTime 헤더" 행 추가(배경 없음 명시).
|
||||
- `write_header_row(fill=None)` 동작·`TODAY` 자동 반영 설명 추가.
|
||||
|
||||
### ubisam-ppt-make/SKILL.md 개선
|
||||
- `screen_guide` 레이아웃 필드에 `image(PNG경로)` 추가 — 실 이미지 삽입 필드가 문서에 누락돼 있었음.
|
||||
|
||||
### .gitignore 구조 수정
|
||||
| 항목 | 변경 전 | 변경 후 |
|
||||
|---|---|---|
|
||||
| 스킬 추적 | `.claude/` 전체 무시 | `.claude/*` + `!.claude/skills/` 예외 |
|
||||
| Python 캐시 | 미정의 | `__pycache__/`, `*.pyc` 추가 |
|
||||
|
||||
결과: `.claude/skills/` 하위만 git 추적, settings·memory 등 나머지는 여전히 무시.
|
||||
|
||||
### 검증 (build 재실행)
|
||||
- xlsx 4종 재빌드 성공 (오류 없음).
|
||||
- 시트 구성·배너 색상·헤더 fill 검증 스크립트 All PASS.
|
||||
- PLC-PC·모션축IO: banner=FF1F4E78, header=FF2E75B6 ✅
|
||||
- 기능명세서·TactTime: banner·header 배경 없음(00000000) ✅
|
||||
|
||||
### 기타
|
||||
- `_meta/project.yaml` 담당자 확정: 노도연 / otteryeonny@ubisam.com / 로봇자동화사업부
|
||||
- `기능명세서.md` xlsx 8탭↔md 코드기반 탭 구조 차이 명시
|
||||
|
||||
## 잔여·후속 작업
|
||||
- 커밋 전 최종 사용자 확인 대기.
|
||||
- 모션축 행정/속도/Home 파라미터 미확정 → PLC/모션 확정 후 `(제안)` 값 갱신.
|
||||
- 04.셋업 산출물(HW_IO모션점검, 파라미터_레시피백업) 미작성.
|
||||
Loading…
x
Reference in New Issue
Block a user