5a96a696b1
- 펌웨어(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>
70 lines
2.3 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|