156 lines
3.0 KiB
Go
156 lines
3.0 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
type ParsedMessage struct {
|
|
typeByte byte
|
|
payloadBytes []byte
|
|
}
|
|
|
|
func (pm *ParsedMessage) Type() byte {
|
|
return pm.typeByte
|
|
}
|
|
|
|
func (pm *ParsedMessage) Payload() []byte {
|
|
return pm.payloadBytes
|
|
}
|
|
|
|
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() (pm ParsedMessage, err error) {
|
|
if !pa.IsMessageAvailable() {
|
|
return ParsedMessage{}, fmt.Errorf("No message available")
|
|
}
|
|
lastMessage := pa.messages[0]
|
|
pa.messages = pa.messages[1:]
|
|
|
|
return lastMessage, nil
|
|
}
|