Quellcode durchsuchen

chore: move relevant types to app context, create new health types

master
Blaz Smehov vor 1 Woche
Ursprung
Commit
7f10683483
23 geänderte Dateien mit 242 neuen und 170 gelöschten Zeilen
  1. BIN
      bridge
  2. BIN
      decoder
  3. +2
    -2
      internal/app/decoder/app.go
  4. +2
    -2
      internal/app/location/app.go
  5. +3
    -3
      internal/app/server/app.go
  6. +1
    -1
      internal/pkg/apiclient/updatedb.go
  7. +2
    -1
      internal/pkg/bridge/handler.go
  8. +20
    -21
      internal/pkg/common/appcontext/context.go
  9. +1
    -1
      internal/pkg/common/appcontext/type_methods.go
  10. +100
    -0
      internal/pkg/common/appcontext/types.go
  11. +3
    -2
      internal/pkg/common/utils/beacons.go
  12. +2
    -2
      internal/pkg/common/utils/distance.go
  13. +3
    -3
      internal/pkg/controller/settings_controller.go
  14. +2
    -1
      internal/pkg/database/database.go
  15. +2
    -2
      internal/pkg/decoder/process.go
  16. +20
    -0
      internal/pkg/kafkaclient/manager.go
  17. +4
    -5
      internal/pkg/location/assign.go
  18. +71
    -0
      internal/pkg/model/health.go
  19. +4
    -2
      internal/pkg/model/parser.go
  20. +0
    -13
      internal/pkg/model/settings.go
  21. +0
    -109
      internal/pkg/model/types.go
  22. BIN
      location
  23. BIN
      server

BIN
bridge Datei anzeigen


BIN
decoder Datei anzeigen


+ 2
- 2
internal/app/decoder/app.go Datei anzeigen

@@ -19,7 +19,7 @@ type DecoderApp struct {
KafkaManager *kafkaclient.KafkaManager KafkaManager *kafkaclient.KafkaManager
AppState *appcontext.AppState AppState *appcontext.AppState
ParserRegistry *model.ParserRegistry ParserRegistry *model.ParserRegistry
ChRaw chan model.BeaconAdvertisement
ChRaw chan appcontext.BeaconAdvertisement
ChParser chan model.KafkaParser ChParser chan model.KafkaParser
Cleanup func() Cleanup func()
wg sync.WaitGroup wg sync.WaitGroup
@@ -48,7 +48,7 @@ func New(cfg *config.Config) (*DecoderApp, error) {
KafkaManager: kafkaManager, KafkaManager: kafkaManager,
AppState: appState, AppState: appState,
ParserRegistry: registry, ParserRegistry: registry,
ChRaw: make(chan model.BeaconAdvertisement, config.LARGE_CHANNEL_SIZE),
ChRaw: make(chan appcontext.BeaconAdvertisement, config.LARGE_CHANNEL_SIZE),
ChParser: make(chan model.KafkaParser, config.SMALL_CHANNEL_SIZE), ChParser: make(chan model.KafkaParser, config.SMALL_CHANNEL_SIZE),
Cleanup: cleanup, Cleanup: cleanup,
}, nil }, nil


+ 2
- 2
internal/app/location/app.go Datei anzeigen

@@ -22,7 +22,7 @@ type LocationApp struct {
KafkaManager *kafkaclient.KafkaManager KafkaManager *kafkaclient.KafkaManager
AppState *appcontext.AppState AppState *appcontext.AppState
Inferencer pkglocation.Inferencer Inferencer pkglocation.Inferencer
ChRaw chan model.BeaconAdvertisement
ChRaw chan appcontext.BeaconAdvertisement
ChSettings chan map[string]any ChSettings chan map[string]any
Cleanup func() Cleanup func()
wg sync.WaitGroup wg sync.WaitGroup
@@ -47,7 +47,7 @@ func New(cfg *config.Config) (*LocationApp, error) {
KafkaManager: kafkaManager, KafkaManager: kafkaManager,
AppState: appState, AppState: appState,
Inferencer: pkglocation.NewDefaultInferencer(cfg.TLSInsecureSkipVerify), Inferencer: pkglocation.NewDefaultInferencer(cfg.TLSInsecureSkipVerify),
ChRaw: make(chan model.BeaconAdvertisement, config.LARGE_CHANNEL_SIZE),
ChRaw: make(chan appcontext.BeaconAdvertisement, config.LARGE_CHANNEL_SIZE),
ChSettings: make(chan map[string]any, config.SMALL_CHANNEL_SIZE), ChSettings: make(chan map[string]any, config.SMALL_CHANNEL_SIZE),
Cleanup: cleanup, Cleanup: cleanup,
}, nil }, nil


+ 3
- 3
internal/app/server/app.go Datei anzeigen

@@ -28,7 +28,7 @@ type ServerApp struct {
KafkaManager *kafkaclient.KafkaManager KafkaManager *kafkaclient.KafkaManager
AppState *appcontext.AppState AppState *appcontext.AppState
ChLoc chan model.HTTPLocation ChLoc chan model.HTTPLocation
ChEvents chan model.BeaconEvent
ChEvents chan appcontext.BeaconEvent
ctx context.Context ctx context.Context
Server *http.Server Server *http.Server
Cleanup func() Cleanup func()
@@ -98,12 +98,12 @@ func (a *ServerApp) Init(ctx context.Context) error {
slog.Error("UpdateDB", "err", err) slog.Error("UpdateDB", "err", err)
} }


readerTopics := []string{"locevents", "alertbeacons"}
readerTopics := []string{"locevents", "alertbeacons", "health"}
a.KafkaManager.PopulateKafkaManager(a.Cfg.KafkaURL, "server", readerTopics) a.KafkaManager.PopulateKafkaManager(a.Cfg.KafkaURL, "server", readerTopics)
slog.Info("Kafka readers initialized", "topics", readerTopics) slog.Info("Kafka readers initialized", "topics", readerTopics)


a.ChLoc = make(chan model.HTTPLocation, config.SMALL_CHANNEL_SIZE) a.ChLoc = make(chan model.HTTPLocation, config.SMALL_CHANNEL_SIZE)
a.ChEvents = make(chan model.BeaconEvent, config.MEDIUM_CHANNEL_SIZE)
a.ChEvents = make(chan appcontext.BeaconEvent, config.MEDIUM_CHANNEL_SIZE)


a.wg.Add(2) a.wg.Add(2)
go kafkaclient.Consume(a.KafkaManager.GetReader("locevents"), a.ChLoc, ctx, &a.wg) go kafkaclient.Consume(a.KafkaManager.GetReader("locevents"), a.ChLoc, ctx, &a.wg)


+ 1
- 1
internal/pkg/apiclient/updatedb.go Datei anzeigen

@@ -70,7 +70,7 @@ func UpdateDB(db *gorm.DB, ctx context.Context, cfg *config.Config, writer *kafk
} }
} }


var settings model.Settings
var settings appcontext.Settings
db.First(&settings) db.First(&settings)
if settings.ID == 0 { if settings.ID == 0 {
msg := "settings are empty" msg := "settings are empty"


+ 2
- 1
internal/pkg/bridge/handler.go Datei anzeigen

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"time" "time"


"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/kafkaclient" "github.com/AFASystems/presence/internal/pkg/kafkaclient"
"github.com/AFASystems/presence/internal/pkg/model" "github.com/AFASystems/presence/internal/pkg/model"
"github.com/segmentio/kafka-go" "github.com/segmentio/kafka-go"
@@ -42,7 +43,7 @@ func HandleMQTTMessage(topic string, payload []byte, lookup BeaconLookup, writer
if !ok { if !ok {
continue continue
} }
adv := model.BeaconAdvertisement{
adv := appcontext.BeaconAdvertisement{
ID: id, ID: id,
Hostname: hostname, Hostname: hostname,
MAC: reading.MAC, MAC: reading.MAC,


+ 20
- 21
internal/pkg/common/appcontext/context.go Datei anzeigen

@@ -5,16 +5,15 @@ import (
"log/slog" "log/slog"
"os" "os"


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


// AppState provides centralized access to application state // AppState provides centralized access to application state
type AppState struct { type AppState struct {
beacons model.BeaconsList
settings model.Settings
beaconEvents model.BeaconEventList
beaconsLookup model.BeaconsLookup
beacons BeaconsList
settings Settings
beaconEvents BeaconEventList
beaconsLookup BeaconsLookup
} }


func getEnv(key, def string) string { func getEnv(key, def string) string {
@@ -27,10 +26,10 @@ func getEnv(key, def string) string {
// NewAppState creates a new application context AppState with default values // NewAppState creates a new application context AppState with default values
func NewAppState() *AppState { func NewAppState() *AppState {
return &AppState{ return &AppState{
beacons: model.BeaconsList{
Beacons: make(map[string]model.Beacon),
beacons: BeaconsList{
Beacons: make(map[string]Beacon),
}, },
settings: model.Settings{
settings: Settings{
ID: 1, ID: 1,
CurrentAlgorithm: getEnv("ALGORITHM", "filter"), CurrentAlgorithm: getEnv("ALGORITHM", "filter"),
LocationConfidence: 4, LocationConfidence: 4,
@@ -41,29 +40,29 @@ func NewAppState() *AppState {
RSSIEnforceThreshold: false, RSSIEnforceThreshold: false,
RSSIMinThreshold: 100, RSSIMinThreshold: 100,
}, },
beaconEvents: model.BeaconEventList{
Beacons: make(map[string]model.BeaconEvent),
beaconEvents: BeaconEventList{
Beacons: make(map[string]BeaconEvent),
}, },
beaconsLookup: model.BeaconsLookup{
beaconsLookup: BeaconsLookup{
Lookup: make(map[string]string), Lookup: make(map[string]string),
}, },
} }
} }


// GetBeacons returns thread-safe access to beacons list // GetBeacons returns thread-safe access to beacons list
func (m *AppState) GetBeacons() *model.BeaconsList {
func (m *AppState) GetBeacons() *BeaconsList {
m.beacons.Lock.RLock() m.beacons.Lock.RLock()
defer m.beacons.Lock.RUnlock() defer m.beacons.Lock.RUnlock()
return &m.beacons return &m.beacons
} }


// GetSettings returns thread-safe access to settings // GetSettings returns thread-safe access to settings
func (m *AppState) GetSettings() *model.Settings {
func (m *AppState) GetSettings() *Settings {
return &m.settings return &m.settings
} }


// GetBeaconEvents returns thread-safe access to beacon events // GetBeaconEvents returns thread-safe access to beacon events
func (m *AppState) GetBeaconEvents() *model.BeaconEventList {
func (m *AppState) GetBeaconEvents() *BeaconEventList {
m.beaconEvents.Lock.RLock() m.beaconEvents.Lock.RLock()
defer m.beaconEvents.Lock.RUnlock() defer m.beaconEvents.Lock.RUnlock()
return &m.beaconEvents return &m.beaconEvents
@@ -98,7 +97,7 @@ func (m *AppState) BeaconExists(id string) (string, bool) {
} }


// GetBeacon returns a beacon by ID (thread-safe) // GetBeacon returns a beacon by ID (thread-safe)
func (m *AppState) GetBeacon(id string) (model.Beacon, bool) {
func (m *AppState) GetBeacon(id string) (Beacon, bool) {
m.beacons.Lock.RLock() m.beacons.Lock.RLock()
defer m.beacons.Lock.RUnlock() defer m.beacons.Lock.RUnlock()


@@ -107,7 +106,7 @@ func (m *AppState) GetBeacon(id string) (model.Beacon, bool) {
} }


// UpdateBeacon updates a beacon in the list (thread-safe) // UpdateBeacon updates a beacon in the list (thread-safe)
func (m *AppState) UpdateBeacon(id string, beacon model.Beacon) {
func (m *AppState) UpdateBeacon(id string, beacon Beacon) {
m.beacons.Lock.Lock() m.beacons.Lock.Lock()
defer m.beacons.Lock.Unlock() defer m.beacons.Lock.Unlock()


@@ -115,7 +114,7 @@ func (m *AppState) UpdateBeacon(id string, beacon model.Beacon) {
} }


// GetBeaconEvent returns a beacon event by ID (thread-safe) // GetBeaconEvent returns a beacon event by ID (thread-safe)
func (m *AppState) GetBeaconEvent(id string) (model.BeaconEvent, bool) {
func (m *AppState) GetBeaconEvent(id string) (BeaconEvent, bool) {
m.beaconEvents.Lock.RLock() m.beaconEvents.Lock.RLock()
defer m.beaconEvents.Lock.RUnlock() defer m.beaconEvents.Lock.RUnlock()


@@ -124,7 +123,7 @@ func (m *AppState) GetBeaconEvent(id string) (model.BeaconEvent, bool) {
} }


// UpdateBeaconEvent updates a beacon event in the list (thread-safe) // UpdateBeaconEvent updates a beacon event in the list (thread-safe)
func (m *AppState) UpdateBeaconEvent(id string, event model.BeaconEvent) {
func (m *AppState) UpdateBeaconEvent(id string, event BeaconEvent) {
m.beaconEvents.Lock.Lock() m.beaconEvents.Lock.Lock()
defer m.beaconEvents.Lock.Unlock() defer m.beaconEvents.Lock.Unlock()


@@ -132,11 +131,11 @@ func (m *AppState) UpdateBeaconEvent(id string, event model.BeaconEvent) {
} }


// GetAllBeacons returns a copy of all beacons // GetAllBeacons returns a copy of all beacons
func (m *AppState) GetAllBeacons() map[string]model.Beacon {
func (m *AppState) GetAllBeacons() map[string]Beacon {
m.beacons.Lock.RLock() m.beacons.Lock.RLock()
defer m.beacons.Lock.RUnlock() defer m.beacons.Lock.RUnlock()


beacons := make(map[string]model.Beacon)
beacons := make(map[string]Beacon)
for id, beacon := range m.beacons.Beacons { for id, beacon := range m.beacons.Beacons {
beacons[id] = beacon beacons[id] = beacon
} }
@@ -152,7 +151,7 @@ func (m *AppState) GetBeaconCount() int {
} }


// GetSettingsValue returns current settings as a value // GetSettingsValue returns current settings as a value
func (m *AppState) GetSettingsValue() model.Settings {
func (m *AppState) GetSettingsValue() Settings {
return m.settings return m.settings
} }




internal/pkg/model/type_methods.go → internal/pkg/common/appcontext/type_methods.go Datei anzeigen

@@ -1,4 +1,4 @@
package model
package appcontext


import ( import (
"crypto/sha256" "crypto/sha256"

+ 100
- 0
internal/pkg/common/appcontext/types.go Datei anzeigen

@@ -0,0 +1,100 @@
package appcontext

import "sync"

// BeaconAdvertisement represents the JSON payload received from beacon advertisements.
type BeaconAdvertisement struct {
ID string
Hostname string `json:"hostname"`
MAC string `json:"mac"`
RSSI int64 `json:"rssi"`
ScanResponse string `json:"is_scan_response"`
Type string `json:"type"`
Data string `json:"data"`
BeaconType string `json:"beacon_type"`
UUID string `json:"uuid"`
Major string `json:"major"`
Minor string `json:"minor"`
TXPower string `json:"tx_power"`
NamespaceID string `json:"namespace"`
InstanceID string `json:"instance_id"`
HSButtonCounter int64 `json:"hb_button_counter"`
HSButtonPrev int64 `json:"hb_button_counter_prev"`
HSBatteryLevel int64 `json:"hb_button_battery"`
HSRandomNonce string `json:"hb_button_random"`
HSButtonMode string `json:"hb_button_mode"`
}

// BeaconMetric stores signal and distance data for a beacon.
type BeaconMetric struct {
Location string
Distance float64
RSSI int64
Timestamp int64
}

// Beacon holds all relevant information about a tracked beacon device.
type Beacon struct {
Name string `json:"name"`
ID string `json:"beacon_id"`
BeaconType string `json:"beacon_type"`
BeaconLocation string `json:"beacon_location"`
LastSeen int64 `json:"last_seen"`
IncomingJSON BeaconAdvertisement `json:"incoming_json"`
Distance float64 `json:"distance"`
PreviousLocation string
PreviousConfidentLocation string
ExpiredLocation string
LocationConfidence int64
LocationHistory []string
BeaconMetrics []BeaconMetric
Location string `json:"location"`
HSButtonCounter int64 `json:"hs_button_counter"`
HSButtonPrev int64 `json:"hs_button_counter_prev"`
HSBattery int64 `json:"hs_button_battery"`
HSRandomNonce string `json:"hs_button_random"`
HSButtonMode string `json:"hs_button_mode"`
Event int `json:"beacon_event"`
}

type Settings struct {
ID int `gorm:"primaryKey"` // this is always 1
CurrentAlgorithm string `json:"current_algorithm" mapstructure:"current_algorithm"`
LocationConfidence int64 `json:"location_confidence" mapstructure:"location_confidence"`
LastSeenThreshold int64 `json:"last_seen_threshold" mapstructure:"last_seen_threshold"`
BeaconMetricSize int `json:"beacon_metric_size" mapstructure:"beacon_metric_size"`
HASendInterval int `json:"HA_send_interval" mapstructure:"HA_send_interval"`
HASendChangesOnly bool `json:"HA_send_changes_only" mapstructure:"HA_send_changes_only"`
RSSIEnforceThreshold bool `json:"RSSI_enforce_threshold" mapstructure:"RSSI_enforce_threshold"`
RSSIMinThreshold int64 `json:"RSSI_min_threshold" mapstructure:"RSSI_min_threshold"`
}

type BeaconEvent struct {
Name string
ID string
Type string
Battery uint32
Event int
AccX int16
AccY int16
AccZ int16
Temperature uint16
Heart int16
BtnPressed bool
}

// BeaconsList holds all known beacons and their synchronization lock.
type BeaconsList struct {
Beacons map[string]Beacon `json:"beacons"`
Lock sync.RWMutex
}

type BeaconEventList struct {
Beacons map[string]BeaconEvent
Lock sync.RWMutex
}

type BeaconsLookup struct {
Lookup map[string]string
Lock sync.RWMutex
}

+ 3
- 2
internal/pkg/common/utils/beacons.go Datei anzeigen

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


import ( import (
"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/model" "github.com/AFASystems/presence/internal/pkg/model"
) )


@@ -37,8 +38,8 @@ func RemoveFlagBytes(b []byte) []byte {
} }


// Generate event based on the Beacon type // Generate event based on the Beacon type
func LoopADStructures(b []byte, i [][2]int, id string, parserRegistry *model.ParserRegistry) model.BeaconEvent {
be := model.BeaconEvent{}
func LoopADStructures(b []byte, i [][2]int, id string, parserRegistry *model.ParserRegistry) appcontext.BeaconEvent {
be := appcontext.BeaconEvent{}
for _, r := range i { for _, r := range i {
ad := b[r[0]:r[1]] ad := b[r[0]:r[1]]
if !isValidADStructure(ad) { if !isValidADStructure(ad) {


+ 2
- 2
internal/pkg/common/utils/distance.go Datei anzeigen

@@ -4,10 +4,10 @@ import (
"math" "math"
"strconv" "strconv"


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


func CalculateDistance(adv model.BeaconAdvertisement) float64 {
func CalculateDistance(adv appcontext.BeaconAdvertisement) float64 {
rssi := adv.RSSI rssi := adv.RSSI
power := adv.TXPower power := adv.TXPower
ratio := float64(rssi) * (1.0 / float64(twosComp(power))) ratio := float64(rssi) * (1.0 / float64(twosComp(power)))


+ 3
- 3
internal/pkg/controller/settings_controller.go Datei anzeigen

@@ -7,15 +7,15 @@ import (
"net/http" "net/http"


"github.com/AFASystems/presence/internal/pkg/api/response" "github.com/AFASystems/presence/internal/pkg/api/response"
"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/kafkaclient" "github.com/AFASystems/presence/internal/pkg/kafkaclient"
"github.com/AFASystems/presence/internal/pkg/model"
"github.com/segmentio/kafka-go" "github.com/segmentio/kafka-go"
"gorm.io/gorm" "gorm.io/gorm"
) )


func SettingsListController(db *gorm.DB, context context.Context) http.HandlerFunc { func SettingsListController(db *gorm.DB, context context.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var settings []model.Settings
var settings []appcontext.Settings
if err := db.WithContext(context).Find(&settings).Error; err != nil { if err := db.WithContext(context).Find(&settings).Error; err != nil {
response.InternalError(w, "failed to list settings", err) response.InternalError(w, "failed to list settings", err)
return return
@@ -34,7 +34,7 @@ func SettingsUpdateController(db *gorm.DB, writer *kafka.Writer, context context


slog.Info("updating settings", "updates", updates) slog.Info("updating settings", "updates", updates)


if err := db.WithContext(context).Model(&model.Settings{}).Where("id = ?", 1).Updates(updates).Error; err != nil {
if err := db.WithContext(context).Model(&appcontext.Settings{}).Where("id = ?", 1).Updates(updates).Error; err != nil {
response.InternalError(w, "failed to update settings", err) response.InternalError(w, "failed to update settings", err)
return return
} }


+ 2
- 1
internal/pkg/database/database.go Datei anzeigen

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"


"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/config" "github.com/AFASystems/presence/internal/pkg/config"
"github.com/AFASystems/presence/internal/pkg/model" "github.com/AFASystems/presence/internal/pkg/model"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
@@ -25,7 +26,7 @@ func Connect(cfg *config.Config) (*gorm.DB, error) {
return nil, err return nil, err
} }


if err := db.AutoMigrate(&model.Gateway{}, model.Zone{}, model.TrackerZones{}, model.Tracker{}, model.Config{}, model.Settings{}, model.Tracks{}, &model.Alert{}); err != nil {
if err := db.AutoMigrate(&model.Gateway{}, model.Zone{}, model.TrackerZones{}, model.Tracker{}, model.Config{}, appcontext.Settings{}, model.Tracks{}, &model.Alert{}); err != nil {
return nil, err return nil, err
} }




+ 2
- 2
internal/pkg/decoder/process.go Datei anzeigen

@@ -16,14 +16,14 @@ import (
) )


// ProcessIncoming decodes a beacon advertisement and writes the event to the writer if it changed. // ProcessIncoming decodes a beacon advertisement and writes the event to the writer if it changed.
func ProcessIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer, registry *model.ParserRegistry) {
func ProcessIncoming(adv appcontext.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer, registry *model.ParserRegistry) {
if err := DecodeBeacon(adv, appState, writer, registry); err != nil { if err := DecodeBeacon(adv, appState, writer, registry); err != nil {
slog.Error("decoding beacon", "err", err, "id", adv.ID) slog.Error("decoding beacon", "err", err, "id", adv.ID)
} }
} }


// DecodeBeacon hex-decodes the payload, runs the parser registry, dedupes by event hash, and writes to writer. // DecodeBeacon hex-decodes the payload, runs the parser registry, dedupes by event hash, and writes to writer.
func DecodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer, registry *model.ParserRegistry) error {
func DecodeBeacon(adv appcontext.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer, registry *model.ParserRegistry) error {
beacon := strings.TrimSpace(adv.Data) beacon := strings.TrimSpace(adv.Data)
id := adv.ID id := adv.ID
if beacon == "" { if beacon == "" {


+ 20
- 0
internal/pkg/kafkaclient/manager.go Datei anzeigen

@@ -121,3 +121,23 @@ func (m *KafkaManager) GetWriter(topic string) *kafka.Writer {
defer m.kafkaWritersMap.KafkaWritersLock.RUnlock() defer m.kafkaWritersMap.KafkaWritersLock.RUnlock()
return m.kafkaWritersMap.KafkaWriters[topic] return m.kafkaWritersMap.KafkaWriters[topic]
} }

func (m *KafkaManager) GetReaders() []string {
m.kafkaReadersMap.KafkaReadersLock.RLock()
var readers []string
for key := range m.kafkaReadersMap.KafkaReaders {
readers = append(readers, key)
}
m.kafkaReadersMap.KafkaReadersLock.RUnlock()
return readers
}

func (m *KafkaManager) GetWriters() []string {
m.kafkaWritersMap.KafkaWritersLock.RLock()
var writers []string
for key := range m.kafkaWritersMap.KafkaWriters {
writers = append(writers, key)
}
m.kafkaWritersMap.KafkaWritersLock.RUnlock()
return writers
}

+ 4
- 5
internal/pkg/location/assign.go Datei anzeigen

@@ -6,12 +6,11 @@ import (


"github.com/AFASystems/presence/internal/pkg/common/appcontext" "github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/common/utils" "github.com/AFASystems/presence/internal/pkg/common/utils"
"github.com/AFASystems/presence/internal/pkg/model"
) )


// AssignBeaconToList updates app state with a new beacon advertisement: appends a metric // AssignBeaconToList updates app state with a new beacon advertisement: appends a metric
// to the beacon's sliding window and updates last seen. // to the beacon's sliding window and updates last seen.
func AssignBeaconToList(adv model.BeaconAdvertisement, appState *appcontext.AppState) {
func AssignBeaconToList(adv appcontext.BeaconAdvertisement, appState *appcontext.AppState) {
id := adv.ID id := adv.ID
now := time.Now().Unix() now := time.Now().Unix()
settings := appState.GetSettingsValue() settings := appState.GetSettingsValue()
@@ -23,17 +22,17 @@ func AssignBeaconToList(adv model.BeaconAdvertisement, appState *appcontext.AppS


beacon, ok := appState.GetBeacon(id) beacon, ok := appState.GetBeacon(id)
if !ok { if !ok {
beacon = model.Beacon{ID: id}
beacon = appcontext.Beacon{ID: id}
} }


beacon.IncomingJSON = adv beacon.IncomingJSON = adv
beacon.LastSeen = now beacon.LastSeen = now


if beacon.BeaconMetrics == nil { if beacon.BeaconMetrics == nil {
beacon.BeaconMetrics = make([]model.BeaconMetric, 0, settings.BeaconMetricSize)
beacon.BeaconMetrics = make([]appcontext.BeaconMetric, 0, settings.BeaconMetricSize)
} }


metric := model.BeaconMetric{
metric := appcontext.BeaconMetric{
Distance: utils.CalculateDistance(adv), Distance: utils.CalculateDistance(adv),
Timestamp: now, Timestamp: now,
RSSI: int64(adv.RSSI), RSSI: int64(adv.RSSI),


+ 71
- 0
internal/pkg/model/health.go Datei anzeigen

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

import (
"encoding/json"
"sync"
"time"

"github.com/AFASystems/presence/internal/pkg/common/appcontext"
"github.com/AFASystems/presence/internal/pkg/kafkaclient"
)

type BaseHealth struct {
Lock sync.RWMutex
Uptime time.Duration `json:"uptime"`
ActiveReaders []string `json:"activeReaders"`
ActiveWriters []string `json:"activeWriters"`
ActiveBeacons []string `json:"activeBeacons"`
}

type DecoderHealth struct {
BaseHealth
// current active configs ? dont know yet how
}

type LocationHealth struct {
BaseHealth
ActiveSettings []appcontext.Settings `json:"activeSettings"`
}

type BridgeHealth struct {
BaseHealth
}

func (b *BaseHealth) GetUptime(startTime time.Time) {
b.Lock.Lock()
b.Uptime = time.Since(startTime)
b.Lock.Unlock()
}

func (b *BaseHealth) GetActiveReaders(m *kafkaclient.KafkaManager) {
b.Lock.Lock()
b.ActiveReaders = m.GetReaders()
b.Lock.Unlock()
}

func (b *BaseHealth) GetActiveWriters(m *kafkaclient.KafkaManager) {
b.Lock.Lock()
b.ActiveWriters = m.GetWriters()
b.Lock.Unlock()
}

func (b *BaseHealth) GetActiveBeacons(m *appcontext.AppState) {
b.Lock.Lock()
beacons := m.GetAllBeacons()
for beacon := range beacons {
b.ActiveBeacons = append(b.ActiveBeacons, beacon)
}
b.Lock.Unlock()
}

func (d *DecoderHealth) Marshal() ([]byte, error) {
return json.Marshal(d)
}

func (l *LocationHealth) Marshal() ([]byte, error) {
return json.Marshal(l)
}

func (b *BridgeHealth) Marshal() ([]byte, error) {
return json.Marshal(b)
}

+ 4
- 2
internal/pkg/model/parser.go Datei anzeigen

@@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"sync" "sync"

"github.com/AFASystems/presence/internal/pkg/common/appcontext"
) )


type ParserConfig struct { type ParserConfig struct {
@@ -77,9 +79,9 @@ func (p *ParserRegistry) Unregister(name string) {
// TODO: change this to be dynamic, maybe event is interface with no predefined properties // TODO: change this to be dynamic, maybe event is interface with no predefined properties
// or types // or types


func (b *BeaconParser) Parse(name string, ad []byte) (BeaconEvent, bool) {
func (b *BeaconParser) Parse(name string, ad []byte) (appcontext.BeaconEvent, bool) {
flag := false flag := false
event := BeaconEvent{Type: name}
event := appcontext.BeaconEvent{Type: name}
if cfg, ok := b.configs["battery"]; ok { if cfg, ok := b.configs["battery"]; ok {
event.Battery = uint32(b.extract(ad, cfg).(uint16)) event.Battery = uint32(b.extract(ad, cfg).(uint16))
flag = true flag = true


+ 0
- 13
internal/pkg/model/settings.go Datei anzeigen

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

type Settings struct {
ID int `gorm:"primaryKey"` // this is always 1
CurrentAlgorithm string `json:"current_algorithm" mapstructure:"current_algorithm"`
LocationConfidence int64 `json:"location_confidence" mapstructure:"location_confidence"`
LastSeenThreshold int64 `json:"last_seen_threshold" mapstructure:"last_seen_threshold"`
BeaconMetricSize int `json:"beacon_metric_size" mapstructure:"beacon_metric_size"`
HASendInterval int `json:"HA_send_interval" mapstructure:"HA_send_interval"`
HASendChangesOnly bool `json:"HA_send_changes_only" mapstructure:"HA_send_changes_only"`
RSSIEnforceThreshold bool `json:"RSSI_enforce_threshold" mapstructure:"RSSI_enforce_threshold"`
RSSIMinThreshold int64 `json:"RSSI_min_threshold" mapstructure:"RSSI_min_threshold"`
}

+ 0
- 109
internal/pkg/model/types.go Datei anzeigen

@@ -1,40 +1,5 @@
package model package model


import (
"sync"
)

// BeaconAdvertisement represents the JSON payload received from beacon advertisements.
type BeaconAdvertisement struct {
ID string
Hostname string `json:"hostname"`
MAC string `json:"mac"`
RSSI int64 `json:"rssi"`
ScanResponse string `json:"is_scan_response"`
Type string `json:"type"`
Data string `json:"data"`
BeaconType string `json:"beacon_type"`
UUID string `json:"uuid"`
Major string `json:"major"`
Minor string `json:"minor"`
TXPower string `json:"tx_power"`
NamespaceID string `json:"namespace"`
InstanceID string `json:"instance_id"`
HSButtonCounter int64 `json:"hb_button_counter"`
HSButtonPrev int64 `json:"hb_button_counter_prev"`
HSBatteryLevel int64 `json:"hb_button_battery"`
HSRandomNonce string `json:"hb_button_random"`
HSButtonMode string `json:"hb_button_mode"`
}

// BeaconMetric stores signal and distance data for a beacon.
type BeaconMetric struct {
Location string
Distance float64
RSSI int64
Timestamp int64
}

// HTTPLocation describes a beacon's state as served over HTTP. // HTTPLocation describes a beacon's state as served over HTTP.
type HTTPLocation struct { type HTTPLocation struct {
Method string `json:"method"` Method string `json:"method"`
@@ -50,80 +15,6 @@ type HTTPLocation struct {
MAC string `json:"mac"` MAC string `json:"mac"`
} }


// Beacon holds all relevant information about a tracked beacon device.
type Beacon struct {
Name string `json:"name"`
ID string `json:"beacon_id"`
BeaconType string `json:"beacon_type"`
BeaconLocation string `json:"beacon_location"`
LastSeen int64 `json:"last_seen"`
IncomingJSON BeaconAdvertisement `json:"incoming_json"`
Distance float64 `json:"distance"`
PreviousLocation string
PreviousConfidentLocation string
ExpiredLocation string
LocationConfidence int64
LocationHistory []string
BeaconMetrics []BeaconMetric
Location string `json:"location"`
HSButtonCounter int64 `json:"hs_button_counter"`
HSButtonPrev int64 `json:"hs_button_counter_prev"`
HSBattery int64 `json:"hs_button_battery"`
HSRandomNonce string `json:"hs_button_random"`
HSButtonMode string `json:"hs_button_mode"`
Event int `json:"beacon_event"`
}

type BeaconEvent struct {
Name string
ID string
Type string
Battery uint32
Event int
AccX int16
AccY int16
AccZ int16
Temperature uint16
Heart int16
BtnPressed bool
}

type HTTPResult struct {
BeaconId string `json:"id"`
Name string `json:"name"`
ID string `json:"MAC"`
Status string `json:"status"`
Model string `json:"model"`
Position string `json:"position"`
Notes string `json:"notes"`
X float32 `json:"x"`
Y float32 `json:"y"`
Zone string `json:"zone"`
Building string `json:"building"`
BeaconType string `json:"type"`
Battery int64 `json:"battery"`
Event int `json:"event"`
Distance float64 `json:"distance"`
LastSeen int64 `json:"timestamp"`
PreviousConfidentLocation string `json:"previous_confident_location"`
}

// BeaconsList holds all known beacons and their synchronization lock.
type BeaconsList struct {
Beacons map[string]Beacon `json:"beacons"`
Lock sync.RWMutex
}

type BeaconEventList struct {
Beacons map[string]BeaconEvent
Lock sync.RWMutex
}

type BeaconsLookup struct {
Lookup map[string]string
Lock sync.RWMutex
}

// RawReading represents an incoming raw sensor reading. // RawReading represents an incoming raw sensor reading.
type RawReading struct { type RawReading struct {
Timestamp string `json:"timestamp"` Timestamp string `json:"timestamp"`


BIN
location Datei anzeigen


BIN
server Datei anzeigen


Laden…
Abbrechen
Speichern