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