- 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>
13 KiB
작업지시서 — 안전 피드램프 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.BuildSnapshotAsyncoverride 우선) + 안전가드(_sim.Enabled시 auto-write 억제). - WP3 FeedRampAdvisor:
FeedRampCalculator(순수),FeedRampAdvisorService,GET /api/ff/ramp-advisor, DTOFeedRampModels.cs. - §10 역전판정:
TempProfileJudge(순수), 엔진JudgeTempProfile(탑정 D 제외, spanRef 시드, 역전/붕괴 시 front trim HOLD),AdvisoryResult.{TempProfileState,InversionPair,TempSpan,TempSpanRef}+ 컨트롤러 노출. - 테스트 37/37, 라이브 S1~S5·S7·front·역전 검증 완료.
0.1 공통 제약 (전 WP)
- 쓰기 금지(OPC/제어 SP)는 advisory 전부에 유지. WriteGuard·ARM 경유만 실쓰기(기존). 신규 advisory는 계산/표시만.
- DEMO: 실시간 값 = SP + 일정변동 반복(합성). 물리 동특성 추정 금지. temps는 비물리(공간구배 미인코딩) → 물리 검증은 override로 단조/특정 프로파일 주입해야.
- Sim Override로 자율검증:
POST /api/ff/sim/override {enabled,values:{"tag.pv":v}}→ 엔진/ramp-advisor 모두 반영(~2s tick). 단 stale(S6)은 불가(override=항상 fresh), transient(P4)는FeedMoveThresholdPerMin>0필요. - JSON: 신규 응답은 컨트롤러에서 익명객체 camelCase 매핑(기존
MapColumn/MapRamp패턴). 비유한(NaN/Inf)은 null로. - 빌드/테스트:
dotnet build src/Web/ExperionCrawler.csproj(경고0/에러0),dotnet test tests/ExperionCrawler.Tests/ExperionCrawler.Tests.csproj. - 커밋: 기본 브랜치(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 동반 시에만 공정으로 인정).
단계
FeedforwardEngine.ApplyRecovery시그니처에string? tempProfileState추가.Tick에서tp.State전달(JudgeTempProfile 결과; 이미 계산됨).- 신호 정의:
sigInv = tempProfileState=="온도역전",sigCollapse=="프로파일붕괴".- 코러보레이션:
corroborated = sigVloss || sigDp(기존 severity 신호). tempSevere = (sigInv||sigCollapse) && corroborated→severe |= tempSevere.- 비코러보(온도만):
sigInv && !corroborated→ severe 아님. 대신 advisory 메시지 "온도역전(센서 점검 권고 — ΔP/물질수지 정상)" 노출(새 필드 또는 modeReason 보강).
SeverityText()에tempSevere면 "온도역전/붕괴" 추가.- (주의) Δ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.
단계
- Δ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) 운전값으로 설정.
- 옵션A(권장): supervisor/엔진에서 계산해
- 압력 프로파일 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(무변경) 유지.
- config:
delta_p_tag·dtdp·delta_p_flood_limit는 기존 컬럼 존재 → 값만 세팅(운전원/UI). 프로파일 보간 비율은 신규 const/config.
산출물/합격
- 단위테스트: dtdp≠0 + 프로파일 압력 → 각 temp pct가 국소압 반영(상·하 다른 보정). ΔP 합성 동작.
- 라이브: override로 pica/pi-6111b·temps 주입 →
/api/ff/advisorytemps.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(C−D)·하부 ΔT(C−B), 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/compositionGET·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(C−D) 그대로 사용: 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(C−B)=temps[n-2]−temps[n-3] (긴 중간 패킹). 정상 C<B(음수). 중질 상승→C 가열→C−B↑(dev>0)→B↑.
- 상부(D/경질) = ΔT(C−D)=temps[n-2]−temps[n-1] (상부 정류 패킹). 정상 C>D(양수). 경질 침투→C−D↓(dev<0)→환류↑. (방향 calibrate.)
- 각 별도
FrontPositionIndicator(ColumnState 2개). n≥3 필요. trim(2단계): 하부dev→B, 상부dev→D, ±5% clamp + rate≤듀티대역폭. 데모는 단조 temps override로 calibrate.
단계 (결정 후)
- config 확장:
UpperSensitiveTrayTag+LowerSensitiveTrayTag(ff_column_config 신규 컬럼 + ExperionDbContext boot DDL + FeedforwardConfigStore SELECT/INSERT/UPDATE + MapConfig). 또는 섹션별 차온쌍. - 2-point front:
FrontPositionIndicator2개 인스턴스(ColumnState). 상부=ΔT(TI-6111C−TI-6111D보정), 하부=ΔT(TI-6111C−TI-6111B). ApplyFront 섹션별 반환. - fast trim(초~분): 섹션 front 편차 → bounded trim(clamp+rate). B=하부, D=상부.
- 귀속: vloss를 "프론트 드리프트하는 스트림"에 배분(상부 고정·하부 이동=B).
- slow re-baseline(시간): vLossMa+랩분석 → base 분율 재보정(운전원 리뷰, 자동 아님).
- 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 패널.
단계
- 램프 계산기 패널(
#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 응답 그대로. - tempProfileState 뱃지: 카드에
tempProfileState(정상/약화/붕괴/온도역전) 색상 뱃지 +inversionPair·tempSpan. 역전 시 경고색 + frontTrim "HOLD" 표기. - sim override 패널(개발/데모 전용,
SimOverrideEnabled시만 노출): 태그-값 입력 →POST /api/ff/sim/override,DELETE로 해제.simOverrideActive표시. - 기존 ff.js 패턴(
ffCard, fetch, esc) 재사용.node -c ff.js문법 확인.
산출물/합격
- 브라우저에서 램프 계산기 동작(입력→결과), 역전 뱃지 표시, sim 패널로 주입/해제.
dotnet build영향 없음(정적자산). Ctrl+F5 후 확인.
권장 순서
- 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(역전).