namespace ErvProtocol { public static class StatusDecoder { public const byte STATUS = 0x81; // ERV→PC 상태 public const byte ACK = 0x82; // ERV→PC 제어 응답 public const int STATUS_LEN = 240; // 238 + 2((꺼짐)예약 잔여초 u16) public const int THR_OFF = 134; // 모드별 오염단계 임계표 시작 오프셋 public const int TEMPHUMI_OFF = 230; // 각실 온도/습도 블록 시작 (4실 × [Temp,Humi]) public const int RESERVE_OFF = 238; // (꺼짐)예약 잔여초 u16 static int U16(byte[] p, int off) => (p[off] << 8) | p[off + 1]; // 빅엔디안 // STATUS(0x81) payload(73B) → StatusRecord. 길이 부족 시 null. public static StatusRecord? Decode(byte[] p) { if (p.Length < STATUS_LEN) return null; var s = new StatusRecord { Power = p[0], RunMode = p[1], AutoState = p[2], FanMode = p[3], SubMode = p[4], Hood = p[5], HystPreset = p[6], HystPm25 = U16(p, 7), HystPm10 = U16(p, 9), HystVoc = U16(p, 11), HystCo2 = U16(p, 13), ErrorCode = U16(p, 15), }; const int baseOff = 17; for (int r = 0; r < 4; r++) { int o = baseOff + r * 14; var room = s.Rooms[r]; room.Damper = p[o + 0]; room.Pm25 = U16(p, o + 1); room.Pm10 = U16(p, o + 3); room.Voc = U16(p, o + 5); room.Co2 = U16(p, o + 7); room.AirQuality = p[o + 9]; room.LedDim = p[o + 10]; room.LoadScore = U16(p, o + 11); room.FinalVolume = p[o + 13]; } // ERV 리셋 (offset 73) s.Reset = p[73]; // 풍량 VSP 블록 (offset 74~, 9엔트리 × u16 SA·EA) const int vspOff = 74; for (int i = 0; i < 9; i++) { int o = vspOff + i * 4; s.Vsp[i].Sa = U16(p, o); s.Vsp[i].Ea = U16(p, o + 2); } // 히스테리시스 프리셋 테이블 (offset 110~, 3프리셋 × PM2.5/PM10/VOC/CO2 u16) const int hystOff = 110; for (int i = 0; i < 3; i++) { int o = hystOff + i * 8; s.HystTable[i].Pm25 = U16(p, o); s.HystTable[i].Pm10 = U16(p, o + 2); s.HystTable[i].Voc = U16(p, o + 4); s.HystTable[i].Co2 = U16(p, o + 6); } // 모드별 오염단계 임계표 (offset 134~, 3프리셋 × [CO2,PM2.5,PM10,VOC] × L1~L4 u16) for (int i = 0; i < 3; i++) { int o = THR_OFF + i * 32; for (int k = 0; k < 4; k++) s.ThrTable[i].Co2[k] = U16(p, o + 0 + k * 2); for (int k = 0; k < 4; k++) s.ThrTable[i].Pm25[k] = U16(p, o + 8 + k * 2); for (int k = 0; k < 4; k++) s.ThrTable[i].Pm10[k] = U16(p, o + 16 + k * 2); for (int k = 0; k < 4; k++) s.ThrTable[i].Voc[k] = U16(p, o + 24 + k * 2); } // 각실 온도/습도 (offset 230~, 4실 × [Temp, Humi]) for (int r = 0; r < 4; r++) { int o = TEMPHUMI_OFF + r * 2; s.Rooms[r].Temp = p[o + 0]; s.Rooms[r].Humi = p[o + 1]; } // (꺼짐)예약 잔여초 (offset 238, u16) s.ReserveRemainSec = U16(p, RESERVE_OFF); return s; } } }