480 lines
31 KiB
Python
480 lines
31 KiB
Python
# -*- 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 파일 불필요).")
|