- ColumnConfig.TempHighLimit + ff_column_config.temp_high_limit(DDL/ConfigStore/MapConfig) + ff.js 설정폼 - ApplyRecovery sigTHigh: 온도태그 최고값(raw) > TempHighLimit → 단독 severe(운전원 명시 안전한계라 코러보 불요). 기본 1e9=비활성 - 컬럼별 설정이라 타 물질/컬럼 동일 루틴 재사용. 단위 3건(52/52). 매뉴얼 §12.2·§13.3 반영 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
3.1 KiB
C#
71 lines
3.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using ExperionCrawler.Core.Application.Feedforward;
|
|
using ExperionCrawler.Infrastructure.Control;
|
|
using Xunit;
|
|
|
|
namespace ExperionCrawler.Tests;
|
|
|
|
// 온도 HIGH LIMIT(raw, UI 설정) → 전환류 트리거(단독 severe). 코러보 불요.
|
|
public class FeedforwardTempHighLimitTests
|
|
{
|
|
private static ColumnConfig Cfg(double tempHigh) => new()
|
|
{
|
|
Id = 1, Name = "C-TH", Enabled = true, FeedTag = "f", ProductKey = "P", ScanSec = 2,
|
|
PressureTag = null, FeedMoveThresholdPerMin = 0.0,
|
|
RecoveryEnabled = true, RecoveryAutoArm = true, // 자동무장(테스트 간소화)
|
|
ImbalanceTriggerFrac = 0.10, ImbalanceTriggerSec = 4, // 짧게
|
|
TempHighLimit = tempHigh,
|
|
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 Snap(double maxTemp) => 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", maxTemp, true, DateTime.UtcNow), new TagSample("b", maxTemp - 5, true, DateTime.UtcNow),
|
|
new TagSample("c", maxTemp - 10, true, DateTime.UtcNow), new TagSample("d", maxTemp - 20, true, DateTime.UtcNow)
|
|
}
|
|
};
|
|
|
|
[Fact]
|
|
public void Temp_above_high_limit_triggers_recovery_standalone()
|
|
{
|
|
var engine = new FeedforwardEngine(); var st = new ColumnState();
|
|
var cfg = Cfg(tempHigh: 120); // 한계 120
|
|
AdvisoryResult res = null!; string entryReason = "";
|
|
// 최고온도 130 > 120, 다른 신호(vloss/ΔP) 없음에도 지속 시 단독 트리거.
|
|
// 진입 틱의 사유에 "온도HIGH" 표기(이후 틱은 평형대기).
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
res = engine.Tick(cfg, Snap(130), st, DateTime.UtcNow);
|
|
if (res.Mode == ColumnMode.Recovering && (res.ModeReason ?? "").Contains("진입")) entryReason = res.ModeReason!;
|
|
}
|
|
Assert.Equal(ColumnMode.Recovering, res.Mode);
|
|
Assert.Contains("온도HIGH", entryReason);
|
|
}
|
|
|
|
[Fact]
|
|
public void Temp_below_limit_no_trigger()
|
|
{
|
|
var engine = new FeedforwardEngine(); var st = new ColumnState();
|
|
var cfg = Cfg(tempHigh: 120);
|
|
AdvisoryResult res = null!;
|
|
for (int i = 0; i < 6; i++) res = engine.Tick(cfg, Snap(110), st, DateTime.UtcNow); // 110 < 120
|
|
Assert.Equal(ColumnMode.Normal, res.Mode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Default_limit_disabled()
|
|
{
|
|
var engine = new FeedforwardEngine(); var st = new ColumnState();
|
|
var cfg = Cfg(tempHigh: double.MaxValue); // 기본=비활성
|
|
AdvisoryResult res = null!;
|
|
for (int i = 0; i < 6; i++) res = engine.Tick(cfg, Snap(500), st, DateTime.UtcNow); // 매우 높아도
|
|
Assert.Equal(ColumnMode.Normal, res.Mode);
|
|
}
|
|
}
|