Files
ExperionCrawler/plans/안전피드램프-후속작업-작업지시서-WP4-7.md
windpacer 49cf04569e feat: WP5 2·3단계 — 조성 base + 유계 trim (B_SP=분율×feed+편차, advisory)
- ApplyCompositionTrim: LevelDriven 드로우 유계 trim(±5% clamp, B=하부front dev·D=상부front dev, 과도/역전 시 0)
- CompositionStore + /api/ff/composition(랩 분율 수동입력) → supervisor가 TargetCoeff 치환(없으면 config K)
- StreamAdvisory.{CompositionBase,Trim,RecommendedSpComposition,TrimSource} + 컨트롤러·ff.js 표시
- 단위 4건(49/49). advisory·쓰기없음. 게인·부호·분율·rate는 현장 calibrate(데모 검증불가)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 21:19:07 +09:00

159 lines
13 KiB
Markdown
Raw 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.
# 작업지시서 — 안전 피드램프 Advisory 후속 (WP4~WP7)
> 작성 2026-06-01. 다른 LLM/개발자 단독 실행용. 사전 맥락 없이 이 문서 + 참조로 수행.
> 컬럼 C-6111. **선행 통독 필수**: `docs/안전피드램프-한계치-브레인스토밍.md`(§0~§10), `plans/안전피드램프-advisory-작업지시서.md`(WP0~WP3), `docs/안전피드램프-검증시나리오매트릭스.md`.
---
## 0. 현재 상태 (main, 커밋 ~4e1dfc1까지 완료)
이미 구현·검증된 것:
- **버그픽스**: FeedforwardConfigStore ordinal off-by-one, FeedforwardEngine.ApplyFront front 부호(상단−하단).
- **WP0 Sim Override**: `SimOverrideStore`(ISimOverrideStore), `GET/POST/DELETE /api/ff/sim/override`, `appsettings Feedforward:SimOverrideEnabled=true`(DEMO 게이트). **엔진까지 통합**(`FeedforwardSupervisor.BuildSnapshotAsync` override 우선) + 안전가드(`_sim.Enabled` 시 auto-write 억제).
- **WP3 FeedRampAdvisor**: `FeedRampCalculator`(순수), `FeedRampAdvisorService`, `GET /api/ff/ramp-advisor`, DTO `FeedRampModels.cs`.
- **§10 역전판정**: `TempProfileJudge`(순수), 엔진 `JudgeTempProfile`(탑정 D 제외, spanRef 시드, 역전/붕괴 시 front trim HOLD), `AdvisoryResult.{TempProfileState,InversionPair,TempSpan,TempSpanRef}` + 컨트롤러 노출.
- 테스트 37/37, 라이브 S1~S5·S7·front·역전 검증 완료.
## 0.1 공통 제약 (전 WP)
1. **쓰기 금지**(OPC/제어 SP)는 advisory 전부에 유지. WriteGuard·ARM 경유만 실쓰기(기존). 신규 advisory는 계산/표시만.
2. **DEMO**: 실시간 값 = SP + 일정변동 반복(합성). 물리 동특성 추정 금지. temps는 비물리(공간구배 미인코딩) → 물리 검증은 override로 단조/특정 프로파일 주입해야.
3. **Sim Override로 자율검증**: `POST /api/ff/sim/override {enabled,values:{"tag.pv":v}}` → 엔진/ramp-advisor 모두 반영(~2s tick). 단 stale(S6)은 불가(override=항상 fresh), transient(P4)는 `FeedMoveThresholdPerMin>0` 필요.
4. **JSON**: 신규 응답은 컨트롤러에서 **익명객체 camelCase 매핑**(기존 `MapColumn`/`MapRamp` 패턴). 비유한(NaN/Inf)은 null로.
5. **빌드/테스트**: `dotnet build src/Web/ExperionCrawler.csproj`(경고0/에러0), `dotnet test tests/ExperionCrawler.Tests/ExperionCrawler.Tests.csproj`.
6. **커밋**: 기본 브랜치(main)면 브랜치 먼저. WP별 분리 커밋 권장.
## 0.2 C-6111 핵심 (변경 없음)
- 스트림: P(ficq-6118,Commanded,K0.95,θ60,τ900,sp_max2000,rate30), R(ficq-6113,Commanded,K0.8), D(ficq-6114,LevelDriven,K0.02,lica-6113), B(ficq-6116,LevelDriven,K0.03,li-6111). feed=ficq-6101.
- 컬럼: pressure=pica-6111, **dtdp=0(PCT off)**, delta_p_tag=null, sensitive_tray_tag=null, temp_tags=`tica-6111a,ti-6111b,ti-6111c,ti-6111d`.
- 추가 태그: `pi-6111b.pv`(≈리보일러 진공압), `ti-6103.pv`(프리히터 공급온도), `ficq-6115.pv`(스팀 flow 측정), `tica-6111a.op`(스팀밸브 OP 직결).
- 레이아웃(위→아래): pica-6111/REFLUX DIST(R)/TI-6111D/상부PACKING(정류,D)/제품추출 P(70~75%)/TI-6111C(제품 pivot)/긴중간PACKING(주분리,B)/TI-6111B/PREHEATER DIST(피드,TI-6103)/하부짧은PACKING(stripping)/TICA-6111A 리보일러(pi-6111b≈여기)→B.
- **정상 온도 A>B>C>D 단조감소**. TI-6111D는 환류 서브쿨 오염(조성 proxy 부적합).
## 0.3 코드 색인
| 역할 | 경로 |
|---|---|
| 엔진 | `src/Infrastructure/Control/FeedforwardEngine.cs` (Tick/ComputeStream/ApplyFront/ApplyRecovery/JudgeTempProfile) |
| supervisor | `src/Infrastructure/Control/FeedforwardSupervisor.cs` (BuildSnapshotAsync, AutoWriteAsync) |
| 계산기/판정 | `FeedRampCalculator.cs`, `TempProfileJudge.cs` |
| 모델 | `src/Core/Application/Feedforward/FeedforwardModels.cs`, `FeedRampModels.cs` |
| config store/DDL | `FeedforwardConfigStore.cs`, `src/Infrastructure/Database/ExperionDbContext.cs` |
| 컨트롤러 | `src/Web/Controllers/FeedforwardController.cs` (route api/ff) |
| 프론트 | `src/Web/wwwroot/js/ff.js`, `index.html`, `css` |
| 테스트 | `tests/ExperionCrawler.Tests/` |
---
## WP4 — §10 역전을 ApplyRecovery에 연동 + 코러보레이션 (난이도 中) ✅ 2026-06-01
### 목적
온도 역전/붕괴를 전환류 복귀 트리거 severity에 추가하되, **단발 센서이상이 곧장 recovery를 트리거하지 않도록 코러보레이션**(ΔP/vloss 동반 시에만 공정으로 인정).
### 단계
1. `FeedforwardEngine.ApplyRecovery` 시그니처에 `string? tempProfileState` 추가. `Tick`에서 `tp.State` 전달(JudgeTempProfile 결과; 이미 계산됨).
2. 신호 정의:
- `sigInv = tempProfileState=="온도역전"`, `sigCollapse=="프로파일붕괴"`.
- **코러보레이션**: `corroborated = sigVloss || sigDp`(기존 severity 신호).
- `tempSevere = (sigInv||sigCollapse) && corroborated``severe |= tempSevere`.
- **비코러보(온도만)**: `sigInv && !corroborated` → severe 아님. 대신 advisory 메시지 "온도역전(센서 점검 권고 — ΔP/물질수지 정상)" 노출(새 필드 또는 modeReason 보강).
3. `SeverityText()``tempSevere`면 "온도역전/붕괴" 추가.
4. (주의) ΔP 코러보는 WP6(pi-6111b→ΔP) 이후에야 유효. 그 전엔 vloss 코러보만 동작 → 코드는 둘 다 지원하되 ΔP 없으면 vloss로.
### 산출물/합격
- 단위테스트(`FeedforwardRecoveryTests`에 추가): ① 역전+vloss불균형 → severe(전환류 진입 경로), ② 역전 단독 → severe 아님 + 센서점검 메시지.
- 라이브: override로 (a)역전+B과추출(vloss) → recovery 트리거(RecoveryEnabled 필요), (b)역전만 → 트리거 안 됨.
- 빌드0/테스트 통과.
---
## WP6 — 압력 서브시스템 깨우기 (§9) (난이도 中, WP4·WP5 선행 권장) ✅ 2026-06-01
### 목적
pi-6111b를 활용해 **전탑 ΔP**(flooding) + **압력 프로파일 기반 PCT**를 활성화. 현재 dtdp=0·delta_p_tag=null로 dormant.
### 단계
1. **ΔP 파생**: `pi-6111b.pv pica-6111.pv` = 전탑 ΔP.
- 옵션A(권장): supervisor/엔진에서 계산해 `pv.DeltaP`에 주입(현재 `cfg.DeltaPTag`로 읽는 구조). `BuildSnapshotAsync`에서 DeltaPTag 없으면 `pi-6111b pica-6111`로 합성.
- flooding ceiling(ramp-advisor)·ApplyRecovery sigDp가 이 ΔP 사용. `DeltaPFloodLimit`(config, 현재 MaxValue) 운전값으로 설정.
2. **압력 프로파일 PCT**: 현재 `BuildTemps`가 단일 압력(cfg.PressureTag)+dtdp. 확장:
- 각 temp에 **국소 압력** 부여 = 높이 보간(또는 패킹가중): ti-6111d≈pica, tica-6111a≈pi-6111b, ti-6111c/ti-6111b=보간. 보간 비율은 config 또는 const(예: 높이 0/0.33/0.66/1.0 → 실제 표고 미상이라 우선 균등).
- `TempCorrection.PressureCompensated(raw, p_local, pRef_local, dtdp)`. pRef도 프로파일(또는 단일 pRef + 동일 dtdp).
- `dtdp`를 config에서 실측 보정계수로(현재 0). **dtdp=0이면 PCT=raw(무변경)** 유지.
3. config: `delta_p_tag`·`dtdp`·`delta_p_flood_limit`는 기존 컬럼 존재 → 값만 세팅(운전원/UI). 프로파일 보간 비율은 신규 const/config.
### 산출물/합격
- 단위테스트: dtdp≠0 + 프로파일 압력 → 각 temp pct가 국소압 반영(상·하 다른 보정). ΔP 합성 동작.
- 라이브: override로 pica/pi-6111b·temps 주입 → `/api/ff/advisory` temps.pct ≠ raw, flooding ceiling이 합성 ΔP 사용.
- **주의**: PCT 활성화는 §10 역전판정·front 입력을 바꾸므로 WP4 이후 회귀 확인.
---
## WP5 — 편차 trim + 2-point sensitive tray (§8·§8.8) (난이도 高) — **완료 ✅ 2026-06-01 (1·2·3단계)**
> **1단계**: 2-point front(`ApplyFront2Point`, 상부 ΔT(CD)·하부 ΔT(CB), C=pivot temps[n-2], 중립 상태).
> **2단계**: `ApplyCompositionTrim` — LevelDriven 드로우에 유계 trim(±5% clamp, B=하부dev·D=상부dev). 과도/역전 시 trim 0. `StreamAdvisory.{CompositionBase,Trim,RecommendedSpComposition,TrimSource}`.
> **3단계**: `CompositionStore`(랩 분율 수동입력, `/api/ff/composition` GET·POST·DELETE) → supervisor가 LevelDriven TargetCoeff 치환(없으면 config K). `B_SP = 분율×feed + trim`.
> 단위 4건 추가(49/49 통과). **advisory(쓰기 없음)**.
> **현장 calibrate 필요(데모 검증 불가)**: trim 게인(현 1%/°C placeholder)·부호·조성 분율값. rate제한(듀티 대역폭)은 후속(현재 clamp만).
### 목적
LevelDriven 드로우 권장을 `K×feed`(무한상승)에서 **[조성목표(분율×feed)] + [bounded 편차 trim]** 구조로. 편차는 §8.4 2-타임스케일.
### 선행 결정 (확정 2026-06-01)
- **조성 분율 = 랩/원료분석 수동입력**: 배치별 중비물/경비물 분율을 운전원 입력(신규 필드/태그). base = 분율×feed.
- **D 처리 = ΔT(CD) 그대로 사용**: TI-6111D는 "오염" 아님 — (상승 기상물+환류 비산액) 혼합으로 하부 대비 ~10°C 낮은 유효온도(정정). 상부 front에 D 정상 사용.
- **편차 clamp = ±5%(보수)** + rate ≤ 듀티 대역폭(상호작용 방지).
- **config role = LevelDriven 유지** + trim은 **advisory 표시만(쓰기 없음)**. 재분류·level_tag 강등 안 함.
### 2-point front 부호 spec (C=제품 pivot=temps[n-2], 하단→상단 [A,B,C,D])
- **하부(B/중질) = ΔT(CB)=temps[n-2]temps[n-3]** (긴 중간 패킹). 정상 C<B(음수). 중질 상승→C 가열→CB↑(dev>0)→**B↑**.
- **상부(D/경질) = ΔT(CD)=temps[n-2]temps[n-1]** (상부 정류 패킹). 정상 C>D(양수). 경질 침투→CD↓(dev<0)→**환류↑**. (방향 calibrate.)
- 각 별도 `FrontPositionIndicator`(ColumnState 2개). n≥3 필요. trim(2단계): 하부dev→B, 상부dev→D, ±5% clamp + rate≤듀티대역폭. 데모는 단조 temps override로 calibrate.
### 단계 (결정 후)
1. config 확장: `UpperSensitiveTrayTag`+`LowerSensitiveTrayTag`(ff_column_config 신규 컬럼 + ExperionDbContext boot DDL + FeedforwardConfigStore SELECT/INSERT/UPDATE + MapConfig). 또는 섹션별 차온쌍.
2. 2-point front: `FrontPositionIndicator` 2개 인스턴스(ColumnState). 상부=ΔT(TI-6111CTI-6111D보정), 하부=ΔT(TI-6111CTI-6111B). ApplyFront 섹션별 반환.
3. fast trim(초~분): 섹션 front 편차 → bounded trim(clamp+rate). B=하부, D=상부.
4. 귀속: vloss를 "프론트 드리프트하는 스트림"에 배분(상부 고정·하부 이동=B).
5. slow re-baseline(시간): vLossMa+랩분석 → base 분율 재보정(운전원 리뷰, 자동 아님).
6. SP 산출: `B_SP = 분율×feed + trim`(advisory; 쓰기는 기존 정책).
### 산출물/합격
- 단위테스트: 상/하 front 분리, trim 부호·clamp, 귀속.
- 라이브: override로 하부 front 드리프트 → B trim만, 상부 → D trim만.
- **범위 주의**: 가장 크고 설계 미확정. 1단계(2-point front + 표시) → 2단계(trim) → 3단계(조성base SP)로 **분할 권장**.
---
## WP7 — 프론트 UI (ff.js) (난이도 中, 독립 진행 가능) ✅ 2026-06-01
### 목적
운전원 화면에 ① 피드 램프 계산기 ② 온도 프로파일 상태(역전) 표시 ③ (개발용) sim override 패널.
### 단계
1. **램프 계산기 패널**(`#pane-ff` 또는 카드 상단): 입력 targetFeed/deltaIAllow/sensibleGain/feedTempRef/floodLimit → `GET /api/ff/ramp-advisor?columnId=&...` → 결과표(clampedTarget, ceiling{value,binding}, rampRate{value,binding}, rampTimeMin, steam{from,to}, warnings). camelCase 응답 그대로.
2. **tempProfileState 뱃지**: 카드에 `tempProfileState`(정상/약화/붕괴/온도역전) 색상 뱃지 + `inversionPair`·`tempSpan`. 역전 시 경고색 + frontTrim "HOLD" 표기.
3. **sim override 패널**(개발/데모 전용, `SimOverrideEnabled` 시만 노출): 태그-값 입력 → `POST /api/ff/sim/override`, `DELETE`로 해제. `simOverrideActive` 표시.
4. 기존 ff.js 패턴(`ffCard`, fetch, esc) 재사용. `node -c ff.js` 문법 확인.
### 산출물/합격
- 브라우저에서 램프 계산기 동작(입력→결과), 역전 뱃지 표시, sim 패널로 주입/해제.
- `dotnet build` 영향 없음(정적자산). Ctrl+F5 후 확인.
---
## 권장 순서
1. **WP4**(역전→recovery, 작음) → 2. **WP6**(압력/ΔP/PCT — WP4 코러보 ΔP 공급) → 3. **WP7**(UI, 독립) → 4. **WP5**(편차 trim, 설계결정 후 분할).
## 검증 공통 (sim override 자율 루프)
```
curl -s -XPOST localhost:5000/api/ff/sim/override -H 'Content-Type: application/json' -d '{"enabled":true,"values":{...}}'
sleep 3; curl -s localhost:5000/api/ff/advisory # 엔진 반영
curl -s 'localhost:5000/api/ff/ramp-advisor?columnId=1&targetFeed=1100&...'
curl -s -XDELETE localhost:5000/api/ff/sim/override
```
**앱 재기동** 필요(코드 변경 반영): 운전원이 `dotnet run` 재시작.
## 참조 / 메모리
- 메모리: `project_safe_feed_ramp_brainstorm`, `project_demo_system_synthetic_data`, `reference_json_serializer_pascalcase`, `project_sidedraw_ff_advisory`, `project_realtime_collector_stall`.
- `docs/안전피드램프-한계치-브레인스토밍.md` §7(편차/스팀)·§8(편차소스)·§9(압력)·§10(역전).