# 측류추출 통합유량 — Phase III Auto-Write 구현 코딩 (RSP → DCS SP 쓰기)

> **성격**: Phase I(advisory engine) + Phase II(UI 대시보드)에서 계산한 권장 SP(RSP)를 **DCS Setpoint에 직접 쓰는(auto-write) 기능**.
> Phase I 불변식("제어 레지스터 쓰기 0건")을 해제 — **단, 안전장치(WriteGuard)를 동반**.

---

## 0. Phase 분할 현황

| 범위 | Phase I (완료) | Phase II (완료) | Phase III (본 문서) |
|:-----|:----|:----|:----|
| 엔진 | FeedforwardEngine.Tick | θ 자동튜닝·바이어스 적응 (분석) | — |
| 출력 | 권장 SP 저장 + 읽기 API | 대시보드 시각화 (흐림/선명) | **DCS SP 쓰기** |
| 제어 | Advisory(쓰기 0건) | Advisory(쓰기 0건) | Auto-write (조건부 쓰기) |
| 설정 | DB 테이블 + 로더 | Web UI 설정 에디터 | Auto-write On/Off 스위치 |
| 안전 | — | — | **WriteGuard + 워치독** |

---

## 1. 핵심 설계 원칙

### 1.1 Auto-write 조건

**RSP를 DCS SP에 쓸 수 있는 조건은 다음 두 가지를 모두 만족해야 함:**

```
WriteCondition = (Grade == A) AND (!Transient)
```

| 조건 | 의미 | 위반 시 조치 |
|:-----|:-----|:------------|
| **Grade == A** | 모델 파라미터(K, θ, τ)가 잘 튜닝됨 | Grade B/C면 쓰지 않음 |
| **!Transient** | Feed/압력 안정 + 정착 대기 완료 | Transient 중이면 이전 SP 홀드 |

### 1.2 Grade별 Auto-write 정책

| Grade | Auto-write 허용 | 근거 |
|:------|:---------------|:------|
| **A** | ✅ 허용 | K/θ/τ 튜닝 양호, 권장값 신뢰 가능 |
| **B** | ❌ 금지 | 튜닝 불확실성 존재, 운전원 수동 인가 필요 |
| **C** | ❌ 금지 | 모델 자체를 신뢰할 수 없음, 진단 우선 |

### 1.3 Transient 상태별 Auto-write 정책

| 상태 | Auto-write | RSP 처리 | DCS SP 처리 |
|:-----|:----------|:---------|:------------|
| 정상(안정) | ✅ 허용 | 최신 RSP로 갱신 | RSP = DCS SP |
| FEED 이동 중 | ❌ 홀드 | RSP는 계산 계속 (변함) | **이전 SP 유지** |
| 압력 불안정 | ❌ 홀드 | RSP는 계산 계속 (변함) | **이전 SP 유지** |
| 정착 대기 중 | ❌ 홀드 | RSP는 정상 계산 | **이전 SP 유지** |
| FEAD BAD (Hold) | ❌ 홀드 | RSP = 이전 LastRec | **이전 SP 유지** |

### 1.4 홀드 동작 상세

Transient 진입 시:

```
1. Transient 발생 (moving / pUnstable)
2. 현재 DCS SP 값을 snapshot으로 저장 (LastWrittenSP)
3. Transient가 지속되는 동안 DCS SP = LastWrittenSP 유지
4. Transient 해제 후:
   a. Grade == A → RSP로 즉시 갱신 (RSP는 transient 중에도 계속 계산됨)
   b. Grade != A → 쓰지 않음, 운전원 수동 인가 대기
```

**RSP는 transient 중에도 매 틱 갱신되므로**, transient 해제 시점에는 이미 최신 Feed를 반영한 값으로 부드럽게 전환됨 — ramp-up이 필요 없음.

---

## 2. WriteGuard 아키텍처

### 2.1 이중 조건 검증

```
[FF Engine] → RSP
                 │
                 ▼
         [WriteGuard]
           ├─ Grade == A ?
           ├─ !Transient ?
           └─ SP 변동폭 < SafetyLimit ?
                 │
          통과 ?──┤
                 │
              YES  NO
               │    │
           [Write]  [Hold]
```

### 2.2 SP 변동폭 제한 (SafetyLimit)

추가 안전장치로, 한 번에 변경 가능한 SP 폭에 상한을 둠:

```
|RSP - CurrentDCS_SP| > SafetyMaxDelta
→ 쓰지 않고 경고 로그, 운전원 확인 대기
```

제안값: `SafetyMaxDelta = RateUpPerMin × 5min` (5분 분량의 최대 변화율)

### 2.3 워치독 (Watchdog Timer)

```
- Auto-write 활성화 후 일정 시간(예: 30분) 동안
  Feed/압력 변화가 1회도 없으면 → 워치독 알람
- 의미: "RSP가 너무 오래 같음 — DCS가 FF를 추종 중인지 확인"
```

---

## 3. DCS 쓰기 인터페이스

### 3.1 쓰기 방식

DCS SP 쓰기는 OPC UA **Write 서비스를 통해** 수행:

```
OPC UA NodeId:     ns=3;s="{tag_name}.sp"  (예: "ficq-6118.sp")
데이터 타입:        Double
쓰기 권한:          WriteGuard 통과 시에만 호출
```

### 3.2 쓰기 주기

```
정상 상태:  매 Scan(2초)마다 RSP를 쓰지 않음
           → RSP 변화 감지 시에만 Write 호출
           → 변화 없으면 Skip (OPC UA 부하 감소)

Transient: 쓰지 않음 (Hold)
최초 쓰기: Auto-write On → 즉시 1회 Write
```

### 3.3 쓰기 실패 처리

```
- OPC UA Write 실패 → 3회 재시도 (1초 간격)
- 3회 실패 → Auto-write 자중단, 경고 로그
- 운전원 UI에 "DCS 쓰기 실패" 표시
```

---

## 4. UI: Auto-write On/Off 제어

### 4.1 설정 에디터 추가 항목 (Phase II ff.js 확장)

```
컬럼 편집 모달:

  [일반 설정]         [Auto-write 설정]
  컬럼명: C-6111      ☐ Auto-write 활성화
  Feed: ficq-6101     SafetyMaxDelta: ____
  ...                 워치독(분): ____

  [스트림]
  Key │ Flow 태그 │ 역할 │ 레벨태그 │ K │ θ_up │ ...
  P   │ ficq-6118 │ Commanded │    │ 0.95 │ 60 │ ...
  R   │ ficq-6113 │ Commanded │    │ 0.80 │  0 │ ...
  D   │ ficq-6114 │ LevelDriven │ lica-6113 │ 0.02 │ ...
  B   │ ficq-6116 │ LevelDriven │ li-6111   │ 0.03 │ ...
```

### 4.2 대시보드 표시

```
┌─────────────────────────────────────────────────────────┐
│  C-6111                          FEED 1000  12:34:56    │
│  [Auto-Write ● 활성]                                    │
│                                                         │
│  스트림 │ PV    │ 권장 SP │ DCS SP │ Δ    │ 신뢰 │
│  P      │ 780.2 │ 950.0   │ 950.0  │ —    │ A    │ ← auto-written
│  R      │ 623.0 │ 760.0   │ 623.0  │ -137 │ A    │ ← 수동 (환류)
│  D      │ 20.0  │ 20.0    │ 20.0   │ —    │ B    │ ← auto-written
│  B      │ 30.0  │ 30.0    │ 30.0   │ —    │ B    │ ← auto-written
│                                                         │
│  물질수지: 정상  V_loss: +0.5  수율: 95.0%              │
└─────────────────────────────────────────────────────────┘
```

- DCS SP 열 추가 (현재 DCS에 쓰여진 SP 값)
- Δ는 권장 SP - DCS SP (auto-write 중엔 보통 0)
- Auto-write 활성 상태를 헤더에 뱃지 표시

### 4.3 Auto-write 활성화 조건

```
※ 전체 Column 단위 On/Off (스트림 개별 아님)

On 전환 조건:
  - 해당 Column의 모든 스트림이 Grade A는 아니어도 됨
  - 단, Grade A인 스트림만 Auto-write 대상
  - Grade B/C 스트림은 운전원 수동 유지

Off 조건:
  - 운전원 수동 Off
  - WriteGuard 연속 실패 (3회)
  - 워치독 타임아웃
```

---

## 5. DB 변경

### 5.1 ff_column_config (ALTER TABLE)

```sql
ALTER TABLE ff_column_config ADD COLUMN IF NOT EXISTS auto_write_enabled BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE ff_column_config ADD COLUMN IF NOT EXISTS safety_max_delta DOUBLE PRECISION;  -- NULL=무제한
ALTER TABLE ff_column_config ADD COLUMN IF NOT EXISTS watchdog_min INTEGER NOT NULL DEFAULT 30;
```

### 5.2 신규 테이블: ff_write_log (감사 추적)

```sql
CREATE TABLE IF NOT EXISTS ff_write_log (
    id          BIGSERIAL PRIMARY KEY,
    column_id   INTEGER NOT NULL REFERENCES ff_column_config(id) ON DELETE CASCADE,
    stream_key  TEXT NOT NULL,
    sp_before   DOUBLE PRECISION,
    sp_after    DOUBLE PRECISION,
    grade       TEXT NOT NULL,
    transient   BOOLEAN NOT NULL,
    success     BOOLEAN NOT NULL,
    error_msg   TEXT,
    written_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

---

## 6. 구현 파일 배치

```
변경:
  src/Core/Application/Feedforward/
    FeedforwardModels.cs                      # ColumnConfig: AutoWriteEnabled, SafetyMaxDelta, WatchdogMin
    IFeedforwardStores.cs                     # (변경 없음)
  src/Infrastructure/Control/
    FeedforwardEngine.cs                      # (변경 없음 — RSP 계산 로직은 그대로)
    FeedforwardSupervisor.cs                  # Auto-write 로직 추가 (WriteGuard 호출)
    FeedforwardConfigStore.cs                 # auto_write_enabled, safety_max_delta, watchdog_min 저장/로드
  src/Infrastructure/OpcUa/
    OpcUaClientService.cs                     # WriteNodeAsync(tag, value) — OPC UA Write 래퍼
  src/Web/Controllers/
    FeedforwardController.cs                  # GET/POST config에 auto-write 필드 추가
  src/Web/wwwroot/js/ff.js                    # 설정 에디터 + 자동쓰기 On/Off + DCS SP 표시
  src/Web/wwwroot/css/ff.css                  # DCS SP 열, auto-write 뱃지 스타일

신규:
  src/Infrastructure/Control/
    WriteGuard.cs                             # 조건 검증 + SafetyLimit + 워치독
    AutoWriter.cs                             # 조건 충족 시 OPC UA Write 호출
  docs/측류추출식-통합유량설정공식-구현코딩-PhaseIII.md  # 본 문서
```

---

## 7. 안전 시나리오

### 7.1 정상 동작

```
1. 운전원이 FF 설정에서 "Auto-write 활성화" ON
2. Feed/압력 안정, Grade A
3. RSP가 DCS SP에 자동 반영 (Scan 주기로 변화 감지 시)
4. 운전원은 모니터링만
5. 비정상 상황 시 수동 OFF → 즉시 쓰기 중단
```

### 7.2 Transient 발생

```
1. Feed 급변 → moving = true
2. WriteGuard가 transient 감지 → Write 차단
3. DCS SP는 마지막 값 유지 (변경 안 됨)
4. RSP는 내부적으로 계속 계산
5. 30분 후 transient 해제
6. WriteGuard 조건 재확인 → Grade A → 새 RSP Write
7. DCS SP가 최신 RSP로 점프 (ramp 불필요 — RSP는 transient 중에도 계산됐으므로)
```

### 7.3 OPC UA 단절

```
1. OPC UA 서버와 연결 끊김
2. Write 실패 → 3회 재시도 → 실패
3. Auto-write 자동 중단
4. 운전원 UI에 "DCS 쓰기 불가" 경고
5. 연결 복구 시 자동 재개 (선택 사항 — 운전원 확인 후 재활성화)
```

### 7.4 운전원 수동 개입

```
Auto-write 활성화 상태에서 운전원이 DCS SP를 수동 변경:
→ WriteGuard 감지 (DCS SP != LastWrittenSP)
→ 자동 쓰기 일시 중단 (예: 60초)
→ 60초 후 WriteGuard가 조건 재확인
   → 조건 만족: RSP로 다시 쓰기 (운전원 변경 덮어씀)
   → 조건 불만족: 중단 유지

※ 운전원 변경을 존중하려면:
   "수동 변경 감지 시 auto-write 영구 중단" 정책도 고려
   → UI에 "운전원 수동 변경 감지 — Auto-write 중단됨" 표시
```

---

## 8. Phase III 마일스톤

| 단계 | 내용 | 비고 |
|:-----|:-----|:------|
| **M1** | WriteGuard 구현 (조건 검증 + SafetyLimit) | 단위테스트 |
| **M2** | OPC UA Write 래퍼 (WriteNodeAsync) | 기존 OpcUaClientService 확장 |
| **M3** | AutoWriter 구현 (Guard → Write) + ff_write_log | 통합테스트 |
| **M4** | Supervisor에 Auto-wire 루프 통합 + Transient 홀드 | Supervisor 확장 |
| **M5** | DB: 컬럼 추가 + Config Store 확장 | 마이그레이션 |
| **M6** | UI: 설정 에디터(Auto-write On/Off, SafetyLimit) + DCS SP 열 | ff.js 확장 |
| **M7** | UI: Auto-write 뱃지 + 쓰기 실패 표시 | 대시보드 확장 |
| **M8** | 종합 테스트 (시나리오 7.1~7.4) | 감독자 진단 |

---

## 9. Phase I 불변식 해제 선언

```
Phase I 불변식: "제어 레지스터(SP/OP)에 쓰기 호출 0건"
Phase III에서 해제. 단, 아래 조건을 모두 만족해야 함:

✅ WriteGuard가 쓰기 허가를 내린 경우에만 Write
✅ Grade A && !Transient
✅ SafetyMaxDelta 초과 시 Write 금지
✅ 쓰기 실패 3회 → 자동 중단
✅ 운전원 수동 OFF 가능
✅ 모든 쓰기 내역은 ff_write_log에 기록 (감사 추적)
```
