package parser import ( "fmt" ) type ParsedMessage struct { typeByte byte payloadBytes []byte } type ParserConfig struct { StartByte byte } func NewParserConfig() *ParserConfig { return &ParserConfig{ StartByte: 0xAA, } } type ParserErrorCodes int type ParserError struct { Code ParserErrorCodes Message string } const ( ChecksumError ParserErrorCodes = iota ) func (e *ParserError) Error() string { return e.Message } type ParserState int const ( StateWaitForStartByte ParserState = iota StateReadType StateReadLength StateReadPayload StateCheckChecksum ) type Parser struct { messages []ParsedMessage currentMessageBuffer []byte currentMsgType byte readLength byte readIndex byte parserState ParserState parserConfig ParserConfig checksum byte } func NewParser(cfg ParserConfig) *Parser { return &Parser{ messages: []ParsedMessage{}, currentMessageBuffer: []byte{}, currentMsgType: 0, readLength: 0, readIndex: 0, parserState: StateWaitForStartByte, parserConfig: cfg, checksum: 0, } } func (pa *Parser) ParseBytes(p []byte) (n int, err error) { for i, b := range p { err := pa.parseByte(b) if err != nil { return i, err } } return len(p), nil } func (pa *Parser) parseByte(p byte) error { switch pa.parserState { case StateWaitForStartByte: if p == pa.parserConfig.StartByte { pa.parserState = StateReadType pa.readIndex = 0 pa.checksum = 0 } break case StateReadType: pa.currentMsgType = p pa.checksum ^= p pa.parserState = StateReadLength case StateReadLength: pa.readLength = p pa.checksum ^= p pa.parserState = StateReadPayload break case StateReadPayload: if pa.readIndex < pa.readLength { pa.currentMessageBuffer = append(pa.currentMessageBuffer, p) pa.checksum ^= p pa.readIndex++ if pa.readIndex == pa.readLength { pa.parserState = StateCheckChecksum } } break case StateCheckChecksum: if pa.checksum != p { return &ParserError{ Code: ChecksumError, Message: fmt.Sprintf("Checksum does not Match, want %v, got %v", pa.checksum, p), } } pm := &ParsedMessage{ typeByte: pa.currentMsgType, payloadBytes: pa.currentMessageBuffer, } pa.messages = append(pa.messages, *pm) pa.reset() break } return nil } func (pa *Parser) reset() { pa.currentMessageBuffer = nil pa.currentMsgType = 0 pa.readIndex = 0 pa.readLength = 0 pa.checksum = 0 pa.parserState = StateWaitForStartByte } func (pa *Parser) IsMessageAvailable() bool { return len(pa.messages) > 0 } func (pa *Parser) GetNextMessage() (typeByte byte, payload []byte, err error) { if !pa.IsMessageAvailable() { return 0x00, nil, fmt.Errorf("No message available") } lastMessage := pa.messages[0] pa.messages = pa.messages[1:] return lastMessage.typeByte, lastMessage.payloadBytes, nil }