using ErvProtocol; namespace ErvCollector.Storage { // 저장 정책: "N초 주기 + 상태(이산값) 변화 시 즉시" // - 연속값(센서 PM/VOC/CO2 등)은 주기 샘플 // - 이산값(전원/모드/팬/부가/후드/프리셋/에러 + 각실 댐퍼/공기질등급/LED)이 바뀌면 즉시 기록 public sealed class Sampler { readonly TimeSpan _interval; string? _lastFingerprint; DateTime _lastWriteUtc = DateTime.MinValue; public Sampler(int intervalSeconds) { _interval = TimeSpan.FromSeconds(intervalSeconds <= 0 ? 10 : intervalSeconds); } // 기록해야 하면 true (그리고 내부 상태 갱신) public bool ShouldWrite(StatusRecord s, DateTime nowUtc, out string reason) { string fp = Fingerprint(s); bool changed = fp != _lastFingerprint; bool periodic = (nowUtc - _lastWriteUtc) >= _interval; if (changed || periodic) { reason = changed ? (_lastFingerprint == null ? "first" : "change") : "periodic"; _lastFingerprint = fp; _lastWriteUtc = nowUtc; return true; } reason = "skip"; return false; } static string Fingerprint(StatusRecord s) { // 이산 상태/제어값만 모아 변화 감지 (연속 센서값 제외) var sb = new System.Text.StringBuilder(48); sb.Append(s.Power).Append(s.RunMode).Append('.').Append(s.AutoState).Append('.') .Append(s.FanMode).Append('.').Append(s.SubMode).Append('.').Append(s.Hood).Append('.') .Append(s.HystPreset).Append('.').Append(s.ErrorCode).Append('.').Append(s.Reset); foreach (var r in s.Rooms) sb.Append('|').Append(r.Damper).Append(r.AirQuality).Append(r.LedDim); // VSP(설정값) 변경도 즉시 기록 foreach (var v in s.Vsp) sb.Append('#').Append(v.Sa).Append(',').Append(v.Ea); // 히스테리시스 프리셋 테이블 변경도 즉시 기록 foreach (var h in s.HystTable) sb.Append('@').Append(h.Pm25).Append(',').Append(h.Pm10).Append(',').Append(h.Voc).Append(',').Append(h.Co2); return sb.ToString(); } } }