# [SPEC] 인터페이스 에뮬레이팅 — Sensor·Actuator·Modbus 통합 시연

> **버전**: 1.0 (2026-05-12 작성)
> **소스**: 개발계획서 §1 (입출력부 기능 후 — "인터페이스 에뮬레이팅 기능")
> **상태**: spec 확정 / 구현 v0.1.157 예약
> **선행**: Photosensor 모듈 (v0.1.148/151) + Servo Axis 모듈 (v0.1.154/155) + 양방향 PLC sync 확립

## §0 목적

factory00 디지털 트윈의 **시연 자산** — 외부 실 PLC 장비 없이도 Photosensor 감지 → Actuator 정지 → Modbus 코일 sync 일련 동작을 한 빌드 안에서 재현. B2B 미팅·동영상·고객 데모에서 "실제로 작동한다"를 1분 안에 보일 수 있는 환경.

**스코프**:
- Photosensor (입력부) + Servo Axis (구동부) + Mock PLC + Modbus 코일 4종 일괄 동작
- 외부 PLC 시뮬레이터 (Modicon Modbus Slave / EasyModbusTCP) 연동 가이드
- 단축키 트리거 (F12 등) — 빌드 안에서 사용자가 클릭 1회로 시연 시작
- 토스트 + Player.log 로 결과 검증 (사용자 PC 모니터 직접 확인 가능)

**비스코프**:
- 실 PLC HW 통신 (Mitsubishi MX Component · OPC UA · Siemens S7) — 사용자 SDK 도착 후 별도 사이클
- 시각 효과 (이펙트 / 사운드 / 진동) — 디지털 트윈 본질 외
- 다중 부품 동시 시연 (sensor 5개 + actuator 5개 시퀀스) — Phase 2 확장

## §1 통합 시연 시나리오 (3 phase)

### Phase A: 부품 자동 어셈블리 (3초)

| t | 동작 | 결과 |
|---|---|---|
| 0.0s | F12 키 눌림 | RunInterfaceEmulationDemo() 발화 |
| 0.5s | 첫 부품 자동 선택 (`LoadedRoot` 첫 leaf) | 부품 1개 highlight + 토스트 "데모 시작 — XX 부품" |
| 1.0s | Photosensor 자동 추가 (없는 경우) | "포토센서 부착됨 (코일 10)" |
| 1.5s | Servo Axis 자동 추가 (없는 경우) | "구동부 부착됨 (코일 11-14, 레지스터 100)" |
| 2.0s | 코일 매핑 자동 (Sensor=10, Servo Forward=11, Backward=12, Stop=13, Home=14, RegPos=100) | "Modbus 매핑 자동 (10-14, 100)" |
| 2.5s | Inspector 자동 펼침 | 사용자 시각 확인 가능 |

### Phase B: 시연 동작 (5초)

| t | 동작 | 코일 변화 | 결과 |
|---|---|---|---|
| 3.0s | Servo CommandForward 호출 | C11 ON (rising edge → Forward 명령) | Actuator MovingForward 상태 진입 |
| 3.0~5.0s | Actuator 전진 (default RPM 3000, Lead 10mm → MaxVel 0.5m/s, 2s 동안 +1m 이동) | RegPos 100에 mm 정수 write (0 → 1000 mm) | Transform.localPosition 갱신, 자식 메시 추종 |
| 5.0s | Photosensor 감지 (Actuator가 sensor 근처로 이동) | C10 ON (rising edge → SensorStateChangedEvent) | 토스트 "포토센서 감지 — 정지 명령 전송" |
| 5.5s | 사용자 코드 또는 PLC ladder가 C13 ON → Servo CommandStop | Actuator state = Stopped | 토스트 "Actuator 정지" |
| 6.0s | 정지 위치 확인 | RegPos 100 = 약 1000 mm | Inspector 런타임 표시 = 1.0 m |

### Phase C: 회수 (3초)

| t | 동작 | 결과 |
|---|---|---|
| 8.0s | C14 ON (rising edge → CommandHome) | Actuator HomingReturn 상태 |
| 8.0~10.5s | Actuator Home 위치(0m)로 복귀 | RegPos 100 = 0 mm |
| 10.5s | AxisHomedEvent 발화 | 토스트 "Home 복귀 완료 — 데모 종료" |

총 시연 시간: ~10.5초.

## §2 신호 흐름 (시퀀스 다이어그램)

```
F12 keydown
    │
    ▼
BootstrapEmulationDemo.Run()
    │
    ├─[Part 자동 선택]──▶ _selectedPart
    │
    ├─[Photo/Servo 부착]
    │   │
    │   ├─▶ Part.Sensors.Add(new PhotoSensorComponent { ModbusCoil=10 })
    │   └─▶ Part.Actuators.Add(new ServoAxisActuator {
    │            CoilForward=11, CoilBackward=12, CoilStop=13, CoilHome=14,
    │            RegisterPos=100 })
    │
    ├─[Servo CommandForward]
    │   │
    │   ▼
    │   ServoAxisActuator._state = MovingForward
    │   │
    │   ▼ (매 프레임 ActuatorDriver.Update)
    │   ServoAxisActuator.UpdateActuator()
    │       ├─ CurrentPos += velocity * dt
    │       ├─ Transform.localPosition = initPos + dir × (CurrentPos × 1000)  ← mm 변환
    │       └─ EventBus.Publish(AxisPositionChangedEvent)
    │
    ├─[ActuatorPlcBridge Update (매 프레임)]
    │   │
    │   ▼
    │   ushort raw = (int)(CurrentPos × 1000) + 32768
    │   ModbusTcpAdapter.WriteHoldingRegister(100, raw)
    │       └─▶ PLC RegPos 100 (mm 정수 + signed shift)
    │
    ├─[PhotoSensor 감지 (50ms 폴링)]
    │   │
    │   ▼ (SensorPollingDriver)
    │   Physics.OverlapSphereNonAlloc(origin, SensingDistance × 1000, _buf)
    │       └─ angle check → Detected = true
    │   ▼
    │   EventBus.Publish(SensorStateChangedEvent { ModbusCoil=10, Value=true })
    │
    ├─[SensorPlcBridge 수신]
    │   │
    │   ▼
    │   ModbusTcpAdapter.WriteBit("COIL:10", true)
    │       └─▶ PLC C10 = ON
    │
    ├─[외부 PLC ladder 또는 데모 코드 — C10 감지]
    │   │
    │   ▼
    │   ModbusTcpAdapter.WriteBit("COIL:13", true)   ← Stop 명령
    │
    ├─[ActuatorPlcBridge Update]
    │   │
    │   ▼ (rising edge 검출)
    │   _state.CommandStop()
    │       └─▶ Servo state = Stopped
    │
    └─[FlashToast 출력]
        "포토센서 감지 → Actuator 정지 (위치 = 1.0 m)"
```

## §3 Mock PLC vs 외부 PLC 시뮬레이터

### 3.1 Mock PLC (Default)

`Factory00.Plc.MockPlcAdapter` — 메모리 기반 Dictionary<string, bool/ushort>. Bootstrap에서 `Modbus = new MockPlcAdapter()` 로 교체 가능.

**장점**:
- 외부 SW 설치 불필요. 한 빌드 내 동작
- 즉시 시연 가능 (사용자 PC 추가 설치 0)
- 회귀 검증 자동화 친화 (단위 테스트 가능)

**한계**:
- "실제 PLC와 통신한다"는 의미 약함 (마케팅 자료 톤 약화)
- 외부 PLC ladder logic 검증 불가

### 3.2 외부 PLC 시뮬레이터 (Phase 2 옵션)

권장 도구:
| 도구 | 라이선스 | 특징 |
|---|---|---|
| Modbus Slave (모디콘) | 30일 trial / $129 | 가장 보편, GUI 친화 |
| EasyModbusTCP | 무료 (GPL) | C# 라이브러리, Server 모드 가능 |
| pyModbusTCP | 무료 (MIT) | Python, 스크립트 friendly |
| ModbusPal | 무료 | Java, 다국어 UI |

연동 절차 (사용자 PC):
1. 외부 시뮬레이터 실행 → 127.0.0.1:502 Listen
2. factory00 빌드 실행
3. F12 데모 시작 → ModbusTcpAdapter가 127.0.0.1:502 connect
4. 외부 시뮬레이터 GUI에서 Coil 10 ON/OFF 확인 (실시간)

### 3.3 어댑터 선택 정책

| 환경 | 권장 |
|---|---|
| 사이클 회귀 자동 검증 / CI | MockPlcAdapter (단위 테스트) |
| 미팅 시연 / 동영상 | 외부 시뮬레이터 (실제 통신 시각 강조) |
| 영구 코드 path | IPlcAdapter 인터페이스로 분기 (현재 ModbusTcpAdapter / MockPlcAdapter 둘 다 구현) |

## §4 단축키 / UI 트리거

### 4.1 F12 — 데모 시작 (default)

Bootstrap에 등록. spec §1 Phase A/B/C 시퀀스 1회 발화.

```csharp
// Factory00Bootstrap.cs Update()
if (Input.GetKeyDown(KeyCode.F12) && !_demoRunning)
{
    StartCoroutine(RunInterfaceEmulationDemo());
}
```

쇼트컷 충돌 검증: F1~F4 (카메라 모드), F11 (좌측 패널 collapse) 사용 중. F12는 미사용. 안전.

### 4.2 ContextMenu "데모 실행" 항목 (옵션)

우클릭 → "도구" → "인터페이스 에뮬레이팅 데모 실행". 마우스 사용자 친화.

### 4.3 Inspector 버튼 (옵션)

"+ 데모 실행" 버튼 추가. 부품 선택 시 가시. 단, Inspector 영역이 본 사이클 폴리시 누적으로 이미 복잡 — 단축키만 우선.

## §5 사용자 안내 (USER-GUIDE)

### 5.1 빠른 시작 (60초)

1. `factory00.exe` 실행
2. 모델 자동 로드 대기 (~3초)
3. **F12** 키 누름
4. 화면 우상단 토스트 5건 순서대로 출현 — 자동 데모 진행
5. 종료 토스트 "Home 복귀 완료" 출현

### 5.2 외부 PLC 시뮬레이터 연동 (5분)

(spec §3.2 절차 그대로 — USER-GUIDE 발췌)

### 5.3 데모 중단 / 재실행

- 중단: ESC 또는 F12 재차 (running flag false)
- 재실행: F12 다시 누름 (running flag true → Phase A부터 재시작)

## §6 회귀 차단 / 검증

### 6.1 Stage 1 (어시스턴트 자동 검증)

PostMessage(WM_KEYDOWN, VK_F12)로 F12 발화 → 12초 대기 → Player.log grep:
- "데모 시작" 토스트 호출
- "포토센서 부착" / "구동부 부착" 토스트
- "포토센서 감지 → 정지" 토스트
- "Home 복귀 완료" 토스트

5건 모두 발견 = PASS.

### 6.2 Stage 2 (사용자 PC 실)

사용자 직접 F12 → 시각 확인:
- Inspector 부품 카드 9 위젯 (Photosensor) + 9 위젯 (Servo) 모두 출현
- Actuator 이동 동안 Transform x mm 값이 0 → 1000 → 0 변화
- 토스트 5건 골드 톤 출현

## §7 코드 영향 추정

| 영역 | 신규 / 수정 | 라인 |
|---|---|---|
| Factory00Bootstrap.cs | `RunInterfaceEmulationDemo()` 코루틴 + F12 keydown | +120 |
| (옵션) ContextMenu "데모" 항목 | RegisterContextMenuItems 1개 추가 | +15 |
| (옵션) PlayerPrefs "demo-coil-map-1" 등 매핑 영구 보존 | 작은 helper | +20 |

총 ~150라인 추정. 본 사이클 (spec only) = 0라인 코드, 구현은 v0.1.157로 분리.

## §8 결정 이력

| 결정 | 사유 |
|---|---|
| F12 단축키 default | F1~F11 모두 사용 중. F12 안전 + Edit 키 아래 위치 |
| Coil 10/11/12/13/14 default | factory00 ModbusMappingTable.cs 기본 범위 (10-19 = 입출력 영역) |
| RegPos 100 default | factory00 기본 (100-199 = position area, 200-299 = velocity area) |
| spec only 1 사이클 | 사이클 한도 ≤ 800라인 spec / ≤ 1000라인 코드. 두 가지 동시 = 한도 초과 |
| Mock + 외부 시뮬 둘 다 지원 | 환경별 trade-off 명확. IPlcAdapter 추상화로 분기 비용 작음 |
| Phase B 5초 시연 시간 | 사용자 시각 인지 + 영상 캡처 모두 합리적 (3초는 짧음, 10초는 김) |

## §9 spec 한계 / 차후 확장

- **Phase 2**: 다중 부품 시퀀스 (sensor 3개 + actuator 3개 동시 동작)
- **Phase 3**: 외부 PLC ladder 자동 다운로드 (PLC vendor SDK 사용자 도착 후)
- **Phase 4**: 시연 녹화 자동 (Unity Recorder + 토스트 텍스트 캡션)

## §10 외부 URL (구현 후 갱신)

- spec: `os-file-server.tail2b816d.ts.net/topics/specs/2026-05-12-interface-emulation.md`
- 구현 release notes: `os-file-server.tail2b816d.ts.net/topics/release-notes/v0.1.157.md` (예약)
- 시연 동영상: `os-file-server.tail2b816d.ts.net/qa-screenshots/v0.1.157/demo.mp4` (예약)
