Files
ExperionCrawler/docs/측류추출식-통합유량설정공식-구현코딩-PhaseIII.md

338 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 측류추출 통합유량 — 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에 기록 (감사 추적)
```