package model import ( "bytes" "encoding/binary" "fmt" "sync" ) type ParserConfig struct { Length int `json:"length"` Offset int `json:"offset"` Order string `json:"order"` } type BeaconParser struct { CanParse func([]byte) bool configs map[string]ParserConfig } type ParserRegistry struct { ParserList map[string]BeaconParser rw sync.RWMutex } type Config struct { Name string `json:"name" gorm:"primaryKey"` Min int `json:"min"` Max int `json:"max"` Pattern []string `json:"pattern" gorm:"serializer:json"` Configs map[string]ParserConfig `json:"configs" gorm:"serializer:json"` } type KafkaParser struct { ID string Name string Config Config } func (pc ParserConfig) GetOrder() any { if pc.Order == "fixedpoint" { return "fixedpoint" } if pc.Order == "bigendian" { return binary.BigEndian } return binary.LittleEndian } func (p *ParserRegistry) Register(name string, c Config) { p.rw.Lock() defer p.rw.Unlock() b := BeaconParser{ CanParse: func(ad []byte) bool { if len(ad) < 2 { return false } return len(ad) >= c.Min && len(ad) <= c.Max && bytes.HasPrefix(ad[1:], c.GetPatternBytes()) }, configs: c.Configs, } p.ParserList[name] = b } func (p *ParserRegistry) Unregister(name string) { p.rw.Lock() delete(p.ParserList, name) p.rw.Unlock() } // TODO: change this to be dynamic, maybe event is interface with no predefined properties // or types func (b *BeaconParser) Parse(name string, ad []byte) (BeaconEvent, bool) { flag := false event := BeaconEvent{Type: name} if cfg, ok := b.configs["battery"]; ok { event.Battery = uint32(b.extract(ad, cfg).(uint16)) flag = true } if cfg, ok := b.configs["accX"]; ok { val := b.extract(ad, cfg).(float64) event.AccX = int16(val) flag = true } if cfg, ok := b.configs["accY"]; ok { val := b.extract(ad, cfg).(float64) event.AccY = int16(val) flag = true } if cfg, ok := b.configs["accZ"]; ok { val := b.extract(ad, cfg).(float64) event.AccZ = int16(val) flag = true } if cfg, ok := b.configs["temperature"]; ok { val := b.extract(ad, cfg).(float64) event.Temperature = uint16(val) flag = true } return event, flag } func (b *BeaconParser) extract(ad []byte, pc ParserConfig) any { if len(ad) < pc.Offset+pc.Length { return 0 } data := ad[pc.Offset : pc.Offset+pc.Length] if pc.Length == 1 { return uint16(data[0]) } order := pc.GetOrder() if order == "fixedpoint" { val := int16(data[0])<<8 | int16(data[1]) return float64(val) / 256.0 } o := order.(binary.ByteOrder) return o.Uint16(data) } func (c Config) GetPatternBytes() []byte { res := make([]byte, len(c.Pattern)) for i, s := range c.Pattern { fmt.Sscanf(s, "0x%02x", &res[i]) } return res }