Procházet zdrojové kódy

chore: refactor and move code for beacon logic

master
Blaz Smehov před 1 týdnem
rodič
revize
8e3125d3f8
3 změnil soubory, kde provedl 146 přidání a 131 odebrání
  1. +17
    -115
      cmd/decoder/main.go
  2. +129
    -0
      internal/pkg/common/utils/beacons.go
  3. +0
    -16
      internal/pkg/model/typeMethods.go

+ 17
- 115
cmd/decoder/main.go Zobrazit soubor

@@ -3,13 +3,12 @@ package main
import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"strings"

"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/common/utils"
"github.com/AFASystems/presence/internal/pkg/config"
"github.com/AFASystems/presence/internal/pkg/kafkaclient"
"github.com/AFASystems/presence/internal/pkg/model"
@@ -73,7 +72,7 @@ func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState,
beacon := strings.TrimSpace(adv.Data)
id := adv.MAC
if beacon == "" {
return nil // How to return error?, do I even need to return error
return nil
}

b, err := hex.DecodeString(beacon)
@@ -81,126 +80,29 @@ func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState,
return err
}

// check for flag byte, if first AD structure is flag bytes, remove it
if len(b) > 1 && b[1] == 0x01 {
l := int(b[0]) // length of AD structure
if 1+l <= len(b) {
b = b[1+l:]
}
}

adStructureIndeces := ParseADFast(b)
event := model.BeaconEvent{}
for _, r := range adStructureIndeces {
ad := b[r[0]:r[1]]
if checkIngics(ad) {
event = parseIngicsState(ad)
event.ID = id
event.Name = id
break
} else if checkEddystoneTLM(ad) {
event = parseEddystoneState(ad)
event.ID = id
event.Name = id
break
} else if checkMinewB7(ad) {
fmt.Println("Minew B7 vendor format")
break
}
}

if event.ID != "" {
prevEvent, ok := appState.GetBeaconEvent(id)
appState.UpdateBeaconEvent(id, event)
if ok && bytes.Equal(prevEvent.Hash(), event.Hash()) {
return nil
}

eMsg, err := json.Marshal(event)
if err != nil {
return err
}

err = writer.WriteMessages(context.Background(), kafka.Message{
Value: eMsg,
})

if err != nil {
return err
}

fmt.Println("Message sent")
}

return nil
}

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
}
b = utils.RemoveFlagBytes(b)

return false
}
indeces := utils.ParseADFast(b)
event := utils.LoopADStructures(b, indeces, id)

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

func checkEddystoneTLM(ad []byte) bool {
if len(ad) >= 4 &&
ad[1] == 0x16 &&
ad[2] == 0xAA &&
ad[3] == 0xFE &&
ad[4] == 0x20 {
return true
prevEvent, ok := appState.GetBeaconEvent(id)
appState.UpdateBeaconEvent(id, event)
if ok && bytes.Equal(prevEvent.Hash(), event.Hash()) {
return nil
}

return false
}

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

// I dont think this is always true, but for testing is ok
func checkMinewB7(ad []byte) bool {
if len(ad) >= 4 &&
ad[1] == 0x16 &&
ad[2] == 0xE1 &&
ad[3] == 0xFF {
return true
eMsg, err := event.ToJSON()
if err != nil {
return err
}

return false
}

func ParseADFast(b []byte) [][2]int {
var res [][2]int
i := 0

for i < len(b) {
l := int(b[i])
if l == 0 || i+1+l > len(b) {
break
}

res = append(res, [2]int{i, i + 1 + l})

i += 1 + l
if err := writer.WriteMessages(context.Background(), kafka.Message{Value: eMsg}); err != nil {
return err
}

return res
return nil
}

+ 129
- 0
internal/pkg/common/utils/beacons.go Zobrazit soubor

@@ -0,0 +1,129 @@
package utils

import (
"encoding/binary"
"fmt"

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

// ParseADFast efficiently parses Advertising Data structures
// Returns slice of [startIndex, endIndex] pairs for each AD structure
func ParseADFast(b []byte) [][2]int {
var res [][2]int
i := 0

for i < len(b) {
l := int(b[i])
if l == 0 || i+1+l > len(b) {
break
}

res = append(res, [2]int{i, i + 1 + l})

i += 1 + l
}

return res
}

// RemoveFlagBytes removes Bluetooth advertising flag bytes if present
// Some beacons include flag bytes as the first AD structure
func RemoveFlagBytes(b []byte) []byte {
if len(b) > 1 && b[1] == 0x01 {
l := int(b[0])
if 1+l <= len(b) {
return b[1+l:]
}
}
return b
}

// Generate event based on the Beacon type
func LoopADStructures(b []byte, i [][2]int, id string) 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 checkMinewB7(ad) {
fmt.Println("Minew B7 vendor format")
break
}
}

return be
}

// IsValidADStructure validates if an AD structure is well-formed
func isValidADStructure(data []byte) bool {
if len(data) < 2 {
return false
}

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",
}
}

// I dont think this is always true, but for testing is ok
func checkMinewB7(ad []byte) bool {
if len(ad) >= 4 &&
ad[1] == 0x16 &&
ad[2] == 0xE1 &&
ad[3] == 0xFF {
return true
}

return false
}

+ 0
- 16
internal/pkg/model/typeMethods.go Zobrazit soubor

@@ -1,16 +0,0 @@
package model

import (
"crypto/sha256"
"fmt"
)

func (b BeaconEvent) Hash() []byte {
rBatt := (b.Battery / 10) * 10
c := fmt.Sprintf("%d%d%s%s%s", rBatt, b.Event, b.ID, b.Name, b.Type)
h := sha256.New()
h.Write([]byte(c))

bs := h.Sum(nil)
return bs
}

Načítá se…
Zrušit
Uložit