Bladeren bron

feat: read decoding configuration from a file

master
Blaz Smehov 4 weken geleden
bovenliggende
commit
0b641d5471
4 gewijzigde bestanden met toevoegingen van 175 en 104 verwijderingen
  1. +37
    -0
      cmd/decoder/config.json
  2. +24
    -5
      cmd/decoder/main.go
  3. +10
    -99
      internal/pkg/common/utils/beacons.go
  4. +104
    -0
      internal/pkg/model/parser.go

+ 37
- 0
cmd/decoder/config.json Bestand weergeven

@@ -0,0 +1,37 @@
[
{
"name": "Ingics",
"min": 4,
"max": 255,
"pattern": ["0xFF", "0x59", "0x00", "0x80", "0xBC"],
"configs": {
"battery": {"offset": 6, "length": 2, "order": "littleendian"},
"event": {"offset": 8, "length": 1}
}
},
{
"name": "Eddystone",
"min": 4,
"max": 255,
"pattern": ["0x16", "0xAA", "0xFE", "0x20"],
"configs": {
"battery": {"offset": 6, "length": 2, "order": "bigendian"}
}
},
{
"name": "Minew B7",
"min": 4,
"max": 18,
"pattern": ["0x16", "0xE1", "0xFF"],
"configs": {
"battery": {"offset": 6, "length": 1}
}
},
{
"name": "Minew Acc",
"min": 19,
"max": 19,
"pattern": ["0x16", "0xE1", "0xFF"],
"configs": {}
}
]

+ 24
- 5
cmd/decoder/main.go Bestand weergeven

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
@@ -29,6 +30,24 @@ func main() {
appState := appcontext.NewAppState()
cfg := config.Load()

parserRegistry := model.ParserRegistry{
ParserList: make([]model.BeaconParser, 0),
}

configFile, err := os.Open("/app/cmd/decoder/config.json")
if err != nil {
panic(err)
}

b, _ := io.ReadAll(configFile)

var configs []model.Config
json.Unmarshal(b, &configs)

for _, config := range configs {
parserRegistry.Register(config.Name, config)
}

// Create log file
logFile, err := os.OpenFile("server.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
@@ -60,7 +79,7 @@ eventloop:
case <-ctx.Done():
break eventloop
case msg := <-chRaw:
processIncoming(msg, appState, alertWriter)
processIncoming(msg, appState, alertWriter, &parserRegistry)
}
}

@@ -72,8 +91,8 @@ eventloop:
appState.CleanKafkaWriters()
}

func processIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer) {
err := decodeBeacon(adv, appState, writer)
func processIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer, parserRegistry *model.ParserRegistry) {
err := decodeBeacon(adv, appState, writer, parserRegistry)
if err != nil {
eMsg := fmt.Sprintf("Error in decoding: %v", err)
fmt.Println(eMsg)
@@ -81,7 +100,7 @@ func processIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppStat
}
}

func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer) error {
func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer, parserRegistry *model.ParserRegistry) error {
beacon := strings.TrimSpace(adv.Data)
id := adv.ID
if beacon == "" {
@@ -96,7 +115,7 @@ func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState,
b = utils.RemoveFlagBytes(b)

indeces := utils.ParseADFast(b)
event := utils.LoopADStructures(b, indeces, id)
event := utils.LoopADStructures(b, indeces, id, parserRegistry)

if event.ID == "" {
return nil


+ 10
- 99
internal/pkg/common/utils/beacons.go Bestand weergeven

@@ -1,9 +1,6 @@
package utils

import (
"encoding/binary"
"fmt"

"github.com/AFASystems/presence/internal/pkg/model"
)

@@ -40,30 +37,22 @@ func RemoveFlagBytes(b []byte) []byte {
}

// Generate event based on the Beacon type
func LoopADStructures(b []byte, i [][2]int, id string) model.BeaconEvent {
func LoopADStructures(b []byte, i [][2]int, id string, parserRegistry *model.ParserRegistry) model.BeaconEvent {
be := model.BeaconEvent{}
for _, r := range i {
ad := b[r[0]:r[1]]
if !isValidADStructure(ad) {
break
}
if checkIngics(ad) {
be = parseIngicsState(ad)
be.ID = id
be.Name = id
break
} else if checkEddystoneTLM(ad) {
be = parseEddystoneState(ad)
be.ID = id
be.Name = id
break
} else if checkMinewDeviceInfo(ad) {
be = parseMinewDeviceInfo(ad)
be.ID = id
be.Name = id
break
} else if checkMinewAccData(ad) {
break
for _, parser := range parserRegistry.ParserList {
if parser.CanParse(ad) {
event, ok := parser.Parse(ad)
if ok {
event.ID = id
event.Name = id
return event
}
}
}
}

@@ -79,81 +68,3 @@ func isValidADStructure(data []byte) bool {
length := int(data[0])
return length > 0 && int(length)+1 <= len(data)
}

func checkIngics(ad []byte) bool {
if len(ad) >= 6 &&
ad[1] == 0xFF &&
ad[2] == 0x59 &&
ad[3] == 0x00 &&
ad[4] == 0x80 &&
ad[5] == 0xBC {
return true
}

return false
}

func parseIngicsState(ad []byte) model.BeaconEvent {
return model.BeaconEvent{
Battery: uint32(binary.LittleEndian.Uint16(ad[6:8])),
Event: int(ad[8]),
Type: "Ingics",
}
}

func checkEddystoneTLM(ad []byte) bool {
if len(ad) >= 4 &&
ad[1] == 0x16 &&
ad[2] == 0xAA &&
ad[3] == 0xFE &&
ad[4] == 0x20 {
return true
}

return false
}

func parseEddystoneState(ad []byte) model.BeaconEvent {
return model.BeaconEvent{
Battery: uint32(binary.BigEndian.Uint16(ad[6:8])),
Type: "Eddystone",
}
}

// Minew Battery level
func checkMinewDeviceInfo(ad []byte) bool {
if len(ad) >= 4 &&
len(ad) != 19 &&
ad[1] == 0x16 &&
ad[2] == 0xE1 &&
ad[3] == 0xFF {
fmt.Println("Minew device info")
return true
}

return false
}

func parseMinewDeviceInfo(ad []byte) model.BeaconEvent {
fmt.Printf("ad: %x\n", ad)
return model.BeaconEvent{
Battery: uint32(ad[6]),
Type: "Minew B7",
}
}

func checkMinewAccData(ad []byte) bool {
if len(ad) == 19 &&
ad[1] == 0x16 &&
ad[2] == 0xE1 &&
ad[3] == 0xFF {
fmt.Println("Minew Acc data")
return true
}

return false
}

// func parseMinewAccData(ad []byte) model.BeaconEvent {

// }

+ 104
- 0
internal/pkg/model/parser.go Bestand weergeven

@@ -0,0 +1,104 @@
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 {
Name string
CanParse func([]byte) bool
configs map[string]ParserConfig
}

type ParserRegistry struct {
ParserList []BeaconParser
rw sync.RWMutex
}

type Config struct {
Name string `json:"name"`
Min int `json:"min"`
Max int `json:"max"`
Pattern []string `json:"pattern"`
Configs map[string]ParserConfig `json:"configs"`
}

func (pc ParserConfig) GetOrder() binary.ByteOrder {
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{
Name: name,
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,
}

fmt.Printf("registered beacon parser: %+v\n", b)

p.ParserList = append(p.ParserList, b)
}

func (b *BeaconParser) Parse(ad []byte) (BeaconEvent, bool) {
flag := false
event := BeaconEvent{Type: b.Name}
if cfg, ok := b.configs["battery"]; ok {
event.Battery = uint32(b.extract(ad, cfg))
flag = true
}
if cfg, ok := b.configs["accX"]; ok {
event.AccX = int16(b.extract(ad, cfg))
flag = true
}
if cfg, ok := b.configs["accY"]; ok {
event.AccY = int16(b.extract(ad, cfg))
flag = true
}
if cfg, ok := b.configs["accZ"]; ok {
event.AccZ = int16(b.extract(ad, cfg))
flag = true
}
return event, flag
}

func (b *BeaconParser) extract(ad []byte, pc ParserConfig) uint16 {
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])
}

return pc.GetOrder().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
}

Laden…
Annuleren
Opslaan