Files
HECO2/Simulator/ERVSimulator/Program/Model/DamperSequencer.cs
T
jeon a502322188 chore: HERV 통합 저장소 재초기화 커밋
손상된 .git 히스토리(missing tree)로 재초기화 후 작업트리 전체 커밋.
.claude/ 만 제외(로컬 에이전트 설정). 구 저장소 백업(.git_corrupt_backup/) 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:32:17 +09:00

170 lines
6.8 KiB
C#

using System;
using System.Windows.Threading;
namespace ERVSimulator.Model
{
// 펌웨어 [MyMotor.c] Damper_Mode() + Diffuser_Damper_process() 시퀀스를 흉내
// - Cmd 변경 시 18초 시퀀스 트리거 (slot 180 / 120 / 60, 100ms tick)
// - 본체 댐퍼 6개: Run_Mode 에 따라 즉시 목표각 세팅
// - 디퓨저 댐퍼: Memory → Current 슬롯별 복사
// - 팬 PWM: 매 tick ±1 ramp
public class DamperSequencer
{
public ErvState State { get; }
private readonly DispatcherTimer _timer;
private int _diffuserSlot; // 180..0 카운트다운
private int _seqType; // 1=on, 2=off, 3=decrease, 4=increase
private int _prevAirVolume;
private bool _pendingSequence;
public DamperSequencer(ErvState state)
{
State = state;
_timer = new DispatcherTimer(DispatcherPriority.Normal)
{
Interval = TimeSpan.FromMilliseconds(100)
};
_timer.Tick += OnTick;
_timer.Start();
}
// RoomCon/HomeNet 핸들러가 Run_Mode/Fan_Mode 갱신 후 호출
public void NotifyCommandChanged()
{
_pendingSequence = true;
}
void OnTick(object? sender, EventArgs e)
{
// ---- Fan_Speed_process() 시작부: VENT && Fan=0 → 정지 진입 ----
int newAirVolume = (State.RunMode != RunMode.Off && State.FanMode != 0) ? State.FanMode : 0;
if (_pendingSequence)
{
_pendingSequence = false;
_diffuserSlot = 180;
_seqType = DetermineSeqType(_prevAirVolume, newAirVolume);
_prevAirVolume = newAirVolume;
}
// ---- Damper_Mode(Run_Mode) — 본체 댐퍼 즉시 목표각 ----
ApplyBodyDamperMode(EffectiveBodyMode());
// ---- Diffuser_Damper_process() — 슬롯 기반 적용 ----
if (_diffuserSlot == 180)
{
if (_seqType == 1 || _seqType == 4)
CopyMemoryToCurrent(1, 2);
else if (_seqType == 2 || _seqType == 3)
SetFanTargets(); // 즉시 ramp 시작
}
else if (_diffuserSlot == 120)
{
if (_seqType == 1 || _seqType == 4 || _seqType == 2 || _seqType == 3)
CopyMemoryToCurrent(3, 4);
}
else if (_diffuserSlot == 60)
{
if (_seqType == 1 || _seqType == 4)
SetFanTargets();
else if (_seqType == 2 || _seqType == 3)
CopyMemoryToCurrent(1, 2);
}
if (_diffuserSlot > 0) _diffuserSlot--;
// ---- 팬 ramp ±1 (펌웨어와 동일) ----
if (State.Fan1Current < State.Fan1Target) State.Fan1Current++;
else if (State.Fan1Current > State.Fan1Target) State.Fan1Current--;
if (State.Fan2Current < State.Fan2Target) State.Fan2Current++;
else if (State.Fan2Current > State.Fan2Target) State.Fan2Current--;
}
int DetermineSeqType(int prev, int now)
{
if (prev == 0 && now != 0) return 4; // increase (power on)
if (prev != 0 && now == 0) return 3; // decrease (power off)
if (prev > now) return 3;
return 4;
}
RunMode EffectiveBodyMode()
{
// VENT && Fan=0 → 본체 댐퍼는 MODE_OFF 로 진입 (펌웨어 Fan_Speed_process 분기)
if (State.RunMode == RunMode.Off) return RunMode.Off;
if (State.RunMode == RunMode.Ventilation && State.FanMode == 0 &&
State.Fan1Current == 0 && State.Fan2Current == 0)
return RunMode.Off;
return State.RunMode;
}
// 펌웨어 Damper_Mode() — MyMotor.c:472
void ApplyBodyDamperMode(RunMode mode)
{
switch (mode)
{
case RunMode.Ventilation:
SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 100);
SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 70); SetBody(DamperId.AIR, 105);
break;
case RunMode.AirClean:
SetBody(DamperId.OA, 100); SetBody(DamperId.EA, 100); SetBody(DamperId.BYPASS, 100);
SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 140); SetBody(DamperId.AIR, 0);
break;
case RunMode.Bypass:
SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 0);
SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 140); SetBody(DamperId.AIR, 105);
break;
case RunMode.Auto:
// 펌웨어는 자동 시 공기질에 따라 VENT/AIR 선택. 단순화: VENT 와 동일.
SetBody(DamperId.OA, 0); SetBody(DamperId.EA, 0); SetBody(DamperId.BYPASS, 100);
SetBody(DamperId.SA, 0); SetBody(DamperId.RA, 70); SetBody(DamperId.AIR, 105);
break;
case RunMode.Off:
default:
SetBody(DamperId.OA, 100); SetBody(DamperId.EA, 100); SetBody(DamperId.BYPASS, 100);
SetBody(DamperId.SA, 100); SetBody(DamperId.RA, 0); SetBody(DamperId.AIR, 105);
break;
}
}
void SetBody(DamperId id, int angle) => State.GetDamper(id).TargetAngle = angle;
void CopyMemoryToCurrent(int fromRoom, int toRoom)
{
for (int r = fromRoom; r <= toRoom; r++)
{
var room = State.GetRoom(r);
room.CurrentSA = room.MemorySA;
room.CurrentRA = room.MemoryRA;
}
}
// 펌웨어 Fan_Speed_Setting(Run_Mode, Fan_Mode) — MyMotor.c:1233
void SetFanTargets()
{
int idx = System.Math.Clamp(State.FanMode, (byte)0, (byte)4);
switch (State.RunMode)
{
case RunMode.Ventilation:
case RunMode.Auto:
State.Fan1Target = State.FanSAPreset_Vent[idx];
State.Fan2Target = State.FanEAPreset_Vent[idx];
break;
case RunMode.Bypass:
State.Fan1Target = State.FanSAPreset_Bypass[idx];
State.Fan2Target = State.FanEAPreset_Bypass[idx];
break;
case RunMode.AirClean:
State.Fan1Target = State.FanSAPreset_Air[idx];
State.Fan2Target = State.FanEAPreset_Air[idx];
break;
default:
State.Fan1Target = 0;
State.Fan2Target = 0;
break;
}
}
}
}