a502322188
손상된 .git 히스토리(missing tree)로 재초기화 후 작업트리 전체 커밋. .claude/ 만 제외(로컬 에이전트 설정). 구 저장소 백업(.git_corrupt_backup/) 포함. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
170 lines
6.8 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|