| @@ -19,7 +19,7 @@ type DecoderApp struct { | |||
| KafkaManager *kafkaclient.KafkaManager | |||
| AppState *appcontext.AppState | |||
| ParserRegistry *model.ParserRegistry | |||
| ChRaw chan model.BeaconAdvertisement | |||
| ChRaw chan appcontext.BeaconAdvertisement | |||
| ChParser chan model.KafkaParser | |||
| Cleanup func() | |||
| wg sync.WaitGroup | |||
| @@ -48,7 +48,7 @@ func New(cfg *config.Config) (*DecoderApp, error) { | |||
| KafkaManager: kafkaManager, | |||
| AppState: appState, | |||
| 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), | |||
| Cleanup: cleanup, | |||
| }, nil | |||
| @@ -22,7 +22,7 @@ type LocationApp struct { | |||
| KafkaManager *kafkaclient.KafkaManager | |||
| AppState *appcontext.AppState | |||
| Inferencer pkglocation.Inferencer | |||
| ChRaw chan model.BeaconAdvertisement | |||
| ChRaw chan appcontext.BeaconAdvertisement | |||
| ChSettings chan map[string]any | |||
| Cleanup func() | |||
| wg sync.WaitGroup | |||
| @@ -47,7 +47,7 @@ func New(cfg *config.Config) (*LocationApp, error) { | |||
| KafkaManager: kafkaManager, | |||
| AppState: appState, | |||
| 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), | |||
| Cleanup: cleanup, | |||
| }, nil | |||
| @@ -28,7 +28,7 @@ type ServerApp struct { | |||
| KafkaManager *kafkaclient.KafkaManager | |||
| AppState *appcontext.AppState | |||
| ChLoc chan model.HTTPLocation | |||
| ChEvents chan model.BeaconEvent | |||
| ChEvents chan appcontext.BeaconEvent | |||
| ctx context.Context | |||
| Server *http.Server | |||
| Cleanup func() | |||
| @@ -98,12 +98,12 @@ func (a *ServerApp) Init(ctx context.Context) error { | |||
| slog.Error("UpdateDB", "err", err) | |||
| } | |||
| readerTopics := []string{"locevents", "alertbeacons"} | |||
| readerTopics := []string{"locevents", "alertbeacons", "health"} | |||
| a.KafkaManager.PopulateKafkaManager(a.Cfg.KafkaURL, "server", readerTopics) | |||
| slog.Info("Kafka readers initialized", "topics", readerTopics) | |||
| 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) | |||
| go kafkaclient.Consume(a.KafkaManager.GetReader("locevents"), a.ChLoc, ctx, &a.wg) | |||
| @@ -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) | |||
| if settings.ID == 0 { | |||
| msg := "settings are empty" | |||
| @@ -7,6 +7,7 @@ import ( | |||
| "strings" | |||
| "time" | |||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | |||
| "github.com/AFASystems/presence/internal/pkg/kafkaclient" | |||
| "github.com/AFASystems/presence/internal/pkg/model" | |||
| "github.com/segmentio/kafka-go" | |||
| @@ -42,7 +43,7 @@ func HandleMQTTMessage(topic string, payload []byte, lookup BeaconLookup, writer | |||
| if !ok { | |||
| continue | |||
| } | |||
| adv := model.BeaconAdvertisement{ | |||
| adv := appcontext.BeaconAdvertisement{ | |||
| ID: id, | |||
| Hostname: hostname, | |||
| MAC: reading.MAC, | |||
| @@ -5,16 +5,15 @@ import ( | |||
| "log/slog" | |||
| "os" | |||
| "github.com/AFASystems/presence/internal/pkg/model" | |||
| "github.com/mitchellh/mapstructure" | |||
| ) | |||
| // AppState provides centralized access to application state | |||
| 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 { | |||
| @@ -27,10 +26,10 @@ func getEnv(key, def string) string { | |||
| // NewAppState creates a new application context AppState with default values | |||
| func NewAppState() *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, | |||
| CurrentAlgorithm: getEnv("ALGORITHM", "filter"), | |||
| LocationConfidence: 4, | |||
| @@ -41,29 +40,29 @@ func NewAppState() *AppState { | |||
| RSSIEnforceThreshold: false, | |||
| 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), | |||
| }, | |||
| } | |||
| } | |||
| // GetBeacons returns thread-safe access to beacons list | |||
| func (m *AppState) GetBeacons() *model.BeaconsList { | |||
| func (m *AppState) GetBeacons() *BeaconsList { | |||
| m.beacons.Lock.RLock() | |||
| defer m.beacons.Lock.RUnlock() | |||
| return &m.beacons | |||
| } | |||
| // GetSettings returns thread-safe access to settings | |||
| func (m *AppState) GetSettings() *model.Settings { | |||
| func (m *AppState) GetSettings() *Settings { | |||
| return &m.settings | |||
| } | |||
| // GetBeaconEvents returns thread-safe access to beacon events | |||
| func (m *AppState) GetBeaconEvents() *model.BeaconEventList { | |||
| func (m *AppState) GetBeaconEvents() *BeaconEventList { | |||
| m.beaconEvents.Lock.RLock() | |||
| defer m.beaconEvents.Lock.RUnlock() | |||
| return &m.beaconEvents | |||
| @@ -98,7 +97,7 @@ func (m *AppState) BeaconExists(id string) (string, bool) { | |||
| } | |||
| // 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() | |||
| 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) | |||
| func (m *AppState) UpdateBeacon(id string, beacon model.Beacon) { | |||
| func (m *AppState) UpdateBeacon(id string, beacon Beacon) { | |||
| m.beacons.Lock.Lock() | |||
| 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) | |||
| func (m *AppState) GetBeaconEvent(id string) (model.BeaconEvent, bool) { | |||
| func (m *AppState) GetBeaconEvent(id string) (BeaconEvent, bool) { | |||
| m.beaconEvents.Lock.RLock() | |||
| 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) | |||
| func (m *AppState) UpdateBeaconEvent(id string, event model.BeaconEvent) { | |||
| func (m *AppState) UpdateBeaconEvent(id string, event BeaconEvent) { | |||
| m.beaconEvents.Lock.Lock() | |||
| 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 | |||
| func (m *AppState) GetAllBeacons() map[string]model.Beacon { | |||
| func (m *AppState) GetAllBeacons() map[string]Beacon { | |||
| m.beacons.Lock.RLock() | |||
| defer m.beacons.Lock.RUnlock() | |||
| beacons := make(map[string]model.Beacon) | |||
| beacons := make(map[string]Beacon) | |||
| for id, beacon := range m.beacons.Beacons { | |||
| beacons[id] = beacon | |||
| } | |||
| @@ -152,7 +151,7 @@ func (m *AppState) GetBeaconCount() int { | |||
| } | |||
| // GetSettingsValue returns current settings as a value | |||
| func (m *AppState) GetSettingsValue() model.Settings { | |||
| func (m *AppState) GetSettingsValue() Settings { | |||
| return m.settings | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| package model | |||
| package appcontext | |||
| import ( | |||
| "crypto/sha256" | |||
| @@ -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 | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package utils | |||
| import ( | |||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | |||
| "github.com/AFASystems/presence/internal/pkg/model" | |||
| ) | |||
| @@ -37,8 +38,8 @@ func RemoveFlagBytes(b []byte) []byte { | |||
| } | |||
| // 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 { | |||
| ad := b[r[0]:r[1]] | |||
| if !isValidADStructure(ad) { | |||
| @@ -4,10 +4,10 @@ import ( | |||
| "math" | |||
| "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 | |||
| power := adv.TXPower | |||
| ratio := float64(rssi) * (1.0 / float64(twosComp(power))) | |||
| @@ -7,15 +7,15 @@ import ( | |||
| "net/http" | |||
| "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/model" | |||
| "github.com/segmentio/kafka-go" | |||
| "gorm.io/gorm" | |||
| ) | |||
| func SettingsListController(db *gorm.DB, context context.Context) http.HandlerFunc { | |||
| 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 { | |||
| response.InternalError(w, "failed to list settings", err) | |||
| return | |||
| @@ -34,7 +34,7 @@ func SettingsUpdateController(db *gorm.DB, writer *kafka.Writer, context context | |||
| 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) | |||
| return | |||
| } | |||
| @@ -4,6 +4,7 @@ import ( | |||
| "fmt" | |||
| "log/slog" | |||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | |||
| "github.com/AFASystems/presence/internal/pkg/config" | |||
| "github.com/AFASystems/presence/internal/pkg/model" | |||
| "gorm.io/driver/postgres" | |||
| @@ -25,7 +26,7 @@ func Connect(cfg *config.Config) (*gorm.DB, error) { | |||
| 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 | |||
| } | |||
| @@ -16,14 +16,14 @@ import ( | |||
| ) | |||
| // 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 { | |||
| 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. | |||
| 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) | |||
| id := adv.ID | |||
| if beacon == "" { | |||
| @@ -121,3 +121,23 @@ func (m *KafkaManager) GetWriter(topic string) *kafka.Writer { | |||
| defer m.kafkaWritersMap.KafkaWritersLock.RUnlock() | |||
| 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 | |||
| } | |||
| @@ -6,12 +6,11 @@ import ( | |||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | |||
| "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 | |||
| // 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 | |||
| now := time.Now().Unix() | |||
| settings := appState.GetSettingsValue() | |||
| @@ -23,17 +22,17 @@ func AssignBeaconToList(adv model.BeaconAdvertisement, appState *appcontext.AppS | |||
| beacon, ok := appState.GetBeacon(id) | |||
| if !ok { | |||
| beacon = model.Beacon{ID: id} | |||
| beacon = appcontext.Beacon{ID: id} | |||
| } | |||
| beacon.IncomingJSON = adv | |||
| beacon.LastSeen = now | |||
| 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), | |||
| Timestamp: now, | |||
| RSSI: int64(adv.RSSI), | |||
| @@ -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) | |||
| } | |||
| @@ -6,6 +6,8 @@ import ( | |||
| "fmt" | |||
| "log/slog" | |||
| "sync" | |||
| "github.com/AFASystems/presence/internal/pkg/common/appcontext" | |||
| ) | |||
| 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 | |||
| // 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 | |||
| event := BeaconEvent{Type: name} | |||
| event := appcontext.BeaconEvent{Type: name} | |||
| if cfg, ok := b.configs["battery"]; ok { | |||
| event.Battery = uint32(b.extract(ad, cfg).(uint16)) | |||
| flag = true | |||
| @@ -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"` | |||
| } | |||
| @@ -1,40 +1,5 @@ | |||
| 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. | |||
| type HTTPLocation struct { | |||
| Method string `json:"method"` | |||
| @@ -50,80 +15,6 @@ type HTTPLocation struct { | |||
| 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. | |||
| type RawReading struct { | |||
| Timestamp string `json:"timestamp"` | |||