Compare commits
5 Commits
9ac2ec5481
...
981999dafe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981999dafe | ||
|
|
bb6c159adf | ||
|
|
e919db5bf7 | ||
|
|
77a5fe219d | ||
|
|
372507ec84 |
@@ -0,0 +1,181 @@
|
||||
/* ═══════════════════════════════════════════════════
|
||||
Capping 상세 패널 스타일
|
||||
═══════════════════════════════════════════════════ */
|
||||
|
||||
/* ── 루트 (전체 화면 오버레이) ─────────────────────── */
|
||||
|
||||
.ewlk-capping {
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding: 60px 20px 20px 80px;
|
||||
}
|
||||
|
||||
/* ── 메인 컨테이너 ────────────────────────────────── */
|
||||
|
||||
.ewlk-capping__container {
|
||||
width: 340px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── 설비 정보 헤더 ───────────────────────────────── */
|
||||
|
||||
.ewlk-capping__info-header {
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-color: rgb(220, 220, 225);
|
||||
border-width: 1px;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ewlk-capping__info-header-left {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ewlk-capping__info-title {
|
||||
font-size: 11px;
|
||||
color: rgb(100, 100, 110);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.ewlk-capping__equip-name {
|
||||
font-size: 16px;
|
||||
-unity-font-style: bold;
|
||||
color: rgb(30, 30, 40);
|
||||
}
|
||||
|
||||
/* ── 가동 토글 (읽기 전용) ──────────────────────── */
|
||||
|
||||
.ewlk-capping__toggle-wrap {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-label {
|
||||
font-size: 12px;
|
||||
color: rgb(80, 80, 100);
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle {
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-track {
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
padding: 2px;
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-track--on {
|
||||
background-color: rgb(60, 180, 80);
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-track--off {
|
||||
background-color: rgb(200, 200, 210);
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
transition-property: translate;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-thumb--on {
|
||||
translate: 20px 0;
|
||||
}
|
||||
|
||||
.ewlk-capping__toggle-thumb--off {
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
/* ── 테이블 공통 ──────────────────────────────────── */
|
||||
|
||||
.ewlk-capping__table {
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-color: rgb(220, 220, 225);
|
||||
border-width: 1px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ewlk-capping__table-header {
|
||||
flex-direction: row;
|
||||
background-color: rgb(245, 246, 250);
|
||||
border-bottom-color: rgb(220, 220, 225);
|
||||
border-bottom-width: 1px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.ewlk-capping__table-header-cell {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
font-size: 12px;
|
||||
-unity-font-style: bold;
|
||||
color: rgb(60, 60, 70);
|
||||
-unity-text-align: middle-center;
|
||||
}
|
||||
|
||||
.ewlk-capping__table-row {
|
||||
flex-direction: row;
|
||||
border-bottom-color: rgb(240, 240, 242);
|
||||
border-bottom-width: 1px;
|
||||
min-height: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ewlk-capping__table-cell {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
font-size: 12px;
|
||||
-unity-text-align: middle-center;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.ewlk-capping__table-cell--category {
|
||||
color: rgb(60, 60, 70);
|
||||
}
|
||||
|
||||
.ewlk-capping__table-cell--measured {
|
||||
color: rgb(30, 30, 40);
|
||||
}
|
||||
|
||||
.ewlk-capping__table-cell--setting {
|
||||
color: rgb(60, 80, 200);
|
||||
}
|
||||
|
||||
/* ── 선택 해제 버튼 ───────────────────────────────── */
|
||||
|
||||
.ewlk-capping__deselect-btn {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
align-self: center;
|
||||
left: 50%;
|
||||
translate: -50% 0;
|
||||
padding: 8px 24px;
|
||||
font-size: 13px;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(60, 130, 240);
|
||||
border-radius: 6px;
|
||||
border-width: 0;
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
|
||||
.ewlk-capping__deselect-btn:hover {
|
||||
background-color: rgb(40, 110, 220);
|
||||
}
|
||||
|
||||
.ewlk-capping__deselect-btn:active {
|
||||
background-color: rgb(30, 90, 190);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc9c71db03fe52740a37fd8c641881c1
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
unsupportedSelectorAction: 0
|
||||
@@ -0,0 +1,236 @@
|
||||
/* ═══════════════════════════════════════════════════
|
||||
설비 상세 패널 스타일
|
||||
═══════════════════════════════════════════════════ */
|
||||
|
||||
/* ── 루트 (전체 화면 오버레이) ─────────────────────── */
|
||||
|
||||
.ewlk-detail {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 60px 20px 20px 80px;
|
||||
}
|
||||
|
||||
/* ── 좌/우 컬럼 ───────────────────────────────────── */
|
||||
|
||||
.ewlk-detail__left {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ewlk-detail__right {
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── 섹션 패널 ────────────────────────────────────── */
|
||||
|
||||
.ewlk-detail__section {
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-color: rgb(220, 220, 225);
|
||||
border-width: 1px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ewlk-detail__section--stretch {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.ewlk-detail__section-header {
|
||||
background-color: rgb(245, 246, 250);
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
-unity-font-style: bold;
|
||||
color: rgb(40, 40, 50);
|
||||
border-bottom-color: rgb(220, 220, 225);
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.ewlk-detail__sub-header {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
-unity-font-style: bold;
|
||||
color: rgb(80, 80, 100);
|
||||
background-color: rgb(250, 250, 252);
|
||||
border-bottom-color: rgb(230, 230, 235);
|
||||
border-bottom-width: 1px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ── 스크롤 영역 ──────────────────────────────────── */
|
||||
|
||||
.ewlk-detail__scroll {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
/* ── 키-값 행 ─────────────────────────────────────── */
|
||||
|
||||
.ewlk-detail__row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 5px 12px;
|
||||
border-bottom-color: rgb(240, 240, 242);
|
||||
border-bottom-width: 1px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.ewlk-detail__row-key {
|
||||
width: 90px;
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: rgb(100, 100, 110);
|
||||
}
|
||||
|
||||
.ewlk-detail__row-value {
|
||||
flex-grow: 1;
|
||||
font-size: 12px;
|
||||
color: rgb(30, 30, 40);
|
||||
-unity-text-align: upper-right;
|
||||
}
|
||||
|
||||
/* ── 키 라벨 중요도 표시 (*) ──────────────────────── */
|
||||
|
||||
.ewlk-detail__key-star {
|
||||
color: rgb(220, 40, 40);
|
||||
font-size: 13px;
|
||||
-unity-font-style: bold;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
/* ── 값 색상 구분 (측정/세팅/단위) ────────────────── */
|
||||
|
||||
.ewlk-detail__row-value-wrap {
|
||||
flex-grow: 1;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ewlk-detail__val--measured {
|
||||
font-size: 12px;
|
||||
color: rgb(30, 30, 40);
|
||||
}
|
||||
|
||||
.ewlk-detail__val--setting {
|
||||
font-size: 12px;
|
||||
color: rgb(60, 80, 200);
|
||||
}
|
||||
|
||||
.ewlk-detail__val--separator {
|
||||
font-size: 12px;
|
||||
color: rgb(30, 30, 40);
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.ewlk-detail__val--unit {
|
||||
font-size: 11px;
|
||||
color: rgb(30, 30, 40);
|
||||
}
|
||||
|
||||
/* ── 데이터 유형 범례 ─────────────────────────────── */
|
||||
|
||||
.ewlk-detail__data-type-wrap {
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.ewlk-detail__data-type-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.ewlk-detail__dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.ewlk-detail__dot--measured {
|
||||
background-color: rgb(30, 30, 30);
|
||||
}
|
||||
|
||||
.ewlk-detail__dot--setting {
|
||||
background-color: rgb(60, 80, 200);
|
||||
}
|
||||
|
||||
.ewlk-detail__dot--predicted {
|
||||
background-color: rgb(40, 160, 40);
|
||||
}
|
||||
|
||||
.ewlk-detail__data-type-label {
|
||||
font-size: 11px;
|
||||
color: rgb(60, 60, 70);
|
||||
}
|
||||
|
||||
/* ── 가동 상태 행 (쌍) ────────────────────────────── */
|
||||
|
||||
.ewlk-detail__status-pair {
|
||||
flex-direction: row;
|
||||
border-bottom-color: rgb(240, 240, 242);
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.ewlk-detail__status-cell {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
padding: 5px 12px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.ewlk-detail__status-key {
|
||||
font-size: 12px;
|
||||
color: rgb(100, 100, 110);
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ewlk-detail__status-value {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ewlk-detail__status--active {
|
||||
background-color: rgb(220, 245, 220);
|
||||
color: rgb(20, 120, 20);
|
||||
}
|
||||
|
||||
.ewlk-detail__status--inactive {
|
||||
background-color: rgb(240, 240, 242);
|
||||
color: rgb(140, 140, 150);
|
||||
}
|
||||
|
||||
/* ── 선택 해제 버튼 ───────────────────────────────── */
|
||||
|
||||
.ewlk-detail__deselect-btn {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
align-self: center;
|
||||
left: 50%;
|
||||
translate: -50% 0;
|
||||
padding: 8px 24px;
|
||||
font-size: 13px;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(60, 130, 240);
|
||||
border-radius: 6px;
|
||||
border-width: 0;
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
|
||||
.ewlk-detail__deselect-btn:hover {
|
||||
background-color: rgb(40, 110, 220);
|
||||
}
|
||||
|
||||
.ewlk-detail__deselect-btn:active {
|
||||
background-color: rgb(30, 90, 190);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcff7fee6969c3046a315174982a7558
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
unsupportedSelectorAction: 0
|
||||
@@ -0,0 +1,41 @@
|
||||
/* ── 설비 정보 팝업 ─────────────────────────────── */
|
||||
|
||||
.ewlk-equip-popup {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 4px 10px 4px 8px;
|
||||
background-color: rgba(20, 24, 40, 0.85);
|
||||
border-radius: 6px;
|
||||
/* 팝업 중앙 정렬용 (translate은 Tracker에서 설정) */
|
||||
}
|
||||
|
||||
/* ── 상태 표시 원 ──────────────────────────────── */
|
||||
|
||||
.ewlk-equip-popup__indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
margin-right: 6px;
|
||||
/* 기본: 투명 (상태 클래스로 색상 결정) */
|
||||
background-color: rgba(128, 128, 128, 0.5);
|
||||
}
|
||||
|
||||
.ewlk-equip-popup__indicator.ewlk-equip-popup--running {
|
||||
background-color: rgb(0, 200, 80);
|
||||
}
|
||||
|
||||
.ewlk-equip-popup__indicator.ewlk-equip-popup--idle {
|
||||
background-color: rgb(240, 200, 0);
|
||||
}
|
||||
|
||||
.ewlk-equip-popup__indicator.ewlk-equip-popup--planned-stop {
|
||||
background-color: rgb(140, 140, 140);
|
||||
}
|
||||
|
||||
/* ── 설비명 라벨 ──────────────────────────────── */
|
||||
|
||||
.ewlk-equip-popup__label {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 13px;
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16e88e33fc1fa9a4697423125d9b9279
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
unsupportedSelectorAction: 0
|
||||
@@ -0,0 +1,206 @@
|
||||
/* ──────────────────────────────────────────────────────────
|
||||
EWLKMfgOrderModalContent — 제조지시현황 모달
|
||||
────────────────────────────────────────────────────────── */
|
||||
|
||||
/* ── 루트 컨테이너 ──────────────────────────────────────── */
|
||||
.ewlk-mfgorder {
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
background-color: rgb(18, 22, 36);
|
||||
padding: 0;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
/* ── 탭 바 ──────────────────────────────────────────────── */
|
||||
.ewlk-mfgorder__tab-bar {
|
||||
flex-direction: row;
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-color: rgb(50, 60, 90);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__tab {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 20px;
|
||||
background-color: rgb(34, 42, 66);
|
||||
color: rgb(140, 150, 175);
|
||||
border-width: 0;
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-color: rgba(0, 0, 0, 0);
|
||||
border-radius: 0;
|
||||
font-size: 13px;
|
||||
-unity-font-style: normal;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__tab:hover {
|
||||
background-color: rgb(40, 50, 78);
|
||||
color: rgb(220, 225, 240);
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__tab--active {
|
||||
color: rgb(220, 225, 240);
|
||||
border-bottom-color: rgb(0, 140, 255);
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
|
||||
/* ── 요약 바 ─────────────────────────────────────────────── */
|
||||
.ewlk-mfgorder__summary-bar {
|
||||
flex-direction: row;
|
||||
background-color: rgb(26, 32, 50);
|
||||
padding: 10px 16px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgb(50, 60, 90);
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__summary-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__summary-title {
|
||||
color: rgb(140, 150, 175);
|
||||
font-size: 11px;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__summary-value {
|
||||
color: rgb(220, 225, 240);
|
||||
font-size: 13px;
|
||||
-unity-font-style: bold;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── 설비가동률 행 ───────────────────────────────────────── */
|
||||
.ewlk-mfgorder__equip-row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: rgb(26, 32, 50);
|
||||
padding: 6px 16px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgb(50, 60, 90);
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__equip-title {
|
||||
color: rgb(140, 150, 175);
|
||||
font-size: 11px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 10px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__equip-rate {
|
||||
color: rgb(220, 225, 240);
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
margin-left: 8px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__count {
|
||||
color: rgb(140, 150, 175);
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
/* ── 프로그레스 바 (공통) ────────────────────────────────── */
|
||||
.ewlk-mfgorder__progress-wrap {
|
||||
flex-grow: 1;
|
||||
height: 10px;
|
||||
background-color: rgb(50, 60, 90);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__progress-fill {
|
||||
height: 100%;
|
||||
background-color: rgb(0, 200, 100);
|
||||
border-radius: 4px;
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
/* ── 테이블 헤더 ─────────────────────────────────────────── */
|
||||
.ewlk-mfgorder__header {
|
||||
flex-direction: row;
|
||||
background-color: rgb(34, 42, 66);
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgb(50, 60, 90);
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__header-cell {
|
||||
color: rgb(140, 150, 175);
|
||||
font-size: 11px;
|
||||
-unity-font-style: bold;
|
||||
-unity-text-align: middle-center;
|
||||
padding: 6px 4px;
|
||||
border-right-width: 1px;
|
||||
border-right-color: rgb(50, 60, 90);
|
||||
}
|
||||
|
||||
/* ── ListView ────────────────────────────────────────────── */
|
||||
.ewlk-mfgorder__list {
|
||||
flex-grow: 1;
|
||||
background-color: rgb(18, 22, 36);
|
||||
}
|
||||
|
||||
/* ── 데이터 행 ───────────────────────────────────────────── */
|
||||
.ewlk-mfgorder__row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
padding: 0 4px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: rgb(50, 60, 90);
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__cell {
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
padding: 0 4px;
|
||||
border-right-width: 1px;
|
||||
border-right-color: rgb(50, 60, 90);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__cell-label {
|
||||
color: rgb(220, 225, 240);
|
||||
font-size: 12px;
|
||||
-unity-text-align: middle-left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── 달성률 셀 내부 ─────────────────────────────────────── */
|
||||
.ewlk-mfgorder__stopped {
|
||||
color: rgb(160, 165, 185);
|
||||
font-size: 11px;
|
||||
-unity-text-align: middle-center;
|
||||
background-color: rgb(60, 65, 85);
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ewlk-mfgorder__pct {
|
||||
color: rgb(220, 225, 240);
|
||||
font-size: 11px;
|
||||
-unity-text-align: middle-right;
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
width: 36px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── 컬럼 너비 ───────────────────────────────────────────── */
|
||||
.col-equip { width: 100px; flex-shrink: 0; }
|
||||
.col-order { width: 120px; flex-shrink: 0; }
|
||||
.col-item-code { width: 110px; flex-shrink: 0; }
|
||||
.col-item-name { flex-grow: 1; }
|
||||
.col-time { width: 80px; flex-shrink: 0; }
|
||||
.col-target { width: 90px; flex-shrink: 0; }
|
||||
.col-achievement { width: 150px; flex-shrink: 0; flex-direction: row; align-items: center; }
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dbc3cb9562ea774a8fd358ec9d8ec65
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
unsupportedSelectorAction: 0
|
||||
@@ -0,0 +1,53 @@
|
||||
/* ═══════════════════════════════════════════════════
|
||||
충포장 설비 정보 팝업 스타일
|
||||
═══════════════════════════════════════════════════ */
|
||||
|
||||
/* ── 루트 ────────────────────────────────────────── */
|
||||
|
||||
.ewlk-pack-popup {
|
||||
padding: 8px 12px;
|
||||
background-color: rgba(20, 24, 40, 0.88);
|
||||
border-radius: 8px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* ── 타이틀 ──────────────────────────────────────── */
|
||||
|
||||
.ewlk-pack-popup__title {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 14px;
|
||||
-unity-font-style: bold;
|
||||
margin-bottom: 6px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom-color: rgba(255, 255, 255, 0.2);
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
/* ── 데이터 항목 ─────────────────────────────────── */
|
||||
|
||||
.ewlk-pack-popup__item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.ewlk-pack-popup__dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background-color: rgb(180, 200, 255);
|
||||
margin-right: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ewlk-pack-popup__label {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
font-size: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.ewlk-pack-popup__value {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 12px;
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29ef463f5fc415a4bb3ff70c4c6f5ceb
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
unsupportedSelectorAction: 0
|
||||
File diff suppressed because one or more lines are too long
@@ -246,6 +246,151 @@ MonoBehaviour:
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!1 &164967187
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 164967191}
|
||||
- component: {fileID: 164967190}
|
||||
- component: {fileID: 164967189}
|
||||
- component: {fileID: 164967188}
|
||||
- component: {fileID: 164967192}
|
||||
- component: {fileID: 164967193}
|
||||
m_Layer: 0
|
||||
m_Name: Cube_mfg
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!65 &164967188
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 164967187}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &164967189
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 164967187}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &164967190
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 164967187}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &164967191
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 164967187}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -1.5, y: 0.5, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &164967192
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 164967187}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 06942d388a19bcf458c4f333f1afc27a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKEquipInfoTracker
|
||||
uiDocument: {fileID: 1306527162}
|
||||
equipmentName: M001
|
||||
equipmentStatus: 0
|
||||
worldYOffset: 1
|
||||
--- !u!114 &164967193
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 164967187}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 520526319be9b2d4985315191c400d59, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKEquipSelectHandler
|
||||
uiDocument: {fileID: 1306527162}
|
||||
outlineScale: 1.05
|
||||
outlineColor: {r: 0.2, g: 0.5, b: 1, a: 0.4}
|
||||
--- !u!1 &557669013
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -356,6 +501,7 @@ GameObject:
|
||||
- component: {fileID: 1079771973}
|
||||
- component: {fileID: 1079771972}
|
||||
- component: {fileID: 1079771971}
|
||||
- component: {fileID: 1079771974}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
@@ -430,13 +576,430 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1079771970}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.2588191, y: 0, z: 0, w: 0.9659258}
|
||||
m_LocalPosition: {x: 0, y: 3, z: -5}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 30, y: 0, z: 0}
|
||||
--- !u!114 &1079771974
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1079771970}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
m_Version: 2
|
||||
--- !u!1 &1177134822
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1177134827}
|
||||
- component: {fileID: 1177134826}
|
||||
- component: {fileID: 1177134825}
|
||||
- component: {fileID: 1177134824}
|
||||
- component: {fileID: 1177134828}
|
||||
m_Layer: 0
|
||||
m_Name: Cube_robotCtn
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!65 &1177134824
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177134822}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1177134825
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177134822}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1177134826
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177134822}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &1177134827
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177134822}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalPosition: {x: 2.53, y: 0.5, z: 0.42}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1177134828
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177134822}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 95cc4aa0987ca5645bae8bdaf4dd0a28, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKPackEquipInfoTracker
|
||||
uiDocument: {fileID: 1306527162}
|
||||
equipType: 1
|
||||
worldYOffset: 1
|
||||
outlineScale: 1.05
|
||||
outlineColor: {r: 0.2, g: 0.5, b: 1, a: 0.4}
|
||||
--- !u!1 &1177232402
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1177232406}
|
||||
- component: {fileID: 1177232405}
|
||||
- component: {fileID: 1177232404}
|
||||
- component: {fileID: 1177232403}
|
||||
m_Layer: 0
|
||||
m_Name: Stage
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!64 &1177232403
|
||||
MeshCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177232402}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 5
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!23 &1177232404
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177232402}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1177232405
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177232402}
|
||||
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &1177232406
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1177232402}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 10, y: 1, z: 10}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1191611111
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1191611116}
|
||||
- component: {fileID: 1191611115}
|
||||
- component: {fileID: 1191611114}
|
||||
- component: {fileID: 1191611113}
|
||||
- component: {fileID: 1191611117}
|
||||
m_Layer: 0
|
||||
m_Name: Cube_loadingRobot
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!65 &1191611113
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1191611111}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1191611114
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1191611111}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1191611115
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1191611111}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &1191611116
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1191611111}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 5.09, y: 0.5, z: 2.66}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1191611117
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1191611111}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 95cc4aa0987ca5645bae8bdaf4dd0a28, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKPackEquipInfoTracker
|
||||
uiDocument: {fileID: 1306527162}
|
||||
equipType: 2
|
||||
worldYOffset: 1
|
||||
outlineScale: 1.05
|
||||
outlineColor: {r: 0.2, g: 0.5, b: 1, a: 0.4}
|
||||
--- !u!1 &1306527161
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -597,6 +1160,136 @@ MonoBehaviour:
|
||||
m_PivotReferenceSize: 0
|
||||
m_Pivot: 0
|
||||
m_WorldSpaceCollider: {fileID: 0}
|
||||
--- !u!1 &2034494620
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2034494625}
|
||||
- component: {fileID: 2034494624}
|
||||
- component: {fileID: 2034494623}
|
||||
- component: {fileID: 2034494622}
|
||||
- component: {fileID: 2034494626}
|
||||
m_Layer: 0
|
||||
m_Name: Cube_capping
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!65 &2034494622
|
||||
BoxCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2034494620}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Size: {x: 1, y: 1, z: 1}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &2034494623
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2034494620}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &2034494624
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2034494620}
|
||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!4 &2034494625
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2034494620}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0.91, y: 0.5, z: -1.35}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &2034494626
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2034494620}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 95cc4aa0987ca5645bae8bdaf4dd0a28, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKPackEquipInfoTracker
|
||||
uiDocument: {fileID: 1306527162}
|
||||
equipType: 0
|
||||
worldYOffset: 1
|
||||
outlineScale: 1.05
|
||||
outlineColor: {r: 0.2, g: 0.5, b: 1, a: 0.4}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -608,3 +1301,8 @@ SceneRoots:
|
||||
- {fileID: 1840260739}
|
||||
- {fileID: 883201337}
|
||||
- {fileID: 1664424033}
|
||||
- {fileID: 1177232406}
|
||||
- {fileID: 164967191}
|
||||
- {fileID: 2034494625}
|
||||
- {fileID: 1177134827}
|
||||
- {fileID: 1191611116}
|
||||
|
||||
52
Assets/Scripts/EnglewoodLAB/Data/EWLKCappingDetailData.cs
Normal file
52
Assets/Scripts/EnglewoodLAB/Data/EWLKCappingDetailData.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UVC.EnglewoodLAB.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Capping 상세 패널 — 구분/측정/설정 행 데이터.
|
||||
/// </summary>
|
||||
public class CappingMeasureSettingRow
|
||||
{
|
||||
/// <summary>구분 (예: "Home")</summary>
|
||||
public string Category { get; set; } = "-";
|
||||
|
||||
/// <summary>측정값</summary>
|
||||
public string Measured { get; set; } = "-";
|
||||
|
||||
/// <summary>설정값</summary>
|
||||
public string Setting { get; set; } = "-";
|
||||
|
||||
public CappingMeasureSettingRow() { }
|
||||
|
||||
public CappingMeasureSettingRow(string category, string measured = "-", string setting = "-")
|
||||
{
|
||||
Category = category;
|
||||
Measured = measured;
|
||||
Setting = setting;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capping 상세 패널 전체 데이터.
|
||||
/// 설비 정보 + 가동 토글 + 구분/측정/설정 테이블.
|
||||
/// </summary>
|
||||
public class EWLKCappingDetailData
|
||||
{
|
||||
/// <summary>설비명</summary>
|
||||
public string EquipmentName { get; set; } = "Capping";
|
||||
|
||||
/// <summary>가동 여부 (읽기 전용 토글 표시용)</summary>
|
||||
public bool IsRunning { get; set; }
|
||||
|
||||
/// <summary>구분/측정/설정 테이블 (5행)</summary>
|
||||
public List<CappingMeasureSettingRow> MeasureSettingRows { get; } = new()
|
||||
{
|
||||
new("Home"),
|
||||
new("상승대기"),
|
||||
new("하강"),
|
||||
new("High Speed"),
|
||||
new("Low Speed"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2b078b543c790746b4a43ba7a9b0946
|
||||
172
Assets/Scripts/EnglewoodLAB/Data/EWLKManuEquipDetailData.cs
Normal file
172
Assets/Scripts/EnglewoodLAB/Data/EWLKManuEquipDetailData.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UVC.EnglewoodLAB.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 설비 상세 패널 전체 데이터.
|
||||
/// </summary>
|
||||
public class EWLKManuEquipDetailData
|
||||
{
|
||||
public EWLKManuEquipBasicInfoData BasicInfo { get; } = new();
|
||||
public EWLKQualityInfoData QualityInfo { get; } = new();
|
||||
public EWLKManuEquipOperationData OperationInfo { get; } = new();
|
||||
public EWLKMainMixerData MainMixer { get; } = new();
|
||||
}
|
||||
|
||||
// ── 설비 기본 정보 ──────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 설비 기본 정보 패널 데이터.
|
||||
/// </summary>
|
||||
public class EWLKManuEquipBasicInfoData
|
||||
{
|
||||
/// <summary>설비명</summary>
|
||||
public string EquipmentName { get; set; } = "-";
|
||||
|
||||
/// <summary>설비 CAPA (예: "999.9 / 999.9")</summary>
|
||||
public string Capacity { get; set; } = "-";
|
||||
|
||||
/// <summary>설비 위치 (예: "내관 1층(제조) / 01라인")</summary>
|
||||
public string Location { get; set; } = "-";
|
||||
}
|
||||
|
||||
// ── 품질 정보 ───────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 품질 목표 항목 한 행.
|
||||
/// </summary>
|
||||
public class QualityTargetItem
|
||||
{
|
||||
/// <summary>항목명 (예: "목표 정보")</summary>
|
||||
public string Label { get; set; } = "목표 정보";
|
||||
|
||||
/// <summary>범위 값 (예: "99 ~ 99")</summary>
|
||||
public string Range { get; set; } = "-";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품질 정보 패널 데이터.
|
||||
/// </summary>
|
||||
public class EWLKQualityInfoData
|
||||
{
|
||||
public List<QualityTargetItem> Targets { get; } = new()
|
||||
{
|
||||
new QualityTargetItem(),
|
||||
new QualityTargetItem(),
|
||||
};
|
||||
}
|
||||
|
||||
// ── 설비 운영 정보 ──────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 공정/상별 용해 항목.
|
||||
/// </summary>
|
||||
public class OperationInfoItem
|
||||
{
|
||||
/// <summary>품번</summary>
|
||||
public string ProductNo { get; set; } = "-";
|
||||
|
||||
/// <summary>품명</summary>
|
||||
public string ProductName { get; set; } = "-";
|
||||
|
||||
/// <summary>로트(LOT)</summary>
|
||||
public string LotNo { get; set; } = "-";
|
||||
|
||||
/// <summary>제조량</summary>
|
||||
public string ProductionQty { get; set; } = "-";
|
||||
|
||||
/// <summary>작업자</summary>
|
||||
public string Worker { get; set; } = "-";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 설비 운영 정보 패널 데이터.
|
||||
/// </summary>
|
||||
public class EWLKManuEquipOperationData
|
||||
{
|
||||
/// <summary>공정 정보</summary>
|
||||
public OperationInfoItem ProcessInfo { get; } = new();
|
||||
|
||||
/// <summary>상별 용해</summary>
|
||||
public OperationInfoItem MeltInfo { get; } = new();
|
||||
}
|
||||
|
||||
// ── 메인 믹서 ───────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 설비 가동 상태 항목 (가동/비가동 표시용).
|
||||
/// </summary>
|
||||
public class EquipRunStatusItem
|
||||
{
|
||||
public string Label { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
public EquipRunStatusItem(string label, bool isActive = false)
|
||||
{
|
||||
Label = label;
|
||||
IsActive = isActive;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메인 믹서 패널 데이터.
|
||||
/// 측정값(검정) / 세팅값(파랑) / 단위(검정)로 색상 구분됩니다.
|
||||
/// </summary>
|
||||
public class EWLKMainMixerData
|
||||
{
|
||||
// ── 측정값만 (검정) ──────────────────────────────
|
||||
|
||||
/// <summary>자켓 온도 — 측정값</summary>
|
||||
public string JacketTemp { get; set; } = "0.0";
|
||||
|
||||
/// <summary>상부 온도 — 측정값</summary>
|
||||
public string UpperTemp { get; set; } = "0.0";
|
||||
|
||||
// ── 측정값 + 세팅값 (검정 / 파랑) ────────────────
|
||||
|
||||
/// <summary>온도 — 측정값</summary>
|
||||
public string TempMeasured { get; set; } = "0.0";
|
||||
/// <summary>온도 — 세팅값</summary>
|
||||
public string TempSetting { get; set; } = "0.0";
|
||||
|
||||
/// <summary>HOMO — 측정값 (RPM)</summary>
|
||||
public string HomoMeasured { get; set; } = "0";
|
||||
/// <summary>HOMO — 세팅값 (RPM)</summary>
|
||||
public string HomoSetting { get; set; } = "0";
|
||||
|
||||
/// <summary>PAD — 측정값 (RPM)</summary>
|
||||
public string PadMeasured { get; set; } = "0";
|
||||
/// <summary>PAD — 세팅값 (RPM)</summary>
|
||||
public string PadSetting { get; set; } = "0";
|
||||
|
||||
/// <summary>SCR — 측정값 (RPM)</summary>
|
||||
public string ScrMeasured { get; set; } = "0";
|
||||
/// <summary>SCR — 세팅값 (RPM)</summary>
|
||||
public string ScrSetting { get; set; } = "0";
|
||||
|
||||
/// <summary>설비내 압력 — 측정값 (mmHg)</summary>
|
||||
public string PressureMeasured { get; set; } = "0.0";
|
||||
/// <summary>설비내 압력 — 세팅값 (mmHg)</summary>
|
||||
public string PressureSetting { get; set; } = "0.0";
|
||||
|
||||
// ── 세팅값만 (파랑) ──────────────────────────────
|
||||
|
||||
/// <summary>가열온도 설정 — 세팅값</summary>
|
||||
public string HeatingTempSetting { get; set; } = "0.0";
|
||||
|
||||
/// <summary>냉각온도 설정 — 세팅값</summary>
|
||||
public string CoolingTempSetting { get; set; } = "0.0";
|
||||
|
||||
// ── 단일 측정값 ──────────────────────────────────
|
||||
|
||||
/// <summary>가동 상태 항목 목록 (진공시작, 진공파기, 배출시작 등)</summary>
|
||||
public List<EquipRunStatusItem> RunStatuses { get; } = new()
|
||||
{
|
||||
new("진공시작"),
|
||||
new("진공파기"),
|
||||
new("배출시작"),
|
||||
new("탱크벨트"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b171558f2adbd54b8f22b6c2672ab68
|
||||
29
Assets/Scripts/EnglewoodLAB/Data/EWLKManuEquipInfoData.cs
Normal file
29
Assets/Scripts/EnglewoodLAB/Data/EWLKManuEquipInfoData.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
#nullable enable
|
||||
|
||||
namespace UVC.EnglewoodLAB.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 설비 상태 열거형.
|
||||
/// </summary>
|
||||
public enum EquipmentStatus
|
||||
{
|
||||
/// <summary>가동 (초록)</summary>
|
||||
Running,
|
||||
/// <summary>비가동 (노랑)</summary>
|
||||
Idle,
|
||||
/// <summary>계획 정지 (회색)</summary>
|
||||
PlannedStop,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 설비 정보 팝업에 표시할 데이터.
|
||||
/// </summary>
|
||||
public class EWLKManuEquipInfoData
|
||||
{
|
||||
/// <summary>설비 상태</summary>
|
||||
public EquipmentStatus Status { get; set; } = EquipmentStatus.Running;
|
||||
|
||||
/// <summary>설비명 (예: M001)</summary>
|
||||
public string EquipmentName { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 426fb0aa797442849b6434b959f94739
|
||||
84
Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderData.cs
Normal file
84
Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderData.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UVC.EnglewoodLAB.Data
|
||||
{
|
||||
/// <summary>제조지시현황 요약 데이터 (라인별)</summary>
|
||||
public class EWLKMfgOrderSummaryData
|
||||
{
|
||||
/// <summary>공장구분 (탭 레이블로 사용)</summary>
|
||||
public string FactoryName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>전체 제조 목표량</summary>
|
||||
public string TotalTarget { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>실제 실적량</summary>
|
||||
public string TotalActual { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>전체 제조 진척률 (0~100)</summary>
|
||||
public float TotalProgress { get; set; }
|
||||
|
||||
/// <summary>일 설비가동률 가중대수% (0~100)</summary>
|
||||
public float EquipRate { get; set; }
|
||||
|
||||
/// <summary>가동 중 설비 수</summary>
|
||||
public int ActiveCount { get; set; }
|
||||
|
||||
/// <summary>전체 설비 수</summary>
|
||||
public int TotalCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>설비별 제조지시 행 데이터</summary>
|
||||
public class EWLKMfgOrderRowData
|
||||
{
|
||||
/// <summary>설비명</summary>
|
||||
public string EquipName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>지시번호</summary>
|
||||
public string OrderNo { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>품목코드</summary>
|
||||
public string ItemCode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>품목명</summary>
|
||||
public string ItemName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>시작시간</summary>
|
||||
public string StartTime { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>목표량</summary>
|
||||
public string Target { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>달성률 (0~100). IsStopped가 true면 사용되지 않음</summary>
|
||||
public float Achievement { get; set; }
|
||||
|
||||
/// <summary>계획 정지 여부 (true면 달성률 대신 "계획 정지" 표시)</summary>
|
||||
public bool IsStopped { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>공장 라인별 제조지시 데이터 (요약 + 행 목록)</summary>
|
||||
public class EWLKMfgOrderLineData
|
||||
{
|
||||
/// <summary>기본 라인 이름 (MQTT summary 수신 전 초기값)</summary>
|
||||
public string DefaultName { get; }
|
||||
|
||||
/// <summary>요약 데이터</summary>
|
||||
public EWLKMfgOrderSummaryData Summary { get; } = new();
|
||||
|
||||
/// <summary>설비별 행 목록 (ListView itemsSource)</summary>
|
||||
public List<EWLKMfgOrderRowData> Rows { get; } = new();
|
||||
|
||||
/// <param name="defaultName">MQTT 수신 전 초기 탭 레이블</param>
|
||||
public EWLKMfgOrderLineData(string defaultName) => DefaultName = defaultName;
|
||||
}
|
||||
|
||||
/// <summary>제조지시현황 전체 데이터 (2개 라인)</summary>
|
||||
public class EWLKMfgOrderData
|
||||
{
|
||||
/// <summary>라인 1</summary>
|
||||
public EWLKMfgOrderLineData Line1 { get; } = new("라인 1");
|
||||
|
||||
/// <summary>라인 2</summary>
|
||||
public EWLKMfgOrderLineData Line2 { get; } = new("라인 2");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3db028a33aa08d4c843705db64e7550
|
||||
205
Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderMqttService.cs
Normal file
205
Assets/Scripts/EnglewoodLAB/Data/EWLKMfgOrderMqttService.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Mqtt;
|
||||
|
||||
namespace UVC.EnglewoodLAB.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 제조지시현황 데이터를 MQTT로 수신하는 서비스.
|
||||
/// 2개 라인의 요약 및 설비 행 데이터를 구독합니다.
|
||||
/// DataRepository.Instance.MqttReceiver(공유 수신기)를 사용합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// MQTT 토픽 구조:
|
||||
/// ewlk/mfgorder/line1/summary — 라인1 요약 (DataObject)
|
||||
/// ewlk/mfgorder/line1/rows — 라인1 설비 행 배열 (DataArray)
|
||||
/// ewlk/mfgorder/line2/summary — 라인2 요약 (DataObject)
|
||||
/// ewlk/mfgorder/line2/rows — 라인2 설비 행 배열 (DataArray)
|
||||
///
|
||||
/// Summary JSON 스키마:
|
||||
/// <code>
|
||||
/// {
|
||||
/// "factory_name": "2공장 제조1",
|
||||
/// "total_target": "47,910.2 Kg",
|
||||
/// "total_actual": "38,889.0 Kg",
|
||||
/// "total_progress": 34.2,
|
||||
/// "equip_rate": 0.0,
|
||||
/// "active_count": 0,
|
||||
/// "total_count": 47
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// Rows JSON 스키마 (배열):
|
||||
/// <code>
|
||||
/// [
|
||||
/// {
|
||||
/// "equip_name": "UHM50",
|
||||
/// "order_no": "-",
|
||||
/// "item_code": "-",
|
||||
/// "item_name": "-",
|
||||
/// "start_time": "-",
|
||||
/// "target": "-",
|
||||
/// "achievement": 0.0,
|
||||
/// "status": "stopped"
|
||||
/// },
|
||||
/// ...
|
||||
/// ]
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public class EWLKMfgOrderMqttService : IDisposable
|
||||
{
|
||||
// ── MQTT 토픽 ──────────────────────────────────────────────
|
||||
private const string TopicLine1Summary = "ewlk/mfgorder/line1/summary";
|
||||
private const string TopicLine1Rows = "ewlk/mfgorder/line1/rows";
|
||||
private const string TopicLine2Summary = "ewlk/mfgorder/line2/summary";
|
||||
private const string TopicLine2Rows = "ewlk/mfgorder/line2/rows";
|
||||
|
||||
// ── DataMask ──────────────────────────────────────────────
|
||||
private static readonly DataMask s_SummaryMask = CreateSummaryMask();
|
||||
private static readonly DataMask s_RowMask = CreateRowMask();
|
||||
|
||||
private bool _subscribed;
|
||||
private bool _disposed;
|
||||
|
||||
// ── 공개 API ──────────────────────────────────────────────
|
||||
|
||||
/// <summary>가장 최근에 수신한 제조지시 데이터</summary>
|
||||
public EWLKMfgOrderData CurrentData { get; } = new();
|
||||
|
||||
/// <summary>라인 데이터(요약 또는 행)가 갱신될 때 발생 — 메인 스레드에서 호출됩니다.</summary>
|
||||
public event Action<EWLKMfgOrderData>? OnDataUpdated;
|
||||
|
||||
// ── 구독 관리 ──────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// DataRepository 공유 수신기에 4개 토픽을 등록합니다.
|
||||
/// MqttReceiver.Start()는 EWLKSceneMain에서 호출합니다.
|
||||
/// </summary>
|
||||
public void Subscribe()
|
||||
{
|
||||
if (_subscribed || _disposed) return;
|
||||
_subscribed = true;
|
||||
|
||||
var receiver = DataRepository.Instance.MqttReceiver;
|
||||
receiver.Add(BuildSummaryConfig(TopicLine1Summary,
|
||||
data => UpdateSummary(CurrentData.Line1, data)));
|
||||
receiver.Add(BuildRowsConfig(TopicLine1Rows,
|
||||
data => UpdateRows(CurrentData.Line1, data)));
|
||||
receiver.Add(BuildSummaryConfig(TopicLine2Summary,
|
||||
data => UpdateSummary(CurrentData.Line2, data)));
|
||||
receiver.Add(BuildRowsConfig(TopicLine2Rows,
|
||||
data => UpdateRows(CurrentData.Line2, data)));
|
||||
}
|
||||
|
||||
/// <summary>DataRepository 공유 수신기에서 4개 토픽 구독을 해제합니다.</summary>
|
||||
public void Unsubscribe()
|
||||
{
|
||||
if (!_subscribed) return;
|
||||
_subscribed = false;
|
||||
|
||||
var receiver = DataRepository.Instance.MqttReceiver;
|
||||
receiver.Remove(TopicLine1Summary);
|
||||
receiver.Remove(TopicLine1Rows);
|
||||
receiver.Remove(TopicLine2Summary);
|
||||
receiver.Remove(TopicLine2Rows);
|
||||
}
|
||||
|
||||
// ── 내부 구현 ──────────────────────────────────────────────
|
||||
|
||||
private static DataMask CreateSummaryMask()
|
||||
{
|
||||
var mask = new DataMask();
|
||||
mask["factory_name"] = "";
|
||||
mask["total_target"] = "";
|
||||
mask["total_actual"] = "";
|
||||
mask["total_progress"] = 0.0f;
|
||||
mask["equip_rate"] = 0.0f;
|
||||
mask["active_count"] = 0;
|
||||
mask["total_count"] = 0;
|
||||
return mask;
|
||||
}
|
||||
|
||||
private static DataMask CreateRowMask()
|
||||
{
|
||||
var mask = new DataMask();
|
||||
mask["equip_name"] = "";
|
||||
mask["order_no"] = "";
|
||||
mask["item_code"] = "";
|
||||
mask["item_name"] = "";
|
||||
mask["start_time"] = "";
|
||||
mask["target"] = "";
|
||||
mask["achievement"] = 0.0f;
|
||||
mask["status"] = ""; // "running" | "stopped"
|
||||
return mask;
|
||||
}
|
||||
|
||||
private static MqttSubscriptionConfig BuildSummaryConfig(
|
||||
string topic, Action<IDataObject?> handler) =>
|
||||
new MqttSubscriptionConfig(topic)
|
||||
.SetDataMapper(new DataMapper(s_SummaryMask))
|
||||
.SetHandler(handler);
|
||||
|
||||
private static MqttSubscriptionConfig BuildRowsConfig(
|
||||
string topic, Action<IDataObject?> handler) =>
|
||||
new MqttSubscriptionConfig(topic)
|
||||
.SetDataMapper(new DataMapper(s_RowMask))
|
||||
.SetHandler(handler);
|
||||
|
||||
/// <summary>요약 DataObject를 SummaryData에 반영합니다.</summary>
|
||||
private void UpdateSummary(EWLKMfgOrderLineData line, IDataObject? data)
|
||||
{
|
||||
if (data is not DataObject obj) return;
|
||||
|
||||
var s = line.Summary;
|
||||
s.FactoryName = obj.GetString("factory_name") ?? string.Empty;
|
||||
s.TotalTarget = obj.GetString("total_target") ?? string.Empty;
|
||||
s.TotalActual = obj.GetString("total_actual") ?? string.Empty;
|
||||
s.TotalProgress = obj.GetFloat("total_progress") ?? 0f;
|
||||
s.EquipRate = obj.GetFloat("equip_rate") ?? 0f;
|
||||
s.ActiveCount = obj.GetInt("active_count") ?? 0;
|
||||
s.TotalCount = obj.GetInt("total_count") ?? 0;
|
||||
|
||||
OnDataUpdated?.Invoke(CurrentData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 행 DataArray를 파싱하여 Rows를 교체합니다.
|
||||
/// DataArray는 List<DataObject>를 상속합니다.
|
||||
/// </summary>
|
||||
private void UpdateRows(EWLKMfgOrderLineData line, IDataObject? data)
|
||||
{
|
||||
if (data is not DataArray arr) return;
|
||||
|
||||
line.Rows.Clear();
|
||||
foreach (var obj in arr)
|
||||
{
|
||||
line.Rows.Add(new EWLKMfgOrderRowData
|
||||
{
|
||||
EquipName = obj.GetString("equip_name") ?? string.Empty,
|
||||
OrderNo = obj.GetString("order_no") ?? string.Empty,
|
||||
ItemCode = obj.GetString("item_code") ?? string.Empty,
|
||||
ItemName = obj.GetString("item_name") ?? string.Empty,
|
||||
StartTime = obj.GetString("start_time") ?? string.Empty,
|
||||
Target = obj.GetString("target") ?? string.Empty,
|
||||
Achievement = obj.GetFloat("achievement") ?? 0f,
|
||||
IsStopped = (obj.GetString("status") ?? "") == "stopped",
|
||||
});
|
||||
}
|
||||
|
||||
OnDataUpdated?.Invoke(CurrentData);
|
||||
}
|
||||
|
||||
// ── IDisposable ────────────────────────────────────────────
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
Unsubscribe();
|
||||
OnDataUpdated = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db642e1ce759b354aa8d0b4dd0f64084
|
||||
38
Assets/Scripts/EnglewoodLAB/Data/EWLKPackEquipInfoData.cs
Normal file
38
Assets/Scripts/EnglewoodLAB/Data/EWLKPackEquipInfoData.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UVC.EnglewoodLAB.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 충포장 설비 팝업 데이터 항목.
|
||||
/// </summary>
|
||||
public class PackEquipInfoItem
|
||||
{
|
||||
/// <summary>항목 라벨 (예: "토크 L")</summary>
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>값 (예: "0.0")</summary>
|
||||
public string Value { get; set; } = "0.0";
|
||||
|
||||
public PackEquipInfoItem() { }
|
||||
|
||||
public PackEquipInfoItem(string label, string value = "0.0")
|
||||
{
|
||||
Label = label;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 충포장 설비 정보 팝업 데이터.
|
||||
/// 타이틀 + 데이터 항목 리스트로 구성됩니다.
|
||||
/// </summary>
|
||||
public class EWLKPackEquipInfoData
|
||||
{
|
||||
/// <summary>설비 타이틀 (예: "Capping", "로봇카토너")</summary>
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>표시할 데이터 항목들</summary>
|
||||
public List<PackEquipInfoItem> Items { get; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae76d48b92ccf2a4e86d4e09be3e8933
|
||||
156
Assets/Scripts/EnglewoodLAB/EWLKManuEquipInfoTracker.cs
Normal file
156
Assets/Scripts/EnglewoodLAB/EWLKManuEquipInfoTracker.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
using UVC.EnglewoodLAB.UIToolkit;
|
||||
|
||||
namespace UVC.EnglewoodLAB
|
||||
{
|
||||
/// <summary>
|
||||
/// 3D 오브젝트 위에 설비 정보 팝업을 표시하는 트래커.
|
||||
/// 이 컴포넌트를 설비 오브젝트(예: Cube)에 부착하고,
|
||||
/// Inspector에서 UIDocument(DynamicUI)를 연결합니다.
|
||||
/// </summary>
|
||||
public class EWLKManuEquipInfoTracker : MonoBehaviour
|
||||
{
|
||||
[Header("UI")]
|
||||
[Tooltip("팝업을 표시할 UIDocument (DynamicUI 권장)")]
|
||||
[SerializeField] private UIDocument? uiDocument;
|
||||
|
||||
[Header("설비 데이터")]
|
||||
[Tooltip("설비명")]
|
||||
[SerializeField] private string equipmentName = "M001";
|
||||
|
||||
[Tooltip("설비 상태")]
|
||||
[SerializeField] private EquipmentStatus equipmentStatus = EquipmentStatus.Running;
|
||||
|
||||
[Header("위치 설정")]
|
||||
[Tooltip("오브젝트 상단으로부터 Y 오프셋 (월드 단위)")]
|
||||
[SerializeField] private float worldYOffset = 1.0f;
|
||||
|
||||
// ── 내부 상태 ─────────────────────────────────
|
||||
private EWLKManuEquipInfoPopup? _popup;
|
||||
private EWLKManuEquipInfoData _data = new();
|
||||
private Camera? _mainCamera;
|
||||
private VisualElement? _root;
|
||||
private bool _attached;
|
||||
|
||||
// ────────────────────────────────────────────
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
|
||||
if (uiDocument == null)
|
||||
{
|
||||
Debug.LogWarning($"[EWLKManuEquipInfoTracker] {name}: UIDocument가 할당되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// rootVisualElement가 아직 준비되지 않았을 수 있으므로 대기
|
||||
if (uiDocument.rootVisualElement != null)
|
||||
AttachPopup();
|
||||
else
|
||||
uiDocument.rootVisualElement?.RegisterCallback<AttachToPanelEvent>(_ => AttachPopup());
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// OnEnable에서 root가 없었던 경우 Start에서 재시도
|
||||
if (!_attached && uiDocument != null && uiDocument.rootVisualElement != null)
|
||||
AttachPopup();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
DetachPopup();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (_popup == null || _mainCamera == null || !_attached) return;
|
||||
|
||||
Vector3 worldPos = transform.position + Vector3.up * worldYOffset;
|
||||
|
||||
// 카메라 뒤에 있으면 숨김
|
||||
Vector3 toTarget = (worldPos - _mainCamera.transform.position).normalized;
|
||||
if (Vector3.Dot(_mainCamera.transform.forward, toTarget) <= 0)
|
||||
{
|
||||
_popup.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.style.display = DisplayStyle.Flex;
|
||||
|
||||
// 월드 → 스크린 → 패널 좌표 변환
|
||||
Vector3 screenPos = _mainCamera.WorldToScreenPoint(worldPos);
|
||||
|
||||
// UI Toolkit: 좌상단 원점, Y 하향
|
||||
Vector2 flipped = new Vector2(screenPos.x, Screen.height - screenPos.y);
|
||||
|
||||
if (_popup.panel != null)
|
||||
{
|
||||
Vector2 panelPos = RuntimePanelUtils.ScreenToPanel(_popup.panel, flipped);
|
||||
|
||||
// 팝업 중앙이 오브젝트 위에 오도록 보정
|
||||
_popup.style.left = panelPos.x;
|
||||
_popup.style.top = panelPos.y;
|
||||
_popup.style.translate = new Translate(Length.Percent(-50), Length.Percent(-100));
|
||||
}
|
||||
}
|
||||
|
||||
// ── 팝업 부착/해제 ─────────────────────────────
|
||||
|
||||
private void AttachPopup()
|
||||
{
|
||||
if (_attached) return;
|
||||
|
||||
_root = uiDocument?.rootVisualElement;
|
||||
if (_root == null) return;
|
||||
|
||||
_popup = new EWLKManuEquipInfoPopup();
|
||||
ApplyData();
|
||||
_root.Add(_popup);
|
||||
_attached = true;
|
||||
}
|
||||
|
||||
private void DetachPopup()
|
||||
{
|
||||
if (_popup != null && _root != null)
|
||||
{
|
||||
_root.Remove(_popup);
|
||||
}
|
||||
_popup = null;
|
||||
_attached = false;
|
||||
}
|
||||
|
||||
// ── 데이터 적용 ─────────────────────────────────
|
||||
|
||||
private void ApplyData()
|
||||
{
|
||||
_data.EquipmentName = equipmentName;
|
||||
_data.Status = equipmentStatus;
|
||||
_popup?.SetData(_data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 외부에서 설비 데이터를 업데이트합니다.
|
||||
/// </summary>
|
||||
public void UpdateData(EWLKManuEquipInfoData data)
|
||||
{
|
||||
_data = data;
|
||||
_popup?.SetData(data);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Inspector 값 변경 시 실시간 반영 (에디터 전용).
|
||||
/// </summary>
|
||||
private void OnValidate()
|
||||
{
|
||||
if (_popup != null && Application.isPlaying)
|
||||
ApplyData();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06942d388a19bcf458c4f333f1afc27a
|
||||
174
Assets/Scripts/EnglewoodLAB/EWLKManuEquipSelectHandler.cs
Normal file
174
Assets/Scripts/EnglewoodLAB/EWLKManuEquipSelectHandler.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
using UVC.EnglewoodLAB.UIToolkit;
|
||||
|
||||
namespace UVC.EnglewoodLAB
|
||||
{
|
||||
/// <summary>
|
||||
/// 설비 오브젝트(예: Cube)에 부착하여 클릭 시 상세 패널을 표시합니다.
|
||||
/// 선택 시 파란 아웃라인을 표시하고, 선택 해제 시 모두 제거합니다.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class EWLKManuEquipSelectHandler : MonoBehaviour
|
||||
{
|
||||
[Header("UI")]
|
||||
[Tooltip("패널을 표시할 UIDocument (DynamicUI 권장)")]
|
||||
[SerializeField] private UIDocument? uiDocument;
|
||||
|
||||
[Header("아웃라인")]
|
||||
[Tooltip("아웃라인 두께 (스케일 배율)")]
|
||||
[SerializeField] private float outlineScale = 1.05f;
|
||||
|
||||
[Tooltip("아웃라인 색상")]
|
||||
[SerializeField] private Color outlineColor = new(0.2f, 0.5f, 1f, 0.4f);
|
||||
|
||||
// ── 상세 패널 활성 상태 (다른 핸들러에서 클릭 차단용) ──
|
||||
/// <summary>상세 패널이 열려 있는지 여부</summary>
|
||||
public static bool IsPanelActive { get; private set; }
|
||||
|
||||
// ── 내부 상태 ─────────────────────────────────
|
||||
private EWLKManuEquipDetailPanel? _panel;
|
||||
private EWLKManuEquipDetailData _data = new();
|
||||
private GameObject? _outlineObject;
|
||||
private bool _selected;
|
||||
private VisualElement? _root;
|
||||
|
||||
// ────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 외부에서 표시할 데이터를 설정합니다.
|
||||
/// </summary>
|
||||
public void SetData(EWLKManuEquipDetailData data)
|
||||
{
|
||||
_data = data;
|
||||
_panel?.SetData(data);
|
||||
}
|
||||
|
||||
// ── Unity 생명주기 ──────────────────────────────
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Deselect();
|
||||
DestroyOutlineObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 클릭 감지. Collider 필수.
|
||||
/// </summary>
|
||||
private void OnMouseDown()
|
||||
{
|
||||
if (_selected) return;
|
||||
if (EWLKPackEquipInfoTracker.IsPanelActive) return;
|
||||
Select();
|
||||
}
|
||||
|
||||
// ── 선택/해제 ──────────────────────────────────
|
||||
|
||||
/// <summary>오브젝트를 선택합니다.</summary>
|
||||
public void Select()
|
||||
{
|
||||
if (_selected) return;
|
||||
_selected = true;
|
||||
IsPanelActive = true;
|
||||
|
||||
ShowOutline();
|
||||
ShowPanel();
|
||||
}
|
||||
|
||||
/// <summary>오브젝트 선택을 해제합니다.</summary>
|
||||
public void Deselect()
|
||||
{
|
||||
if (!_selected) return;
|
||||
_selected = false;
|
||||
IsPanelActive = false;
|
||||
|
||||
HideOutline();
|
||||
HidePanel();
|
||||
}
|
||||
|
||||
// ── 아웃라인 ───────────────────────────────────
|
||||
|
||||
private void ShowOutline()
|
||||
{
|
||||
if (_outlineObject == null)
|
||||
CreateOutlineObject();
|
||||
|
||||
if (_outlineObject != null)
|
||||
_outlineObject.SetActive(true);
|
||||
}
|
||||
|
||||
private void HideOutline()
|
||||
{
|
||||
if (_outlineObject != null)
|
||||
_outlineObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void CreateOutlineObject()
|
||||
{
|
||||
var meshFilter = GetComponent<MeshFilter>();
|
||||
if (meshFilter == null || meshFilter.sharedMesh == null) return;
|
||||
|
||||
_outlineObject = new GameObject("_Outline");
|
||||
_outlineObject.transform.SetParent(transform, false);
|
||||
_outlineObject.transform.localScale = Vector3.one * outlineScale;
|
||||
|
||||
// 메시 복제
|
||||
var outlineMF = _outlineObject.AddComponent<MeshFilter>();
|
||||
outlineMF.sharedMesh = meshFilter.sharedMesh;
|
||||
|
||||
// 반투명 파란 머티리얼
|
||||
var mat = new Material(Shader.Find("Sprites/Default"));
|
||||
mat.color = outlineColor;
|
||||
mat.renderQueue = 3000;
|
||||
|
||||
var renderer = _outlineObject.AddComponent<MeshRenderer>();
|
||||
renderer.material = mat;
|
||||
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
renderer.receiveShadows = false;
|
||||
|
||||
_outlineObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void DestroyOutlineObject()
|
||||
{
|
||||
if (_outlineObject != null)
|
||||
{
|
||||
Destroy(_outlineObject);
|
||||
_outlineObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 패널 ───────────────────────────────────────
|
||||
|
||||
private void ShowPanel()
|
||||
{
|
||||
if (uiDocument == null)
|
||||
{
|
||||
Debug.LogWarning($"[EWLKManuEquipSelectHandler] {name}: UIDocument가 할당되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
_root = uiDocument.rootVisualElement;
|
||||
if (_root == null) return;
|
||||
|
||||
_panel = new EWLKManuEquipDetailPanel();
|
||||
_panel.SetData(_data);
|
||||
_panel.OnDeselectClicked += Deselect;
|
||||
_root.Add(_panel);
|
||||
}
|
||||
|
||||
private void HidePanel()
|
||||
{
|
||||
if (_panel != null)
|
||||
{
|
||||
_panel.OnDeselectClicked -= Deselect;
|
||||
_root?.Remove(_panel);
|
||||
_panel.Dispose();
|
||||
_panel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 520526319be9b2d4985315191c400d59
|
||||
319
Assets/Scripts/EnglewoodLAB/EWLKPackEquipInfoTracker.cs
Normal file
319
Assets/Scripts/EnglewoodLAB/EWLKPackEquipInfoTracker.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
using UVC.EnglewoodLAB.UIToolkit;
|
||||
|
||||
namespace UVC.EnglewoodLAB
|
||||
{
|
||||
/// <summary>
|
||||
/// 충포장 설비 유형 열거형.
|
||||
/// </summary>
|
||||
public enum PackEquipType
|
||||
{
|
||||
/// <summary>Capping 설비 (클릭 시 상세 패널 표시)</summary>
|
||||
Capping,
|
||||
/// <summary>로봇카토너</summary>
|
||||
RobotCartoner,
|
||||
/// <summary>적재로봇</summary>
|
||||
LoadingRobot,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 3D 오브젝트 위에 충포장 설비 정보 팝업을 표시하는 트래커.
|
||||
/// Capping 설비는 클릭 시 상세 패널도 표시합니다.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class EWLKPackEquipInfoTracker : MonoBehaviour
|
||||
{
|
||||
[Header("UI")]
|
||||
[Tooltip("팝업을 표시할 UIDocument (DynamicUI 권장)")]
|
||||
[SerializeField] private UIDocument? uiDocument;
|
||||
|
||||
[Header("설비 설정")]
|
||||
[Tooltip("충포장 설비 유형")]
|
||||
[SerializeField] private PackEquipType equipType = PackEquipType.Capping;
|
||||
|
||||
[Header("위치 설정")]
|
||||
[Tooltip("오브젝트 상단으로부터 Y 오프셋 (월드 단위)")]
|
||||
[SerializeField] private float worldYOffset = 1.0f;
|
||||
|
||||
[Header("아웃라인")]
|
||||
[Tooltip("아웃라인 두께 (스케일 배율)")]
|
||||
[SerializeField] private float outlineScale = 1.05f;
|
||||
|
||||
[Tooltip("아웃라인 색상")]
|
||||
[SerializeField] private Color outlineColor = new(0.2f, 0.5f, 1f, 0.4f);
|
||||
|
||||
// ── 내부 상태 ─────────────────────────────────
|
||||
private EWLKPackEquipInfoPopup? _popup;
|
||||
private EWLKPackEquipInfoData _popupData = new();
|
||||
private Camera? _mainCamera;
|
||||
private VisualElement? _root;
|
||||
private bool _popupAttached;
|
||||
|
||||
// ── 상세 패널 활성 상태 (다른 핸들러에서 클릭 차단용) ──
|
||||
/// <summary>Capping 상세 패널이 열려 있는지 여부</summary>
|
||||
public static bool IsPanelActive { get; private set; }
|
||||
|
||||
// Capping 전용
|
||||
private EWLKCappingDetailPanel? _detailPanel;
|
||||
private EWLKCappingDetailData _cappingData = new();
|
||||
private GameObject? _outlineObject;
|
||||
private bool _selected;
|
||||
|
||||
// ────────────────────────────────────────────
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
InitPopupData();
|
||||
|
||||
if (uiDocument == null)
|
||||
{
|
||||
Debug.LogWarning($"[EWLKPackEquipInfoTracker] {name}: UIDocument가 할당되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (uiDocument.rootVisualElement != null)
|
||||
AttachPopup();
|
||||
else
|
||||
uiDocument.rootVisualElement?.RegisterCallback<AttachToPanelEvent>(_ => AttachPopup());
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (!_popupAttached && uiDocument != null && uiDocument.rootVisualElement != null)
|
||||
AttachPopup();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Deselect();
|
||||
DetachPopup();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyOutlineObject();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (_popup == null || _mainCamera == null || !_popupAttached) return;
|
||||
|
||||
Vector3 worldPos = transform.position + Vector3.up * worldYOffset;
|
||||
|
||||
// 카메라 뒤에 있으면 숨김
|
||||
Vector3 toTarget = (worldPos - _mainCamera.transform.position).normalized;
|
||||
if (Vector3.Dot(_mainCamera.transform.forward, toTarget) <= 0)
|
||||
{
|
||||
_popup.style.display = DisplayStyle.None;
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.style.display = DisplayStyle.Flex;
|
||||
|
||||
// 월드 → 스크린 → 패널 좌표 변환
|
||||
Vector3 screenPos = _mainCamera.WorldToScreenPoint(worldPos);
|
||||
Vector2 flipped = new Vector2(screenPos.x, Screen.height - screenPos.y);
|
||||
|
||||
if (_popup.panel != null)
|
||||
{
|
||||
Vector2 panelPos = RuntimePanelUtils.ScreenToPanel(_popup.panel, flipped);
|
||||
_popup.style.left = panelPos.x;
|
||||
_popup.style.top = panelPos.y;
|
||||
_popup.style.translate = new Translate(Length.Percent(-50), Length.Percent(-100));
|
||||
}
|
||||
}
|
||||
|
||||
// ── 클릭 (Capping만 상세 패널 표시) ──────────────
|
||||
|
||||
private void OnMouseDown()
|
||||
{
|
||||
if (equipType != PackEquipType.Capping) return;
|
||||
if (_selected) return;
|
||||
if (EWLKManuEquipSelectHandler.IsPanelActive) return;
|
||||
Select();
|
||||
}
|
||||
|
||||
// ── 선택/해제 ──────────────────────────────────
|
||||
|
||||
/// <summary>Capping 설비를 선택합니다.</summary>
|
||||
public void Select()
|
||||
{
|
||||
if (_selected) return;
|
||||
_selected = true;
|
||||
IsPanelActive = true;
|
||||
|
||||
ShowOutline();
|
||||
ShowDetailPanel();
|
||||
}
|
||||
|
||||
/// <summary>선택을 해제합니다.</summary>
|
||||
public void Deselect()
|
||||
{
|
||||
if (!_selected) return;
|
||||
_selected = false;
|
||||
IsPanelActive = false;
|
||||
|
||||
HideOutline();
|
||||
HideDetailPanel();
|
||||
}
|
||||
|
||||
// ── 팝업 데이터 초기화 ────────────────────────────
|
||||
|
||||
private void InitPopupData()
|
||||
{
|
||||
_popupData = new EWLKPackEquipInfoData();
|
||||
|
||||
switch (equipType)
|
||||
{
|
||||
case PackEquipType.Capping:
|
||||
_popupData.Title = "Capping";
|
||||
_popupData.Items.Add(new PackEquipInfoItem("토크 L:", "0.0"));
|
||||
_popupData.Items.Add(new PackEquipInfoItem("토크 R:", "0.0"));
|
||||
break;
|
||||
|
||||
case PackEquipType.RobotCartoner:
|
||||
_popupData.Title = "로봇카토너";
|
||||
_popupData.Items.Add(new PackEquipInfoItem("RPM", "0.0"));
|
||||
break;
|
||||
|
||||
case PackEquipType.LoadingRobot:
|
||||
_popupData.Title = "적재로봇";
|
||||
_popupData.Items.Add(new PackEquipInfoItem("적재완료 수량", "0.0"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 팝업 부착/해제 ─────────────────────────────
|
||||
|
||||
private void AttachPopup()
|
||||
{
|
||||
if (_popupAttached) return;
|
||||
|
||||
_root = uiDocument?.rootVisualElement;
|
||||
if (_root == null) return;
|
||||
|
||||
_popup = new EWLKPackEquipInfoPopup();
|
||||
_popup.SetData(_popupData);
|
||||
_root.Add(_popup);
|
||||
_popupAttached = true;
|
||||
}
|
||||
|
||||
private void DetachPopup()
|
||||
{
|
||||
if (_popup != null && _root != null)
|
||||
_root.Remove(_popup);
|
||||
|
||||
_popup = null;
|
||||
_popupAttached = false;
|
||||
}
|
||||
|
||||
// ── 아웃라인 ───────────────────────────────────
|
||||
|
||||
private void ShowOutline()
|
||||
{
|
||||
if (_outlineObject == null)
|
||||
CreateOutlineObject();
|
||||
|
||||
if (_outlineObject != null)
|
||||
_outlineObject.SetActive(true);
|
||||
}
|
||||
|
||||
private void HideOutline()
|
||||
{
|
||||
if (_outlineObject != null)
|
||||
_outlineObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void CreateOutlineObject()
|
||||
{
|
||||
var meshFilter = GetComponent<MeshFilter>();
|
||||
if (meshFilter == null || meshFilter.sharedMesh == null) return;
|
||||
|
||||
_outlineObject = new GameObject("_Outline");
|
||||
_outlineObject.transform.SetParent(transform, false);
|
||||
_outlineObject.transform.localScale = Vector3.one * outlineScale;
|
||||
|
||||
var outlineMF = _outlineObject.AddComponent<MeshFilter>();
|
||||
outlineMF.sharedMesh = meshFilter.sharedMesh;
|
||||
|
||||
var mat = new Material(Shader.Find("Sprites/Default"));
|
||||
mat.color = outlineColor;
|
||||
mat.renderQueue = 3000;
|
||||
|
||||
var renderer = _outlineObject.AddComponent<MeshRenderer>();
|
||||
renderer.material = mat;
|
||||
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
renderer.receiveShadows = false;
|
||||
|
||||
_outlineObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void DestroyOutlineObject()
|
||||
{
|
||||
if (_outlineObject != null)
|
||||
{
|
||||
Destroy(_outlineObject);
|
||||
_outlineObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 상세 패널 (Capping 전용) ──────────────────────
|
||||
|
||||
private void ShowDetailPanel()
|
||||
{
|
||||
if (_root == null) return;
|
||||
|
||||
_detailPanel = new EWLKCappingDetailPanel();
|
||||
_detailPanel.SetData(_cappingData);
|
||||
_detailPanel.OnDeselectClicked += Deselect;
|
||||
_root.Add(_detailPanel);
|
||||
}
|
||||
|
||||
private void HideDetailPanel()
|
||||
{
|
||||
if (_detailPanel != null)
|
||||
{
|
||||
_detailPanel.OnDeselectClicked -= Deselect;
|
||||
_root?.Remove(_detailPanel);
|
||||
_detailPanel.Dispose();
|
||||
_detailPanel = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 외부 API ───────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 팝업 데이터를 업데이트합니다.
|
||||
/// </summary>
|
||||
public void UpdatePopupData(EWLKPackEquipInfoData data)
|
||||
{
|
||||
_popupData = data;
|
||||
_popup?.SetData(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capping 상세 패널 데이터를 업데이트합니다.
|
||||
/// </summary>
|
||||
public void UpdateCappingData(EWLKCappingDetailData data)
|
||||
{
|
||||
_cappingData = data;
|
||||
_detailPanel?.SetData(data);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (_popup != null && Application.isPlaying)
|
||||
{
|
||||
InitPopupData();
|
||||
_popup.SetData(_popupData);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95cc4aa0987ca5645bae8bdaf4dd0a28
|
||||
@@ -53,10 +53,14 @@ namespace UVC.EnglewoodLAB
|
||||
Injector.RegisterInstance(new ModalUIDocument(modalUI), ServiceLifetime.Scene);
|
||||
}
|
||||
|
||||
// OverView MQTT 서비스 등록 (토픽 구독 — 브로커 연결은 EWLKSceneMain.Initialize()에서)
|
||||
// MQTT 서비스 등록 (토픽 구독 — 브로커 연결은 EWLKSceneMain.Initialize()에서)
|
||||
var overViewMqtt = new EWLKOverViewMqttService();
|
||||
overViewMqtt.Subscribe();
|
||||
Injector.RegisterInstance<EWLKOverViewMqttService>(overViewMqtt, ServiceLifetime.Scene);
|
||||
|
||||
var mfgOrderMqtt = new EWLKMfgOrderMqttService();
|
||||
mfgOrderMqtt.Subscribe();
|
||||
Injector.RegisterInstance<EWLKMfgOrderMqttService>(mfgOrderMqtt, ServiceLifetime.Scene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace UVC.EnglewoodLAB
|
||||
[Inject] private DynamicUIDocument? _dynamicUIDoc;
|
||||
[Inject] private ModalUIDocument? _modalUIDoc;
|
||||
[Inject] private EWLKNavSideBar? _navSideBar;
|
||||
[Inject] private EWLKOverViewMqttService? _overViewMqtt;
|
||||
[Inject] private EWLKOverViewMqttService? _overViewMqtt;
|
||||
[Inject] private EWLKMfgOrderMqttService? _mfgOrderMqtt;
|
||||
|
||||
// ── UIDocument 편의 접근자 ────────────────────────
|
||||
private UIDocument? StaticUI => _staticUIDoc?.Value;
|
||||
@@ -61,6 +62,11 @@ namespace UVC.EnglewoodLAB
|
||||
private async UniTask InitializeAsync()
|
||||
{
|
||||
await InjectorAppContext.Instance.WaitForInitializationAsync();
|
||||
|
||||
// PerformSceneInjection() 완료까지 대기 (로딩씬 → 메인씬 전환 시 필요)
|
||||
if (InjectorSceneContext.Instance != null)
|
||||
await InjectorSceneContext.Instance.WaitForInitializationAsync();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
@@ -110,6 +116,10 @@ namespace UVC.EnglewoodLAB
|
||||
ShowOverViewModalAsync()
|
||||
.Forget(ex => Debug.LogError(ex));
|
||||
break;
|
||||
case 2: // 제조지시현황
|
||||
ShowMfgOrderModalAsync()
|
||||
.Forget(ex => Debug.LogError(ex));
|
||||
break;
|
||||
default:
|
||||
// TODO: 인덱스별 콘텐츠 뷰(DynamicUI) 전환 구현 예정
|
||||
Debug.Log($"[EWLKSceneMain] 메뉴 {index} 선택됨");
|
||||
@@ -147,5 +157,32 @@ namespace UVC.EnglewoodLAB
|
||||
// 메뉴 선택 상태 해제 (모달이 닫히면 활성화 항목 없음)
|
||||
_navSideBar?.SetActiveItem(-1);
|
||||
}
|
||||
|
||||
// ── 제조지시현황 모달 ─────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 제조지시현황 모달을 열고 MQTT 실시간 데이터를 바인딩합니다.
|
||||
/// 모달이 닫히면 이벤트 바인딩을 해제합니다.
|
||||
/// </summary>
|
||||
private async UniTask ShowMfgOrderModalAsync()
|
||||
{
|
||||
var content = new EWLKMfgOrderModalContent();
|
||||
|
||||
if (_mfgOrderMqtt != null)
|
||||
{
|
||||
content.UpdateData(_mfgOrderMqtt.CurrentData);
|
||||
_mfgOrderMqtt.OnDataUpdated += content.UpdateData;
|
||||
}
|
||||
|
||||
var modal = UTKModal.Create("제조지시현황", UTKModal.ModalSize.Large);
|
||||
modal.Add(content);
|
||||
await modal.ShowAsync();
|
||||
|
||||
if (_mfgOrderMqtt != null)
|
||||
_mfgOrderMqtt.OnDataUpdated -= content.UpdateData;
|
||||
|
||||
content.Dispose();
|
||||
_navSideBar?.SetActiveItem(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
197
Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKCappingDetailPanel.cs
Normal file
197
Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKCappingDetailPanel.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
|
||||
namespace UVC.EnglewoodLAB.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// Capping 상세 패널.
|
||||
/// 설비 정보 + 가동 토글(읽기전용) + 구분/측정/설정 테이블 + 선택해제 버튼.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class EWLKCappingDetailPanel : VisualElement, IDisposable
|
||||
{
|
||||
private const string UssPath = "EWLK/UIToolkit/Main/EWLKCappingDetailPanelUss";
|
||||
|
||||
/// <summary>선택 해제 버튼 클릭 시 호출</summary>
|
||||
public event Action? OnDeselectClicked;
|
||||
|
||||
// ── 캐시 ──────────────────────────────────────────
|
||||
private readonly Label _equipNameVal;
|
||||
private readonly Label _runningLabel;
|
||||
private readonly VisualElement _toggleTrack;
|
||||
private readonly VisualElement _toggleThumb;
|
||||
|
||||
// 구분/측정/설정 테이블 (5행)
|
||||
private readonly List<(Label category, Label measured, Label setting)> _msRows = new();
|
||||
|
||||
// ────────────────────────────────────────────────
|
||||
|
||||
public EWLKCappingDetailPanel()
|
||||
{
|
||||
var uss = Resources.Load<StyleSheet>(UssPath);
|
||||
if (uss != null) styleSheets.Add(uss);
|
||||
|
||||
AddToClassList("ewlk-capping");
|
||||
style.position = Position.Absolute;
|
||||
style.left = 0;
|
||||
style.top = 0;
|
||||
style.right = 0;
|
||||
style.bottom = 0;
|
||||
|
||||
// ── 메인 컨테이너 (우측 정렬) ─────────────────
|
||||
var container = new VisualElement();
|
||||
container.AddToClassList("ewlk-capping__container");
|
||||
|
||||
// 1) 설비 정보 헤더 + 가동 토글
|
||||
var infoHeader = new VisualElement();
|
||||
infoHeader.AddToClassList("ewlk-capping__info-header");
|
||||
|
||||
var headerLeft = new VisualElement();
|
||||
headerLeft.AddToClassList("ewlk-capping__info-header-left");
|
||||
|
||||
var headerTitle = new Label("설비 정보");
|
||||
headerTitle.AddToClassList("ewlk-capping__info-title");
|
||||
headerLeft.Add(headerTitle);
|
||||
|
||||
_equipNameVal = new Label("-");
|
||||
_equipNameVal.AddToClassList("ewlk-capping__equip-name");
|
||||
headerLeft.Add(_equipNameVal);
|
||||
|
||||
infoHeader.Add(headerLeft);
|
||||
|
||||
// 가동/비가동 토글 (읽기 전용)
|
||||
var toggleWrap = new VisualElement();
|
||||
toggleWrap.AddToClassList("ewlk-capping__toggle-wrap");
|
||||
|
||||
_runningLabel = new Label("비가동");
|
||||
_runningLabel.AddToClassList("ewlk-capping__toggle-label");
|
||||
toggleWrap.Add(_runningLabel);
|
||||
|
||||
var toggleContainer = new VisualElement();
|
||||
toggleContainer.AddToClassList("ewlk-capping__toggle");
|
||||
|
||||
_toggleTrack = new VisualElement();
|
||||
_toggleTrack.AddToClassList("ewlk-capping__toggle-track");
|
||||
_toggleTrack.AddToClassList("ewlk-capping__toggle-track--off");
|
||||
|
||||
_toggleThumb = new VisualElement();
|
||||
_toggleThumb.AddToClassList("ewlk-capping__toggle-thumb");
|
||||
_toggleTrack.Add(_toggleThumb);
|
||||
|
||||
toggleContainer.Add(_toggleTrack);
|
||||
toggleWrap.Add(toggleContainer);
|
||||
|
||||
infoHeader.Add(toggleWrap);
|
||||
container.Add(infoHeader);
|
||||
|
||||
// 2) 구분/측정/설정 테이블
|
||||
var table = new VisualElement();
|
||||
table.AddToClassList("ewlk-capping__table");
|
||||
|
||||
// 테이블 헤더
|
||||
table.Add(MakeTableHeader("구분", "측정", "설정"));
|
||||
|
||||
// 5행
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var row = MakeMeasureSettingRow(out var cat, out var meas, out var set);
|
||||
table.Add(row);
|
||||
_msRows.Add((cat, meas, set));
|
||||
}
|
||||
container.Add(table);
|
||||
|
||||
Add(container);
|
||||
|
||||
// 3) 선택 해제 버튼
|
||||
var deselectBtn = new Button(() => OnDeselectClicked?.Invoke()) { text = "선택 해제" };
|
||||
deselectBtn.AddToClassList("ewlk-capping__deselect-btn");
|
||||
Add(deselectBtn);
|
||||
|
||||
// 기본 데이터 초기화
|
||||
SetData(new EWLKCappingDetailData());
|
||||
}
|
||||
|
||||
// ── 공개 API ────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 데이터를 바인딩합니다.
|
||||
/// </summary>
|
||||
public void SetData(EWLKCappingDetailData data)
|
||||
{
|
||||
// 설비명
|
||||
_equipNameVal.text = data.EquipmentName;
|
||||
|
||||
// 가동 토글
|
||||
UpdateToggle(data.IsRunning);
|
||||
|
||||
// 테이블
|
||||
for (int i = 0; i < _msRows.Count && i < data.MeasureSettingRows.Count; i++)
|
||||
{
|
||||
_msRows[i].category.text = data.MeasureSettingRows[i].Category;
|
||||
_msRows[i].measured.text = data.MeasureSettingRows[i].Measured;
|
||||
_msRows[i].setting.text = data.MeasureSettingRows[i].Setting;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
// ── 토글 ──────────────────────────────────────────
|
||||
|
||||
private void UpdateToggle(bool isRunning)
|
||||
{
|
||||
_runningLabel.text = isRunning ? "가동중" : "비가동";
|
||||
|
||||
_toggleTrack.EnableInClassList("ewlk-capping__toggle-track--on", isRunning);
|
||||
_toggleTrack.EnableInClassList("ewlk-capping__toggle-track--off", !isRunning);
|
||||
_toggleThumb.EnableInClassList("ewlk-capping__toggle-thumb--on", isRunning);
|
||||
_toggleThumb.EnableInClassList("ewlk-capping__toggle-thumb--off", !isRunning);
|
||||
}
|
||||
|
||||
// ── 빌더 헬퍼 ──────────────────────────────────────
|
||||
|
||||
/// <summary>테이블 헤더 생성</summary>
|
||||
private static VisualElement MakeTableHeader(params string[] columns)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-capping__table-header");
|
||||
|
||||
foreach (var col in columns)
|
||||
{
|
||||
var lbl = new Label(col);
|
||||
lbl.AddToClassList("ewlk-capping__table-header-cell");
|
||||
row.Add(lbl);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>구분/측정/설정 행 생성</summary>
|
||||
private static VisualElement MakeMeasureSettingRow(
|
||||
out Label category, out Label measured, out Label setting)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-capping__table-row");
|
||||
|
||||
category = new Label("-");
|
||||
category.AddToClassList("ewlk-capping__table-cell");
|
||||
category.AddToClassList("ewlk-capping__table-cell--category");
|
||||
row.Add(category);
|
||||
|
||||
measured = new Label("-");
|
||||
measured.AddToClassList("ewlk-capping__table-cell");
|
||||
measured.AddToClassList("ewlk-capping__table-cell--measured");
|
||||
row.Add(measured);
|
||||
|
||||
setting = new Label("-");
|
||||
setting.AddToClassList("ewlk-capping__table-cell");
|
||||
setting.AddToClassList("ewlk-capping__table-cell--setting");
|
||||
row.Add(setting);
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 872841a7d299e2741886b1578ad6dcaf
|
||||
@@ -0,0 +1,453 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
|
||||
namespace UVC.EnglewoodLAB.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 설비 상세 패널. 좌측(기본정보·품질·운영) + 우측(메인 믹서) + 선택해제 버튼.
|
||||
/// DynamicUI rootVisualElement에 추가하여 사용합니다.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class EWLKManuEquipDetailPanel : VisualElement, IDisposable
|
||||
{
|
||||
private const string UssPath = "EWLK/UIToolkit/Main/EWLKManuEquipDetailPanelUss";
|
||||
|
||||
/// <summary>선택 해제 버튼 클릭 시 호출</summary>
|
||||
public event Action? OnDeselectClicked;
|
||||
|
||||
// ── 값 라벨 캐시 (데이터 바인딩용) ──────────────
|
||||
// 설비 기본 정보
|
||||
private readonly Label _equipNameVal;
|
||||
private readonly Label _capacityVal;
|
||||
private readonly Label _locationVal;
|
||||
|
||||
// 품질 정보
|
||||
private readonly List<Label> _qualityVals = new();
|
||||
|
||||
// 설비 운영 정보 — 공정
|
||||
private readonly Label _processProductNoVal;
|
||||
private readonly Label _processProductNameVal;
|
||||
private readonly Label _processLotVal;
|
||||
private readonly Label _processQtyVal;
|
||||
private readonly Label _processWorkerVal;
|
||||
|
||||
// 설비 운영 정보 — 상별 용해
|
||||
private readonly Label _meltProductNoVal;
|
||||
private readonly Label _meltProductNameVal;
|
||||
private readonly Label _meltLotVal;
|
||||
private readonly Label _meltQtyVal;
|
||||
private readonly Label _meltWorkerVal;
|
||||
|
||||
// 메인 믹서 — 측정값 라벨 (검정)
|
||||
private readonly Label _jacketTempVal;
|
||||
private readonly Label _upperTempVal;
|
||||
private readonly Label _tempMeasuredVal;
|
||||
private readonly Label _homoMeasuredVal;
|
||||
private readonly Label _padMeasuredVal;
|
||||
private readonly Label _scrMeasuredVal;
|
||||
private readonly Label _pressureMeasuredVal;
|
||||
|
||||
// 메인 믹서 — 세팅값 라벨 (파랑)
|
||||
private readonly Label _tempSettingVal;
|
||||
private readonly Label _heatingVal;
|
||||
private readonly Label _coolingVal;
|
||||
private readonly Label _homoSettingVal;
|
||||
private readonly Label _padSettingVal;
|
||||
private readonly Label _scrSettingVal;
|
||||
private readonly Label _pressureSettingVal;
|
||||
|
||||
private readonly List<Label> _runStatusLabels = new();
|
||||
|
||||
// ────────────────────────────────────────────────
|
||||
|
||||
public EWLKManuEquipDetailPanel()
|
||||
{
|
||||
var uss = Resources.Load<StyleSheet>(UssPath);
|
||||
if (uss != null) styleSheets.Add(uss);
|
||||
|
||||
AddToClassList("ewlk-detail");
|
||||
style.position = Position.Absolute;
|
||||
style.left = 0;
|
||||
style.top = 0;
|
||||
style.right = 0;
|
||||
style.bottom = 0;
|
||||
|
||||
// ── 좌측 컬럼 ──────────────────────────────────
|
||||
var leftCol = new VisualElement();
|
||||
leftCol.AddToClassList("ewlk-detail__left");
|
||||
|
||||
// 1) 설비 기본 정보
|
||||
var basicPanel = MakeSection("설비 기본 정보");
|
||||
basicPanel.Add(MakeRow("설비명", out _equipNameVal));
|
||||
basicPanel.Add(MakeRow("설비 CAPA", out _capacityVal));
|
||||
basicPanel.Add(MakeRow("설비 위치", out _locationVal));
|
||||
basicPanel.Add(MakeDataTypeRow());
|
||||
leftCol.Add(basicPanel);
|
||||
|
||||
// 2) 품질 정보
|
||||
var qualityPanel = MakeSection("품질 정보");
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Label val;
|
||||
qualityPanel.Add(MakeRow("목표 정보", out val));
|
||||
_qualityVals.Add(val);
|
||||
}
|
||||
leftCol.Add(qualityPanel);
|
||||
|
||||
// 3) 설비 운영 정보 (ScrollView)
|
||||
var operationPanel = MakeSection("설비 운영 정보");
|
||||
operationPanel.AddToClassList("ewlk-detail__section--stretch");
|
||||
|
||||
var operationScroll = new ScrollView(ScrollViewMode.Vertical);
|
||||
operationScroll.AddToClassList("ewlk-detail__scroll");
|
||||
|
||||
// 공정 정보
|
||||
operationScroll.Add(MakeSubHeader("공정 정보"));
|
||||
operationScroll.Add(MakeRow("품번", out _processProductNoVal));
|
||||
operationScroll.Add(MakeRow("품명", out _processProductNameVal));
|
||||
operationScroll.Add(MakeRow("로트(LOT)", out _processLotVal));
|
||||
operationScroll.Add(MakeRow("제조량", out _processQtyVal));
|
||||
operationScroll.Add(MakeRow("작업자", out _processWorkerVal));
|
||||
|
||||
// 상별 용해
|
||||
operationScroll.Add(MakeSubHeader("상별 용해"));
|
||||
operationScroll.Add(MakeRow("품번", out _meltProductNoVal));
|
||||
operationScroll.Add(MakeRow("품명", out _meltProductNameVal));
|
||||
operationScroll.Add(MakeRow("로트(LOT)", out _meltLotVal));
|
||||
operationScroll.Add(MakeRow("제조량", out _meltQtyVal));
|
||||
operationScroll.Add(MakeRow("작업자", out _meltWorkerVal));
|
||||
|
||||
operationPanel.Add(operationScroll);
|
||||
leftCol.Add(operationPanel);
|
||||
|
||||
Add(leftCol);
|
||||
|
||||
// ── 우측 컬럼 ──────────────────────────────────
|
||||
var rightCol = new VisualElement();
|
||||
rightCol.AddToClassList("ewlk-detail__right");
|
||||
|
||||
var mixerPanel = MakeSection("메인 믹서");
|
||||
var mixerScroll = new ScrollView(ScrollViewMode.Vertical);
|
||||
mixerScroll.AddToClassList("ewlk-detail__scroll");
|
||||
|
||||
mixerScroll.Add(MakeMeasuredRow("자켓 온도", "°C", out _jacketTempVal));
|
||||
mixerScroll.Add(MakeMeasuredRow("상부 온도", "°C", out _upperTempVal));
|
||||
mixerScroll.Add(MakeDualRow("온도", "°C", out _tempMeasuredVal, out _tempSettingVal));
|
||||
mixerScroll.Add(MakeSettingRow("가열온도 설정", "°C", out _heatingVal, starred: true));
|
||||
mixerScroll.Add(MakeSettingRow("냉각온도 설정", "°C", out _coolingVal, starred: true));
|
||||
mixerScroll.Add(MakeDualRow("HOMO", "RPM", out _homoMeasuredVal, out _homoSettingVal, starred: true));
|
||||
mixerScroll.Add(MakeDualRow("PAD", "RPM", out _padMeasuredVal, out _padSettingVal, starred: true));
|
||||
mixerScroll.Add(MakeDualRow("SCR", "RPM", out _scrMeasuredVal, out _scrSettingVal, starred: true));
|
||||
mixerScroll.Add(MakeDualRow("설비내 압력", "mmHg", out _pressureMeasuredVal, out _pressureSettingVal));
|
||||
|
||||
// 가동 상태 쌍 행
|
||||
var statusNames = new[]
|
||||
{
|
||||
("진공시작", "진공파기"),
|
||||
("배출시작", "탱크벨트"),
|
||||
};
|
||||
foreach (var (left, right) in statusNames)
|
||||
{
|
||||
Label lVal, rVal;
|
||||
mixerScroll.Add(MakeStatusPairRow(left, out lVal, right, out rVal));
|
||||
_runStatusLabels.Add(lVal);
|
||||
_runStatusLabels.Add(rVal);
|
||||
}
|
||||
|
||||
mixerPanel.Add(mixerScroll);
|
||||
rightCol.Add(mixerPanel);
|
||||
Add(rightCol);
|
||||
|
||||
// ── 선택 해제 버튼 (중앙 하단) ──────────────────
|
||||
var deselectBtn = new Button(() => OnDeselectClicked?.Invoke()) { text = "선택 해제" };
|
||||
deselectBtn.AddToClassList("ewlk-detail__deselect-btn");
|
||||
Add(deselectBtn);
|
||||
|
||||
// 기본 데이터로 초기화
|
||||
SetData(new EWLKManuEquipDetailData());
|
||||
}
|
||||
|
||||
// ── 공개 API ────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 데이터를 바인딩합니다.
|
||||
/// </summary>
|
||||
public void SetData(EWLKManuEquipDetailData data)
|
||||
{
|
||||
// 설비 기본 정보
|
||||
_equipNameVal.text = data.BasicInfo.EquipmentName;
|
||||
_capacityVal.text = data.BasicInfo.Capacity;
|
||||
_locationVal.text = data.BasicInfo.Location;
|
||||
|
||||
// 품질 정보
|
||||
for (int i = 0; i < _qualityVals.Count && i < data.QualityInfo.Targets.Count; i++)
|
||||
_qualityVals[i].text = data.QualityInfo.Targets[i].Range;
|
||||
|
||||
// 설비 운영 정보 — 공정
|
||||
var p = data.OperationInfo.ProcessInfo;
|
||||
_processProductNoVal.text = p.ProductNo;
|
||||
_processProductNameVal.text = p.ProductName;
|
||||
_processLotVal.text = p.LotNo;
|
||||
_processQtyVal.text = p.ProductionQty;
|
||||
_processWorkerVal.text = p.Worker;
|
||||
|
||||
// 설비 운영 정보 — 상별 용해
|
||||
var m = data.OperationInfo.MeltInfo;
|
||||
_meltProductNoVal.text = m.ProductNo;
|
||||
_meltProductNameVal.text = m.ProductName;
|
||||
_meltLotVal.text = m.LotNo;
|
||||
_meltQtyVal.text = m.ProductionQty;
|
||||
_meltWorkerVal.text = m.Worker;
|
||||
|
||||
// 메인 믹서
|
||||
var mx = data.MainMixer;
|
||||
_jacketTempVal.text = mx.JacketTemp;
|
||||
_upperTempVal.text = mx.UpperTemp;
|
||||
_tempMeasuredVal.text = mx.TempMeasured;
|
||||
_tempSettingVal.text = mx.TempSetting;
|
||||
_heatingVal.text = mx.HeatingTempSetting;
|
||||
_coolingVal.text = mx.CoolingTempSetting;
|
||||
_homoMeasuredVal.text = mx.HomoMeasured;
|
||||
_homoSettingVal.text = mx.HomoSetting;
|
||||
_padMeasuredVal.text = mx.PadMeasured;
|
||||
_padSettingVal.text = mx.PadSetting;
|
||||
_scrMeasuredVal.text = mx.ScrMeasured;
|
||||
_scrSettingVal.text = mx.ScrSetting;
|
||||
_pressureMeasuredVal.text = mx.PressureMeasured;
|
||||
_pressureSettingVal.text = mx.PressureSetting;
|
||||
|
||||
// 가동 상태
|
||||
for (int i = 0; i < _runStatusLabels.Count && i < mx.RunStatuses.Count; i++)
|
||||
{
|
||||
var status = mx.RunStatuses[i];
|
||||
_runStatusLabels[i].text = status.IsActive ? "가동" : "비가동";
|
||||
_runStatusLabels[i].EnableInClassList("ewlk-detail__status--active", status.IsActive);
|
||||
_runStatusLabels[i].EnableInClassList("ewlk-detail__status--inactive", !status.IsActive);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
// ── 빌더 헬퍼 ──────────────────────────────────────
|
||||
|
||||
/// <summary>섹션 패널 생성 (헤더 + 바디 컨테이너)</summary>
|
||||
private static VisualElement MakeSection(string title)
|
||||
{
|
||||
var section = new VisualElement();
|
||||
section.AddToClassList("ewlk-detail__section");
|
||||
|
||||
var header = new Label(title);
|
||||
header.AddToClassList("ewlk-detail__section-header");
|
||||
section.Add(header);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
/// <summary>서브 헤더 (공정 정보, 상별 용해 등)</summary>
|
||||
private static VisualElement MakeSubHeader(string title)
|
||||
{
|
||||
var header = new Label(title);
|
||||
header.AddToClassList("ewlk-detail__sub-header");
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>키-값 행 생성</summary>
|
||||
private static VisualElement MakeRow(string key, out Label valueLabel)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__row");
|
||||
|
||||
var keyLbl = new Label(key);
|
||||
keyLbl.AddToClassList("ewlk-detail__row-key");
|
||||
row.Add(keyLbl);
|
||||
|
||||
valueLabel = new Label("-");
|
||||
valueLabel.AddToClassList("ewlk-detail__row-value");
|
||||
row.Add(valueLabel);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>키 영역 생성. starred=true이면 빨간 * 를 별도 Label로 추가</summary>
|
||||
private static VisualElement MakeKeyElement(string key, bool starred = false)
|
||||
{
|
||||
var wrap = new VisualElement();
|
||||
wrap.AddToClassList("ewlk-detail__row-key");
|
||||
wrap.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var keyLbl = new Label(key);
|
||||
wrap.Add(keyLbl);
|
||||
|
||||
if (starred)
|
||||
{
|
||||
var star = new Label("*");
|
||||
star.AddToClassList("ewlk-detail__key-star");
|
||||
wrap.Add(star);
|
||||
}
|
||||
|
||||
return wrap;
|
||||
}
|
||||
|
||||
/// <summary>측정값 행 — 값(검정) + 단위(검정)</summary>
|
||||
private static VisualElement MakeMeasuredRow(string key, string unit, out Label valueLabel)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__row");
|
||||
|
||||
row.Add(MakeKeyElement(key));
|
||||
|
||||
var valueWrap = new VisualElement();
|
||||
valueWrap.AddToClassList("ewlk-detail__row-value-wrap");
|
||||
|
||||
valueLabel = new Label("-");
|
||||
valueLabel.AddToClassList("ewlk-detail__val--measured");
|
||||
valueWrap.Add(valueLabel);
|
||||
|
||||
var unitLbl = new Label($" ({unit})");
|
||||
unitLbl.AddToClassList("ewlk-detail__val--unit");
|
||||
valueWrap.Add(unitLbl);
|
||||
|
||||
row.Add(valueWrap);
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>듀얼 행 — 측정값(검정) / 세팅값(파랑) + 단위(검정)</summary>
|
||||
private static VisualElement MakeDualRow(
|
||||
string key, string unit,
|
||||
out Label measuredLabel, out Label settingLabel,
|
||||
bool starred = false)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__row");
|
||||
|
||||
row.Add(MakeKeyElement(key, starred));
|
||||
|
||||
var valueWrap = new VisualElement();
|
||||
valueWrap.AddToClassList("ewlk-detail__row-value-wrap");
|
||||
|
||||
measuredLabel = new Label("-");
|
||||
measuredLabel.AddToClassList("ewlk-detail__val--measured");
|
||||
valueWrap.Add(measuredLabel);
|
||||
|
||||
var slash = new Label("/");
|
||||
slash.AddToClassList("ewlk-detail__val--separator");
|
||||
valueWrap.Add(slash);
|
||||
|
||||
settingLabel = new Label("-");
|
||||
settingLabel.AddToClassList("ewlk-detail__val--setting");
|
||||
valueWrap.Add(settingLabel);
|
||||
|
||||
var unitLbl = new Label($" ({unit})");
|
||||
unitLbl.AddToClassList("ewlk-detail__val--unit");
|
||||
valueWrap.Add(unitLbl);
|
||||
|
||||
row.Add(valueWrap);
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>세팅 전용 행 — 값(파랑) + 단위(검정)</summary>
|
||||
private static VisualElement MakeSettingRow(string key, string unit, out Label valueLabel, bool starred = false)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__row");
|
||||
|
||||
row.Add(MakeKeyElement(key, starred));
|
||||
|
||||
var valueWrap = new VisualElement();
|
||||
valueWrap.AddToClassList("ewlk-detail__row-value-wrap");
|
||||
|
||||
valueLabel = new Label("-");
|
||||
valueLabel.AddToClassList("ewlk-detail__val--setting");
|
||||
valueWrap.Add(valueLabel);
|
||||
|
||||
var unitLbl = new Label($" ({unit})");
|
||||
unitLbl.AddToClassList("ewlk-detail__val--unit");
|
||||
valueWrap.Add(unitLbl);
|
||||
|
||||
row.Add(valueWrap);
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>데이터 유형 행 (●측정값 ●세팅값 ●AI예측값)</summary>
|
||||
private static VisualElement MakeDataTypeRow()
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__row");
|
||||
|
||||
var keyLbl = new Label("데이터 유형");
|
||||
keyLbl.AddToClassList("ewlk-detail__row-key");
|
||||
row.Add(keyLbl);
|
||||
|
||||
var valueWrap = new VisualElement();
|
||||
valueWrap.AddToClassList("ewlk-detail__data-type-wrap");
|
||||
|
||||
valueWrap.Add(MakeDataTypeDot("측정 값", "ewlk-detail__dot--measured"));
|
||||
valueWrap.Add(MakeDataTypeDot("세팅 값", "ewlk-detail__dot--setting"));
|
||||
valueWrap.Add(MakeDataTypeDot("AI 예측 값", "ewlk-detail__dot--predicted"));
|
||||
row.Add(valueWrap);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
private static VisualElement MakeDataTypeDot(string text, string dotClass)
|
||||
{
|
||||
var item = new VisualElement();
|
||||
item.AddToClassList("ewlk-detail__data-type-item");
|
||||
|
||||
var dot = new VisualElement();
|
||||
dot.AddToClassList("ewlk-detail__dot");
|
||||
dot.AddToClassList(dotClass);
|
||||
item.Add(dot);
|
||||
|
||||
var lbl = new Label(text);
|
||||
lbl.AddToClassList("ewlk-detail__data-type-label");
|
||||
item.Add(lbl);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>가동 상태 쌍 행 (예: 진공시작 | 진공파기)</summary>
|
||||
private static VisualElement MakeStatusPairRow(
|
||||
string leftKey, out Label leftVal,
|
||||
string rightKey, out Label rightVal)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__status-pair");
|
||||
|
||||
row.Add(MakeStatusCell(leftKey, out leftVal));
|
||||
row.Add(MakeStatusCell(rightKey, out rightVal));
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>가동 상태 단독 행</summary>
|
||||
private static VisualElement MakeStatusRow(string key, out Label valueLabel)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-detail__status-pair");
|
||||
row.Add(MakeStatusCell(key, out valueLabel));
|
||||
return row;
|
||||
}
|
||||
|
||||
private static VisualElement MakeStatusCell(string key, out Label valueLabel)
|
||||
{
|
||||
var cell = new VisualElement();
|
||||
cell.AddToClassList("ewlk-detail__status-cell");
|
||||
|
||||
var keyLbl = new Label(key);
|
||||
keyLbl.AddToClassList("ewlk-detail__status-key");
|
||||
cell.Add(keyLbl);
|
||||
|
||||
valueLabel = new Label("비가동");
|
||||
valueLabel.AddToClassList("ewlk-detail__status-value");
|
||||
valueLabel.AddToClassList("ewlk-detail__status--inactive");
|
||||
cell.Add(valueLabel);
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48fd515df0ec4be4ea516e678a7e075e
|
||||
@@ -0,0 +1,61 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
|
||||
namespace UVC.EnglewoodLAB.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 설비 정보 팝업 UI 요소.
|
||||
/// 상태 원(초록/노랑/회색) + 설비명으로 구성됩니다.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class EWLKManuEquipInfoPopup : VisualElement
|
||||
{
|
||||
private const string UssPath = "EWLK/UIToolkit/Main/EWLKManuEquipInfoPopupUss";
|
||||
|
||||
private static readonly string ClassRoot = "ewlk-equip-popup";
|
||||
private static readonly string ClassIndicator = "ewlk-equip-popup__indicator";
|
||||
private static readonly string ClassLabel = "ewlk-equip-popup__label";
|
||||
private static readonly string ClassRunning = "ewlk-equip-popup--running";
|
||||
private static readonly string ClassIdle = "ewlk-equip-popup--idle";
|
||||
private static readonly string ClassPlannedStop = "ewlk-equip-popup--planned-stop";
|
||||
|
||||
private readonly VisualElement _indicator;
|
||||
private readonly Label _nameLabel;
|
||||
|
||||
public EWLKManuEquipInfoPopup()
|
||||
{
|
||||
var uss = Resources.Load<StyleSheet>(UssPath);
|
||||
if (uss != null) styleSheets.Add(uss);
|
||||
|
||||
AddToClassList(ClassRoot);
|
||||
|
||||
// 상태 표시 원
|
||||
_indicator = new VisualElement();
|
||||
_indicator.AddToClassList(ClassIndicator);
|
||||
Add(_indicator);
|
||||
|
||||
// 설비명 라벨
|
||||
_nameLabel = new Label();
|
||||
_nameLabel.AddToClassList(ClassLabel);
|
||||
Add(_nameLabel);
|
||||
|
||||
// 절대 위치 (Tracker가 좌표를 설정)
|
||||
style.position = Position.Absolute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 데이터를 바인딩합니다.
|
||||
/// </summary>
|
||||
public void SetData(EWLKManuEquipInfoData data)
|
||||
{
|
||||
_nameLabel.text = data.EquipmentName;
|
||||
|
||||
// 상태 클래스 토글
|
||||
_indicator.EnableInClassList(ClassRunning, data.Status == EquipmentStatus.Running);
|
||||
_indicator.EnableInClassList(ClassIdle, data.Status == EquipmentStatus.Idle);
|
||||
_indicator.EnableInClassList(ClassPlannedStop, data.Status == EquipmentStatus.PlannedStop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70bf7a477fbca2b40a4fd39ec76d3eaa
|
||||
@@ -0,0 +1,386 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
|
||||
namespace UVC.EnglewoodLAB.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 제조지시현황 모달 콘텐츠.
|
||||
/// 2개 라인 탭, 요약 바, 설비 행 ListView로 구성됩니다.
|
||||
/// MQTT 데이터가 없는 경우 플레이스홀더 행을 표시합니다.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class EWLKMfgOrderModalContent : VisualElement, IDisposable
|
||||
{
|
||||
private const string UssPath = "EWLK/UIToolkit/Main/EWLKMfgOrderModalContentUss";
|
||||
|
||||
// ── 탭 ──────────────────────────────────────────────
|
||||
private readonly Button _tab1Btn;
|
||||
private readonly Button _tab2Btn;
|
||||
|
||||
// ── 요약 바 ─────────────────────────────────────────
|
||||
private readonly Label _totalTargetLabel;
|
||||
private readonly Label _totalActualLabel;
|
||||
private readonly Label _totalProgressLabel;
|
||||
private readonly VisualElement _totalProgressFill;
|
||||
private readonly Label _equipRateLabel;
|
||||
private readonly VisualElement _equipRateFill;
|
||||
private readonly Label _countLabel;
|
||||
|
||||
// ── 테이블 ───────────────────────────────────────────
|
||||
private readonly ListView _listView;
|
||||
|
||||
// ── 상태 ─────────────────────────────────────────────
|
||||
private EWLKMfgOrderData? _data;
|
||||
private EWLKMfgOrderLineData? _currentLine;
|
||||
private int _activeTabIndex;
|
||||
private List<EWLKMfgOrderRowData> _displayRows = new();
|
||||
|
||||
// ── 플레이스홀더 (MQTT 연결 전 기본 표시용) ──────────
|
||||
private static readonly List<EWLKMfgOrderRowData> s_PlaceholderRows = CreatePlaceholderRows();
|
||||
|
||||
// ────────────────────────────────────────────────────
|
||||
|
||||
public EWLKMfgOrderModalContent()
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>(UssPath));
|
||||
AddToClassList("ewlk-mfgorder");
|
||||
|
||||
// ── 탭 바 ────────────────────────────────────────
|
||||
var tabBar = new VisualElement();
|
||||
tabBar.AddToClassList("ewlk-mfgorder__tab-bar");
|
||||
|
||||
_tab1Btn = new Button(() => SelectTab(0)) { text = "라인 1" };
|
||||
_tab1Btn.AddToClassList("ewlk-mfgorder__tab");
|
||||
_tab2Btn = new Button(() => SelectTab(1)) { text = "라인 2" };
|
||||
_tab2Btn.AddToClassList("ewlk-mfgorder__tab");
|
||||
tabBar.Add(_tab1Btn);
|
||||
tabBar.Add(_tab2Btn);
|
||||
Add(tabBar);
|
||||
|
||||
// ── 요약 바 ──────────────────────────────────────
|
||||
var summaryBar = new VisualElement();
|
||||
summaryBar.AddToClassList("ewlk-mfgorder__summary-bar");
|
||||
summaryBar.Add(MakeStaticItem("전체 제조 목표량", out _totalTargetLabel));
|
||||
summaryBar.Add(MakeStaticItem("실제 실적량", out _totalActualLabel));
|
||||
summaryBar.Add(MakeProgressItem("전체 제조 진척률",
|
||||
out _totalProgressFill, out _totalProgressLabel));
|
||||
Add(summaryBar);
|
||||
|
||||
// ── 설비가동률 행 ─────────────────────────────────
|
||||
var equipRow = new VisualElement();
|
||||
equipRow.AddToClassList("ewlk-mfgorder__equip-row");
|
||||
|
||||
var equipTitle = new Label("일 설비가동률(가중대수%)");
|
||||
equipTitle.AddToClassList("ewlk-mfgorder__equip-title");
|
||||
equipRow.Add(equipTitle);
|
||||
|
||||
var equipBarWrap = new VisualElement();
|
||||
equipBarWrap.AddToClassList("ewlk-mfgorder__progress-wrap");
|
||||
_equipRateFill = new VisualElement();
|
||||
_equipRateFill.AddToClassList("ewlk-mfgorder__progress-fill");
|
||||
equipBarWrap.Add(_equipRateFill);
|
||||
equipRow.Add(equipBarWrap);
|
||||
|
||||
_equipRateLabel = new Label("-");
|
||||
_equipRateLabel.AddToClassList("ewlk-mfgorder__equip-rate");
|
||||
equipRow.Add(_equipRateLabel);
|
||||
|
||||
_countLabel = new Label("- / -");
|
||||
_countLabel.AddToClassList("ewlk-mfgorder__count");
|
||||
equipRow.Add(_countLabel);
|
||||
Add(equipRow);
|
||||
|
||||
// ── 테이블 헤더 ───────────────────────────────────
|
||||
Add(BuildTableHeader());
|
||||
|
||||
// ── ListView ─────────────────────────────────────
|
||||
_listView = new ListView
|
||||
{
|
||||
makeItem = MakeRowElement,
|
||||
bindItem = BindRowElement,
|
||||
fixedItemHeight = 28,
|
||||
selectionType = SelectionType.None,
|
||||
};
|
||||
_listView.AddToClassList("ewlk-mfgorder__list");
|
||||
Add(_listView);
|
||||
|
||||
SetTabActive(0);
|
||||
|
||||
// 데이터가 없어도 플레이스홀더로 레이아웃 표시
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
// ── 공개 API ──────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 데이터를 갱신합니다. MQTT 수신 시 호출됩니다 (메인 스레드 보장).
|
||||
/// </summary>
|
||||
public void UpdateData(EWLKMfgOrderData data)
|
||||
{
|
||||
_data = data;
|
||||
|
||||
// 공장구분이 수신되면 탭 레이블 갱신
|
||||
if (!string.IsNullOrEmpty(data.Line1.Summary.FactoryName))
|
||||
_tab1Btn.text = data.Line1.Summary.FactoryName;
|
||||
if (!string.IsNullOrEmpty(data.Line2.Summary.FactoryName))
|
||||
_tab2Btn.text = data.Line2.Summary.FactoryName;
|
||||
|
||||
_currentLine = _activeTabIndex == 0 ? data.Line1 : data.Line2;
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
// ── 탭 선택 ───────────────────────────────────────────
|
||||
|
||||
private void SelectTab(int index)
|
||||
{
|
||||
_activeTabIndex = index;
|
||||
SetTabActive(index);
|
||||
if (_data != null)
|
||||
_currentLine = index == 0 ? _data.Line1 : _data.Line2;
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
private void SetTabActive(int index)
|
||||
{
|
||||
_tab1Btn.EnableInClassList("ewlk-mfgorder__tab--active", index == 0);
|
||||
_tab2Btn.EnableInClassList("ewlk-mfgorder__tab--active", index == 1);
|
||||
}
|
||||
|
||||
// ── 뷰 갱신 ───────────────────────────────────────────
|
||||
|
||||
private void RefreshView()
|
||||
{
|
||||
if (_currentLine == null)
|
||||
{
|
||||
// 데이터 없음 — 플레이스홀더 표시
|
||||
_totalTargetLabel.text = "-";
|
||||
_totalActualLabel.text = "-";
|
||||
_totalProgressLabel.text = "-";
|
||||
_totalProgressFill.style.width = Length.Percent(0f);
|
||||
_equipRateLabel.text = "-";
|
||||
_equipRateFill.style.width = Length.Percent(0f);
|
||||
_countLabel.text = "- / -";
|
||||
|
||||
_displayRows = s_PlaceholderRows;
|
||||
_listView.itemsSource = _displayRows;
|
||||
_listView.Rebuild();
|
||||
return;
|
||||
}
|
||||
|
||||
var s = _currentLine.Summary;
|
||||
|
||||
_totalTargetLabel.text = Or(s.TotalTarget, "-");
|
||||
_totalActualLabel.text = Or(s.TotalActual, "-");
|
||||
_totalProgressLabel.text = $"{s.TotalProgress:F1}%";
|
||||
_totalProgressFill.style.width =
|
||||
Length.Percent(Mathf.Clamp(s.TotalProgress, 0f, 100f));
|
||||
|
||||
_equipRateLabel.text = $"{s.EquipRate:F1}%";
|
||||
_equipRateFill.style.width =
|
||||
Length.Percent(Mathf.Clamp(s.EquipRate, 0f, 100f));
|
||||
_countLabel.text = $"{s.ActiveCount} / {s.TotalCount}";
|
||||
|
||||
_displayRows = _currentLine.Rows;
|
||||
_listView.itemsSource = _displayRows;
|
||||
_listView.Rebuild();
|
||||
}
|
||||
|
||||
// ── ListView 바인딩 ───────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 행 요소를 생성합니다. 모든 셀과 서브 요소를 미리 생성합니다.
|
||||
/// </summary>
|
||||
private static VisualElement MakeRowElement()
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("ewlk-mfgorder__row");
|
||||
|
||||
// 문자열 셀 6개 (설비명~목표량)
|
||||
var textCols = new[] {
|
||||
"col-equip", "col-order", "col-item-code",
|
||||
"col-item-name", "col-time", "col-target",
|
||||
};
|
||||
foreach (var cls in textCols)
|
||||
{
|
||||
var cell = new VisualElement();
|
||||
cell.AddToClassList("ewlk-mfgorder__cell");
|
||||
cell.AddToClassList(cls);
|
||||
var lbl = new Label();
|
||||
lbl.AddToClassList("ewlk-mfgorder__cell-label");
|
||||
cell.Add(lbl);
|
||||
row.Add(cell);
|
||||
}
|
||||
|
||||
// 달성률 셀 (progress + stopped-label + pct-label)
|
||||
var achieveCell = new VisualElement();
|
||||
achieveCell.AddToClassList("ewlk-mfgorder__cell");
|
||||
achieveCell.AddToClassList("col-achievement");
|
||||
|
||||
var barWrap = new VisualElement();
|
||||
barWrap.name = "bar-wrap";
|
||||
barWrap.AddToClassList("ewlk-mfgorder__progress-wrap");
|
||||
var fill = new VisualElement();
|
||||
fill.name = "achieve-fill";
|
||||
fill.AddToClassList("ewlk-mfgorder__progress-fill");
|
||||
barWrap.Add(fill);
|
||||
|
||||
var stoppedLbl = new Label("계획 정지");
|
||||
stoppedLbl.name = "stopped-label";
|
||||
stoppedLbl.AddToClassList("ewlk-mfgorder__stopped");
|
||||
|
||||
var pctLbl = new Label();
|
||||
pctLbl.name = "pct-label";
|
||||
pctLbl.AddToClassList("ewlk-mfgorder__pct");
|
||||
|
||||
achieveCell.Add(barWrap);
|
||||
achieveCell.Add(stoppedLbl);
|
||||
achieveCell.Add(pctLbl);
|
||||
row.Add(achieveCell);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ListView 바인딩. _displayRows(실데이터 또는 플레이스홀더)를 사용합니다.
|
||||
/// </summary>
|
||||
private void BindRowElement(VisualElement element, int index)
|
||||
{
|
||||
if (index >= _displayRows.Count) return;
|
||||
var d = _displayRows[index];
|
||||
|
||||
SetLabel(element, "col-equip", d.EquipName);
|
||||
SetLabel(element, "col-order", d.OrderNo);
|
||||
SetLabel(element, "col-item-code", d.ItemCode);
|
||||
SetLabel(element, "col-item-name", d.ItemName);
|
||||
SetLabel(element, "col-time", d.StartTime);
|
||||
SetLabel(element, "col-target", d.Target);
|
||||
|
||||
var barWrap = element.Q<VisualElement>("bar-wrap");
|
||||
var fill = element.Q<VisualElement>("achieve-fill");
|
||||
var stoppedLbl = element.Q<Label>("stopped-label");
|
||||
var pctLbl = element.Q<Label>("pct-label");
|
||||
|
||||
if (d.IsStopped)
|
||||
{
|
||||
if (barWrap != null) barWrap.style.display = DisplayStyle.None;
|
||||
if (stoppedLbl != null) stoppedLbl.style.display = DisplayStyle.Flex;
|
||||
if (pctLbl != null) pctLbl.style.display = DisplayStyle.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
float pct = Mathf.Clamp(d.Achievement, 0f, 100f);
|
||||
if (barWrap != null)
|
||||
{
|
||||
barWrap.style.display = DisplayStyle.Flex;
|
||||
if (fill != null) fill.style.width = Length.Percent(pct);
|
||||
}
|
||||
if (stoppedLbl != null) stoppedLbl.style.display = DisplayStyle.None;
|
||||
if (pctLbl != null)
|
||||
{
|
||||
pctLbl.style.display = DisplayStyle.Flex;
|
||||
pctLbl.text = $"{d.Achievement:F1}%";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 헬퍼 ──────────────────────────────────────────────
|
||||
|
||||
private static void SetLabel(VisualElement row, string cellClass, string text)
|
||||
{
|
||||
var cell = row.Q(className: cellClass);
|
||||
var lbl = cell?.Q<Label>();
|
||||
if (lbl != null) lbl.text = Or(text, "-");
|
||||
}
|
||||
|
||||
private static string Or(string value, string fallback) =>
|
||||
string.IsNullOrEmpty(value) ? fallback : value;
|
||||
|
||||
/// <summary>
|
||||
/// MQTT 연결 전에 레이아웃을 미리 확인하기 위한 플레이스홀더 행 생성.
|
||||
/// Achievement = 10f 로 달성률 바 그래프를 기본 표시합니다.
|
||||
/// </summary>
|
||||
private static List<EWLKMfgOrderRowData> CreatePlaceholderRows()
|
||||
{
|
||||
var rows = new List<EWLKMfgOrderRowData>();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
rows.Add(new EWLKMfgOrderRowData
|
||||
{
|
||||
Achievement = 10f,
|
||||
IsStopped = false,
|
||||
});
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static VisualElement MakeStaticItem(string title, out Label valueLabel)
|
||||
{
|
||||
var wrap = new VisualElement();
|
||||
wrap.AddToClassList("ewlk-mfgorder__summary-item");
|
||||
|
||||
var t = new Label(title);
|
||||
t.AddToClassList("ewlk-mfgorder__summary-title");
|
||||
wrap.Add(t);
|
||||
|
||||
valueLabel = new Label("-");
|
||||
valueLabel.AddToClassList("ewlk-mfgorder__summary-value");
|
||||
wrap.Add(valueLabel);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
private static VisualElement MakeProgressItem(
|
||||
string title,
|
||||
out VisualElement fill,
|
||||
out Label valueLabel)
|
||||
{
|
||||
var wrap = new VisualElement();
|
||||
wrap.AddToClassList("ewlk-mfgorder__summary-item");
|
||||
|
||||
var t = new Label(title);
|
||||
t.AddToClassList("ewlk-mfgorder__summary-title");
|
||||
wrap.Add(t);
|
||||
|
||||
var barWrap = new VisualElement();
|
||||
barWrap.AddToClassList("ewlk-mfgorder__progress-wrap");
|
||||
fill = new VisualElement();
|
||||
fill.AddToClassList("ewlk-mfgorder__progress-fill");
|
||||
barWrap.Add(fill);
|
||||
wrap.Add(barWrap);
|
||||
|
||||
valueLabel = new Label("-");
|
||||
valueLabel.AddToClassList("ewlk-mfgorder__summary-value");
|
||||
wrap.Add(valueLabel);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
private static VisualElement BuildTableHeader()
|
||||
{
|
||||
var header = new VisualElement();
|
||||
header.AddToClassList("ewlk-mfgorder__header");
|
||||
|
||||
var cols = new[] {
|
||||
("설비명", "col-equip"),
|
||||
("지시번호", "col-order"),
|
||||
("품목코드", "col-item-code"),
|
||||
("품목명", "col-item-name"),
|
||||
("시작시간", "col-time"),
|
||||
("목표량", "col-target"),
|
||||
("달성률", "col-achievement"),
|
||||
};
|
||||
foreach (var (text, cls) in cols)
|
||||
{
|
||||
var cell = new Label(text);
|
||||
cell.AddToClassList("ewlk-mfgorder__header-cell");
|
||||
cell.AddToClassList(cls);
|
||||
header.Add(cell);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa9eed65d35268948b6b1a2cdd5f87f9
|
||||
@@ -0,0 +1,95 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.EnglewoodLAB.Data;
|
||||
|
||||
namespace UVC.EnglewoodLAB.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 충포장 설비 정보 팝업 UI 요소.
|
||||
/// 다크 배경 + 타이틀 + 데이터 항목 리스트로 구성됩니다.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class EWLKPackEquipInfoPopup : VisualElement
|
||||
{
|
||||
private const string UssPath = "EWLK/UIToolkit/Main/EWLKPackEquipInfoPopupUss";
|
||||
|
||||
private static readonly string ClassRoot = "ewlk-pack-popup";
|
||||
private static readonly string ClassTitle = "ewlk-pack-popup__title";
|
||||
private static readonly string ClassItem = "ewlk-pack-popup__item";
|
||||
private static readonly string ClassDot = "ewlk-pack-popup__dot";
|
||||
private static readonly string ClassLabel = "ewlk-pack-popup__label";
|
||||
private static readonly string ClassValue = "ewlk-pack-popup__value";
|
||||
|
||||
private readonly Label _titleLabel;
|
||||
private readonly VisualElement _itemsContainer;
|
||||
private readonly List<(Label label, Label value)> _itemCache = new();
|
||||
|
||||
public EWLKPackEquipInfoPopup()
|
||||
{
|
||||
var uss = Resources.Load<StyleSheet>(UssPath);
|
||||
if (uss != null) styleSheets.Add(uss);
|
||||
|
||||
AddToClassList(ClassRoot);
|
||||
|
||||
// 타이틀
|
||||
_titleLabel = new Label();
|
||||
_titleLabel.AddToClassList(ClassTitle);
|
||||
Add(_titleLabel);
|
||||
|
||||
// 항목 컨테이너
|
||||
_itemsContainer = new VisualElement();
|
||||
Add(_itemsContainer);
|
||||
|
||||
// 절대 위치 (Tracker가 좌표를 설정)
|
||||
style.position = Position.Absolute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 데이터를 바인딩합니다.
|
||||
/// </summary>
|
||||
public void SetData(EWLKPackEquipInfoData data)
|
||||
{
|
||||
_titleLabel.text = data.Title;
|
||||
|
||||
// 기존 항목 재사용 또는 새로 생성
|
||||
for (int i = 0; i < data.Items.Count; i++)
|
||||
{
|
||||
if (i < _itemCache.Count)
|
||||
{
|
||||
// 기존 항목 업데이트
|
||||
_itemCache[i].label.text = data.Items[i].Label;
|
||||
_itemCache[i].value.text = data.Items[i].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 새 항목 생성
|
||||
var item = new VisualElement();
|
||||
item.AddToClassList(ClassItem);
|
||||
|
||||
var dot = new VisualElement();
|
||||
dot.AddToClassList(ClassDot);
|
||||
item.Add(dot);
|
||||
|
||||
var lbl = new Label(data.Items[i].Label);
|
||||
lbl.AddToClassList(ClassLabel);
|
||||
item.Add(lbl);
|
||||
|
||||
var val = new Label(data.Items[i].Value);
|
||||
val.AddToClassList(ClassValue);
|
||||
item.Add(val);
|
||||
|
||||
_itemsContainer.Add(item);
|
||||
_itemCache.Add((lbl, val));
|
||||
}
|
||||
}
|
||||
|
||||
// 초과 항목 숨김
|
||||
for (int i = data.Items.Count; i < _itemCache.Count; i++)
|
||||
{
|
||||
_itemsContainer[i].style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08527cc6d1b0cb946876fa1246a0c128
|
||||
Reference in New Issue
Block a user