# 측류추출 통합유량 — 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에 기록 (감사 추적) ```