Files
HECO2/TestProgram/ErvProtocol/FrameParser.cs
T
jeon a502322188 chore: HERV 통합 저장소 재초기화 커밋
손상된 .git 히스토리(missing tree)로 재초기화 후 작업트리 전체 커밋.
.claude/ 만 제외(로컬 에이전트 설정). 구 저장소 백업(.git_corrupt_backup/) 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:32:17 +09:00

70 lines
2.3 KiB
C#

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);
}
}
}