129 lines
2.2 KiB
Go
129 lines
2.2 KiB
Go
package uart
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"alox.tool/api"
|
|
"alox.tool/eventbus"
|
|
)
|
|
|
|
const (
|
|
StartByte = 0xAA
|
|
EscapeByte = 0xBB
|
|
EndByte = 0xCC
|
|
)
|
|
|
|
type parserState int
|
|
|
|
const (
|
|
stateWaitingForStart parserState = iota
|
|
stateGetID
|
|
stateEscapedID
|
|
stateInPayload
|
|
stateEscapedPayload
|
|
)
|
|
|
|
type Parser struct {
|
|
bus eventbus.EventBus
|
|
state parserState
|
|
parsedData []byte
|
|
rawCapture []byte
|
|
checksum byte
|
|
}
|
|
|
|
func New(bus eventbus.EventBus) *Parser {
|
|
return &Parser{
|
|
bus: bus,
|
|
state: stateWaitingForStart,
|
|
parsedData: make([]byte, 0, 1024*4),
|
|
rawCapture: make([]byte, 0, 1024*4),
|
|
}
|
|
}
|
|
|
|
func (p *Parser) reset() {
|
|
p.state = stateWaitingForStart
|
|
p.parsedData = p.parsedData[:0]
|
|
p.rawCapture = p.rawCapture[:0]
|
|
p.checksum = 0
|
|
}
|
|
|
|
func (p *Parser) pushError(reason string) {
|
|
// Throw Error on the Bus befor resetting
|
|
p.bus.Publish(api.TopicUARTError, fmt.Errorf("%s: %02X", reason, p.rawCapture))
|
|
p.reset()
|
|
}
|
|
|
|
func (p *Parser) emitFrame() {
|
|
if len(p.parsedData) == 0 {
|
|
p.reset()
|
|
return
|
|
}
|
|
|
|
// Copy Data for Message Frame
|
|
dataCopy := make([]byte, len(p.parsedData)-1) // Exclude ID
|
|
copy(dataCopy, p.parsedData[1:])
|
|
|
|
f := api.Frame{
|
|
Time: uint64(time.Now().UnixNano()),
|
|
ID: p.parsedData[0],
|
|
Data: dataCopy,
|
|
}
|
|
|
|
p.bus.Publish(api.TopicUARTRx, f)
|
|
p.reset()
|
|
}
|
|
|
|
func (p *Parser) addByte(b byte) {
|
|
p.parsedData = append(p.parsedData, b)
|
|
p.checksum ^= b
|
|
}
|
|
|
|
func (p *Parser) ParseByte(b byte) {
|
|
p.rawCapture = append(p.rawCapture, b)
|
|
|
|
switch p.state {
|
|
case stateWaitingForStart:
|
|
if b == StartByte {
|
|
p.reset()
|
|
p.rawCapture = append(p.rawCapture, b) // Start Byte behalten
|
|
p.state = stateGetID
|
|
}
|
|
|
|
case stateGetID:
|
|
if b == EscapeByte {
|
|
p.state = stateEscapedID
|
|
} else {
|
|
p.addByte(b)
|
|
p.state = stateInPayload
|
|
}
|
|
|
|
case stateEscapedID:
|
|
p.addByte(b)
|
|
p.state = stateInPayload
|
|
|
|
case stateInPayload:
|
|
if b == EscapeByte {
|
|
p.state = stateEscapedPayload
|
|
return
|
|
}
|
|
if b == StartByte {
|
|
p.pushError("unexpected start byte")
|
|
return
|
|
}
|
|
if b == EndByte {
|
|
if p.checksum != 0 {
|
|
p.pushError("checksum mismatch")
|
|
return
|
|
}
|
|
p.emitFrame()
|
|
return
|
|
}
|
|
p.addByte(b)
|
|
|
|
case stateEscapedPayload:
|
|
p.addByte(b)
|
|
p.state = stateInPayload
|
|
}
|
|
}
|