Files
jeon 5a96a696b1 chore: HERV 통합 저장소 초기 커밋
- 펌웨어(program), C# 대시보드(TestProgram), 시뮬레이터(Simulator),
  프로토콜/문서(Protocol, doc) 전체를 단일 저장소로 통합
- program 폴더의 별도 git 저장소를 제거하고 통합 저장소에 흡수
- 빌드 산출물(program/build, bin/obj, *.o/.elf/.bin/.hex 등) .gitignore 처리
- 사내 Synology NAS Git 원격 연결 예정

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:44:23 +09:00

103 lines
3.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace ErvProtocol
{
// StatusRecord → STATUS(0x81) 134B payload. StatusDecoder 의 역(逆).
// ERV(메인보드/시뮬레이터) 측이 STATUS 를 송신할 때 사용. PC_ERV_Protocol.md 4장.
public static class StatusEncoder
{
static void W16(byte[] p, int off, int v) // 빅엔디안
{
p[off] = (byte)((v >> 8) & 0xFF);
p[off + 1] = (byte)(v & 0xFF);
}
// StatusRecord → 134B payload
public static byte[] Encode(StatusRecord s)
{
var p = new byte[StatusDecoder.STATUS_LEN];
// 글로벌 0~16
p[0] = s.Power;
p[1] = s.RunMode;
p[2] = s.AutoState;
p[3] = s.FanMode;
p[4] = s.SubMode;
p[5] = s.Hood;
p[6] = s.HystPreset;
W16(p, 7, s.HystPm25);
W16(p, 9, s.HystPm10);
W16(p, 11, s.HystVoc);
W16(p, 13, s.HystCo2);
W16(p, 15, s.ErrorCode);
// 각실 17~ (14B x 4)
const int baseOff = 17;
for (int r = 0; r < 4; r++)
{
int o = baseOff + r * 14;
var room = s.Rooms[r];
p[o + 0] = room.Damper;
W16(p, o + 1, room.Pm25);
W16(p, o + 3, room.Pm10);
W16(p, o + 5, room.Voc);
W16(p, o + 7, room.Co2);
p[o + 9] = room.AirQuality;
p[o + 10] = room.LedDim;
W16(p, o + 11, room.LoadScore);
p[o + 13] = room.FinalVolume;
}
// ERV 리셋 (offset 73)
p[73] = s.Reset;
// 풍량 VSP (offset 74~, 9엔트리 × u16 SA·EA)
const int vspOff = 74;
for (int i = 0; i < 9; i++)
{
int o = vspOff + i * 4;
W16(p, o, s.Vsp[i].Sa);
W16(p, o + 2, s.Vsp[i].Ea);
}
// 히스테리시스 프리셋 테이블 (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;
W16(p, o, s.HystTable[i].Pm25);
W16(p, o + 2, s.HystTable[i].Pm10);
W16(p, o + 4, s.HystTable[i].Voc);
W16(p, o + 6, s.HystTable[i].Co2);
}
// 모드별 오염단계 임계표 (offset 134~, 3프리셋 × [CO2,PM2.5,PM10,VOC] × L1~L4)
for (int i = 0; i < 3; i++)
{
int o = StatusDecoder.THR_OFF + i * 32;
for (int k = 0; k < 4; k++) W16(p, o + 0 + k * 2, s.ThrTable[i].Co2[k]);
for (int k = 0; k < 4; k++) W16(p, o + 8 + k * 2, s.ThrTable[i].Pm25[k]);
for (int k = 0; k < 4; k++) W16(p, o + 16 + k * 2, s.ThrTable[i].Pm10[k]);
for (int k = 0; k < 4; k++) W16(p, o + 24 + k * 2, s.ThrTable[i].Voc[k]);
}
// 각실 온도/습도 (offset 230~, 4실 × [Temp, Humi])
for (int r = 0; r < 4; r++)
{
int o = StatusDecoder.TEMPHUMI_OFF + r * 2;
p[o + 0] = s.Rooms[r].Temp;
p[o + 1] = s.Rooms[r].Humi;
}
// (꺼짐)예약 잔여초 (offset 238, u16)
W16(p, StatusDecoder.RESERVE_OFF, s.ReserveRemainSec);
return p;
}
// STATUS(0x81) 완성 프레임 (CRC 포함)
public static byte[] BuildStatusFrame(StatusRecord s) => CtrlFrame.Build(StatusDecoder.STATUS, Encode(s));
// ACK(0x82) 프레임 : [echoCmd][result(0 OK/1 ERR)]
public static byte[] BuildAckFrame(byte echoCmd, byte result) => CtrlFrame.Build(StatusDecoder.ACK, echoCmd, result);
}
}