# INVYONE Stage-2 — Water Treatment Digital Twin Demo

수처리 플랜트 SCADA 시연용 인터랙티브 디지털 트윈. 실제 PLC/DB 연결은 없는 **샌드박스형 시각 제어 데모**.

## 목적

- 입찰 PT에서 **"이 시스템이 시각적으로 어떻게 제어되는가"** 를 보여주는 용도
- 정해진 시나리오를 따라가는 데모가 아니라, **발표자가 어디든 클릭/조작해도 자연스럽게 반응**하는 자유 제어형
- 각 컴포넌트가 독립 제어 가능 + 흐름·압력·알람이 서로 연동되어 **시스템 레벨 제어 가능성**을 입증

## 실행

`index.html` 을 브라우저에서 열기. 외부 의존성·빌드 단계 없음.

## 구성

```
invyone-stage2-demo-v1/
├── index.html                    메인 페이지 (인라인 SVG 캔버스)
├── css/invyone-stage2.css        SCADA 다크 테마 + 컴포넌트 상태 스타일
├── js/
│   ├── components.js             SVG 컴포넌트 템플릿 (탱크/펌프/밸브/파이프/모듈/게이지/센서)
│   ├── topology.js               플랜트 노드 정의 + 흐름 그래프 + buildScene()
│   ├── engine.js                 state + tick loop + 흐름/압력/알람 시뮬레이션
│   ├── ui.js                     클릭 위임 + 사이드 패널 + 모드 버튼
│   └── main.js                   부트스트랩
├── svg/
│   ├── pressure-gauge.svg        아날로그 압력계 원본 (참조용)
│   └── carbon-cartridge-bank.svg 카본 카트리지 뱅크 (C1~C5 개별 ID)
└── docs/README.md
```

## 인터랙션

| 액션 | 결과 |
|---|---|
| 펌프 클릭 | ON/OFF 토글, 임펠러 회전 시작/정지, LED 색 변경 |
| 밸브 클릭 | OPEN/CLOSE 토글, 핸들 90° 회전 |
| 탱크 클릭 | 사이드 패널에서 슬라이더로 수위 0~100% 조절 |
| 센서 클릭 | 사이드 패널에서 값 직접 입력 (임계 초과 시 알람) |
| 카트리지 클릭 | 격리(BYPASS) 토글 — 시각적으로 흐릿해지고 외곽 빨간 점선 |
| 빈 영역 클릭 | 선택 해제 |

## 운전 모드 프리셋

상단 사이드바의 모드 버튼 하나로 밸브/펌프 조합을 한 번에 세팅:

- **NORMAL** — 정상 운전 (전처리 → UF → WF → RO → MBR → DP → 방류)
- **BACKWASH** — 역세 (UF 라인만, 다른 라인 차단)
- **CIP** — 화학 세정 (CIP 탱크 → RO 시스템 순환)
- **STOP** — 전체 정지
- **+ 약품 투입** — UF 인입에 케미컬 도징 ON

## 흐름 모델

각 펌프는 `source` 탱크에서 `dest` 탱크로 운반. 다음이 모두 충족되면 흐름이 활성:
1. 펌프 ON
2. 펌프 경로의 모든 밸브 OPEN
3. source 탱크 잔량 > 0
4. dest 탱크 여유 > 0

활성 시 매 250ms tick마다 `LPM × (RPM/1450) × flowMultiplier × (250/60000)` 리터를 source에서 차감 / dest에 적립. 흐름선(파이프 dash)은 active 클래스로 애니메이션.

**유량 배율** 슬라이더로 시연 속도 조절 (0.5x ~ 50x). 기본 5x.

## 알람

- **탱크**: HH(95%↑) / HL(85%↑) / LH(5↓) / LL(20↓) — % 박스 색·외곽 변경 + 이벤트 로그
- **센서**: 각 센서별 lo/hi 임계 — 경계 초과 시 빨간 펄스
- **압력계**: 75% 이상이면 배경 어두운 빨강
- **데모 알람 버튼**: 한 클릭으로 RAW HH + WF LL + pH HI 동시 트리거

## Console API (브라우저 개발자도구)

```js
INVYONE.help()                           // 사용법 출력
INVYONE.engine.state                     // 전체 상태 객체
INVYONE.engine.applyMode('NORMAL')       // 모드 변경
INVYONE.engine.togglePump('uf-pump-a')   // 펌프 토글
INVYONE.engine.toggleValve('v-uf-in')    // 밸브 토글
INVYONE.engine.setTankLevel('raw', 90)   // 탱크 수위
INVYONE.engine.setSensor('ph', 9.5)      // 센서 값
INVYONE.engine.triggerDemoAlarm()        // 데모 알람 트리거
INVYONE.engine.reset()                   // 초기화
```

## 시연 가이드 (PT용 추천 흐름)

발표자가 즉흥적으로 어디든 시연 가능하지만, 다음 시퀀스가 임팩트 큼:

1. **시작**: STOP 상태에서 시작 → "현재 모든 라인 정지"
2. **전체 가동**: NORMAL 클릭 → 모든 펌프 가동, 흐름 시작, 탱크 수위 변동
3. **개별 제어**: UF 펌프 클릭으로 OFF → 그 라인만 흐름 멈춤
4. **알람 시연**: ⚠ 알람 데모 클릭 → 탱크/센서 일괄 알람, 이벤트 로그 폭주
5. **자유 조작**: 어떤 밸브든 닫아보기 → 즉시 경로 재계산되어 차단 표시
6. **카트리지 격리**: MBR 카본 카트리지 하나 클릭 → 개별 부품 제어 가능 시연
7. **모드 전환**: CIP → BACKWASH → NORMAL → 운전 모드 프리셋의 신속성
8. **유량 가속**: 50x로 올림 → 탱크 수위 빠른 변동 → 시간축 압축 시연

## 한계 (PT용 데모 — 이후 단계로 분리)

- 실제 PLC/MQTT/OPC UA 데이터 채널 없음 (mock state)
- 안전 인터록·권한 분리·이력관리(historian)·알람 ack 미구현
- 압력 모델은 단순 휴리스틱 (실제 펌프 곡선·배관 마찰 무시)
- 단일 페이지 (메인 화면만) — 실 입찰 스코프는 폐수3 + 오수2 + 약품탱크실8 + 대기29기로 화면 분할 필요
- ID 컨벤션 단일 DOM 가정 (다중 인스턴스 시 prefix 필요)

## 다음 단계 (Stage-3 후보)

1. **다화면 라우터**: 메인 종합 + 약품탱크실 + 대기방지 시설 화면 분리 (각 화면을 같은 엔진 위에 올림)
2. **WebSocket 데이터 채널**: mock 대신 실제 PLC/시뮬레이터 데이터 입력
3. **이력 그래프**: 탱크 수위/압력 트렌드 차트 (Chart.js 같은 경량 라이브러리)
4. **알람 ack 워크플로우**: 알람 발생 → 운영자 확인 → 해소 로그
5. **사용자 권한**: 알람 OFF·중요 동작은 패스워드 게이트
6. **i18n**: 한·영·베트남어 토글 (입찰처가 베트남법인이므로 베트남어 지원 가치 있음)
