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>
This commit is contained in:
2026-06-15 21:44:23 +09:00
commit 5a96a696b1
265 changed files with 76458 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
namespace ErvProtocol
{
// 고정패킷 파서 (PC_ERV_Protocol.md Rev2.0)
// 0xAA | CMD | DATA[240] | CRC16-MODBUS(LE) = 244B 고정
// 연결(소켓/시리얼)마다 1개 인스턴스. CRC 통과 프레임만 OnFrame 으로 전달.
public sealed class FrameParser
{
public const byte STX = 0xAA;
public const int DataLen = CtrlFrame.DataLen; // 240
public event Action<byte, byte[]>? OnFrame; // (cmd, data[240])
public event Action<string>? OnError;
enum Step { Stx, Cmd, Data, CrcLo, CrcHi }
Step _step = Step.Stx;
byte _cmd, _crcLo;
readonly byte[] _data = new byte[DataLen];
int _dataPos;
public void Feed(ReadOnlySpan<byte> data)
{
foreach (var b in data) FeedByte(b);
}
public void FeedByte(byte b)
{
switch (_step)
{
case Step.Stx:
if (b == STX) _step = Step.Cmd;
break;
case Step.Cmd:
_cmd = b; _dataPos = 0; _step = Step.Data;
break;
case Step.Data:
_data[_dataPos++] = b;
if (_dataPos >= DataLen) _step = Step.CrcLo;
break;
case Step.CrcLo:
_crcLo = b; _step = Step.CrcHi;
break;
case Step.CrcHi:
ushort rxCrc = (ushort)(_crcLo | (b << 8));
_step = Step.Stx;
Verify(rxCrc);
break;
}
}
// 프레임 동기 리셋 (시리얼 프레임 갭 발생 시 등)
public void Reset() => _step = Step.Stx;
void Verify(ushort rxCrc)
{
var buf = new byte[1 + DataLen]; // CMD + DATA[240]
buf[0] = _cmd;
Array.Copy(_data, 0, buf, 1, DataLen);
ushort calc = Crc16.Modbus(buf, 0, buf.Length);
if (calc != rxCrc)
{
OnError?.Invoke($"CRC ERR cmd=0x{_cmd:X2} calc=0x{calc:X4} rx=0x{rxCrc:X4}");
return;
}
var data = new byte[DataLen];
Array.Copy(_data, data, DataLen);
OnFrame?.Invoke(_cmd, data);
}
}
}