fix: Feedforward 버그 2건 — config ordinal off-by-one + front 부호 반전
- FeedforwardConfigStore: advisory_only를 GetBoolean(31)로 읽어 IndexOutOfRange (컬럼 31개=ordinal 0~30, advisory_only=30). 30으로 수정 → FF supervisor 루프 복구 - FeedforwardEngine.ApplyFront: front metric을 Delta(temps[0],temps[^1])=하단−상단으로 계산해 부호 반전(프론트 상승 시 trim 권고 역전). Delta(temps[^1],temps[0])=상단−하단으로 수정 - FeedforwardFrontTests: 엔진 경유 부호 회귀 테스트 2건 추가 (24/24 통과) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -37,4 +37,54 @@ public class FeedforwardFrontTests
|
||||
Assert.Contains("하강", state);
|
||||
Assert.Contains("boilup", trim);
|
||||
}
|
||||
|
||||
// ── ApplyFront 부호 규약 (브레인스토밍 §10.2-A 버그픽스 회귀) ──────────
|
||||
// temp_tags = [A하단 … D상단], 정상 A>B>C>D 단조감소.
|
||||
// front metric은 "상단−하단"이어야 함: 프론트 상승(heavies↑→상단 가열) 시 metric↑ → dev>0 → 상승/환류↑.
|
||||
private static ColumnConfig FrontCfg() => new()
|
||||
{
|
||||
Id = 1, Name = "C-FRONT", Enabled = true, FeedTag = "f", ProductKey = "P", ScanSec = 2,
|
||||
DTdP = 0.0, PRef = double.NaN, PressureTag = null, // 압력경로 비활성 → pUnstable 없음
|
||||
FeedMoveThresholdPerMin = 0.0, // moving 비활성 → transient 없음
|
||||
TempTags = new[] { "a", "b", "c", "d" },
|
||||
Streams = new[] { new StreamConfig { Key = "P", FlowTag = "ficq-6118", Role = StreamRole.Commanded, Grade = Confidence.A, TargetCoeff = 0.95 } }
|
||||
};
|
||||
|
||||
private static PvSnapshot FrontSnap(double a, double b, double c, double d) => new(
|
||||
new TagSample("f", 100, true, DateTime.UtcNow),
|
||||
null,
|
||||
Array.Empty<TagSample>(),
|
||||
new Dictionary<string, TagSample> { ["P"] = new TagSample("ficq-6118", 95, true, DateTime.UtcNow) })
|
||||
{
|
||||
Temps = new[]
|
||||
{
|
||||
new TagSample("a", a, true, DateTime.UtcNow), new TagSample("b", b, true, DateTime.UtcNow),
|
||||
new TagSample("c", c, true, DateTime.UtcNow), new TagSample("d", d, true, DateTime.UtcNow)
|
||||
}
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void ApplyFront_top_warming_is_front_rise_reflux_advice()
|
||||
{
|
||||
var engine = new FeedforwardEngine();
|
||||
var st = new ColumnState();
|
||||
// 정상 단조감소(A=110>B>C>D=90)로 baseline 시드
|
||||
for (int i = 0; i < 5; i++) engine.Tick(FrontCfg(), FrontSnap(110, 105, 100, 90), st, DateTime.UtcNow);
|
||||
// 상단(D) 가열 = heavies 상승 = 프론트 상승 → 상승/환류↑ (버그였다면 하강/boilup로 반전)
|
||||
var res = engine.Tick(FrontCfg(), FrontSnap(110, 105, 100, 100), st, DateTime.UtcNow);
|
||||
Assert.Contains("상승", res.FrontPositionState);
|
||||
Assert.Equal("환류↑ 권장", res.FrontTrimAdvice);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyFront_top_cooling_is_front_fall_boilup_advice()
|
||||
{
|
||||
var engine = new FeedforwardEngine();
|
||||
var st = new ColumnState();
|
||||
for (int i = 0; i < 5; i++) engine.Tick(FrontCfg(), FrontSnap(110, 105, 100, 90), st, DateTime.UtcNow);
|
||||
// 상단(D) 추가 냉각 = 프론트 하강 → 하강/boilup
|
||||
var res = engine.Tick(FrontCfg(), FrontSnap(110, 105, 100, 80), st, DateTime.UtcNow);
|
||||
Assert.Contains("하강", res.FrontPositionState);
|
||||
Assert.Contains("boilup", res.FrontTrimAdvice);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user