|
- package appcontext
-
- import (
- "context"
- "encoding/json"
- "fmt"
- "strings"
- "time"
-
- "github.com/AFASystems/presence/internal/pkg/model"
- "github.com/redis/go-redis/v9"
- "github.com/segmentio/kafka-go"
- )
-
- // AppState provides centralized access to application state
- type AppState struct {
- beacons model.BeaconsList
- settings model.Settings
- beaconEvents model.BeaconEventList
- beaconsLookup map[string]struct{}
- latestList model.LatestBeaconsList
- kafkaReadersList model.KafkaReadersList
- kafkaWritersList model.KafkaWritersList
- valkeyDB *redis.Client
- }
-
- // NewAppState creates a new application context AppState with default values
- func NewAppState() *AppState {
- return &AppState{
- beacons: model.BeaconsList{
- Beacons: make(map[string]model.Beacon),
- },
- settings: model.Settings{
- Settings: model.SettingsVal{
- LocationConfidence: 4,
- LastSeenThreshold: 15,
- BeaconMetricSize: 30,
- HASendInterval: 5,
- HASendChangesOnly: false,
- RSSIEnforceThreshold: false,
- RSSIMinThreshold: 100,
- },
- },
- beaconEvents: model.BeaconEventList{
- Beacons: make(map[string]model.BeaconEvent),
- },
- beaconsLookup: make(map[string]struct{}),
- latestList: model.LatestBeaconsList{
- LatestList: make(map[string]model.Beacon),
- },
- kafkaReadersList: model.KafkaReadersList{
- KafkaReaders: make([]*kafka.Reader, 0),
- },
- kafkaWritersList: model.KafkaWritersList{
- KafkaWriters: make([]*kafka.Writer, 0),
- },
- }
- }
-
- func (m *AppState) AddValkeyClient(url string) *redis.Client {
- valkeyDB := redis.NewClient(&redis.Options{
- Addr: url,
- Password: "",
- })
-
- m.valkeyDB = valkeyDB
- return valkeyDB
- }
-
- func (m *AppState) CleanValkeyClient() {
- fmt.Println("shutdown of valkey client starts")
- if err := m.valkeyDB.Close(); err != nil {
- fmt.Println("Error in shuting down valkey client")
- }
-
- fmt.Println("Succesfully shutting down valkey client")
- }
-
- func (m *AppState) AddKafkaWriter(kafkaUrl, topic string) *kafka.Writer {
- kafkaWriter := &kafka.Writer{
- Addr: kafka.TCP(kafkaUrl),
- Topic: topic,
- Balancer: &kafka.LeastBytes{},
- Async: false,
- RequiredAcks: kafka.RequireAll,
- BatchSize: 100,
- BatchTimeout: 10 * time.Millisecond,
- }
-
- m.kafkaWritersList.KafkaWritersLock.Lock()
- m.kafkaWritersList.KafkaWriters = append(m.kafkaWritersList.KafkaWriters, kafkaWriter)
- m.kafkaWritersList.KafkaWritersLock.Unlock()
-
- return kafkaWriter
- }
-
- func (m *AppState) CleanKafkaWriters() {
- fmt.Println("shutdown of kafka readers starts")
- for _, r := range m.kafkaWritersList.KafkaWriters {
- if err := r.Close(); err != nil {
- fmt.Printf("Error in closing kafka writer %v", err)
- }
- }
-
- fmt.Println("Kafka writers graceful shutdown complete")
- }
-
- func (m *AppState) AddKafkaReader(kafkaUrl, topic, groupID string) *kafka.Reader {
- brokers := strings.Split(kafkaUrl, ",")
- kafkaReader := kafka.NewReader(kafka.ReaderConfig{
- Brokers: brokers,
- GroupID: groupID,
- Topic: topic,
- MinBytes: 1,
- MaxBytes: 10e6,
- })
-
- m.kafkaReadersList.KafkaReadersLock.Lock()
- m.kafkaReadersList.KafkaReaders = append(m.kafkaReadersList.KafkaReaders, kafkaReader)
- m.kafkaReadersList.KafkaReadersLock.Unlock()
-
- return kafkaReader
- }
-
- func (m *AppState) CleanKafkaReaders() {
- for _, r := range m.kafkaReadersList.KafkaReaders {
- if err := r.Close(); err != nil {
- fmt.Printf("Error in closing kafka reader %v", err)
- }
- }
-
- fmt.Println("Kafka readers graceful shutdown complete")
- }
-
- // GetBeacons returns thread-safe access to beacons list
- func (m *AppState) GetBeacons() *model.BeaconsList {
- return &m.beacons
- }
-
- // GetSettings returns thread-safe access to settings
- func (m *AppState) GetSettings() *model.Settings {
- return &m.settings
- }
-
- // GetBeaconEvents returns thread-safe access to beacon events
- func (m *AppState) GetBeaconEvents() *model.BeaconEventList {
- return &m.beaconEvents
- }
-
- // GetBeaconsLookup returns thread-safe access to beacon lookup map
- func (m *AppState) GetBeaconsLookup() map[string]struct{} {
- return m.beaconsLookup
- }
-
- // GetLatestList returns thread-safe access to latest beacons list
- func (m *AppState) GetLatestList() *model.LatestBeaconsList {
- return &m.latestList
- }
-
- // AddBeaconToLookup adds a beacon ID to the lookup map
- func (m *AppState) AddBeaconToLookup(id string) {
- m.beaconsLookup[id] = struct{}{}
- }
-
- // RemoveBeaconFromLookup removes a beacon ID from the lookup map
- func (m *AppState) RemoveBeaconFromLookup(id string) {
- delete(m.beaconsLookup, id)
- }
-
- func (m *AppState) RemoveBeacon(id string) {
- m.beacons.Lock.Lock()
- delete(m.beacons.Beacons, id)
- m.beacons.Lock.Unlock()
- }
-
- // BeaconExists checks if a beacon exists in the lookup
- func (m *AppState) BeaconExists(id string) bool {
- _, exists := m.beaconsLookup[id]
- return exists
- }
-
- // GetBeacon returns a beacon by ID (thread-safe)
- func (m *AppState) GetBeacon(id string) (model.Beacon, bool) {
- m.beacons.Lock.RLock()
- defer m.beacons.Lock.RUnlock()
-
- beacon, exists := m.beacons.Beacons[id]
- return beacon, exists
- }
-
- // UpdateBeacon updates a beacon in the list (thread-safe)
- func (m *AppState) UpdateBeacon(id string, beacon model.Beacon) {
- m.beacons.Lock.Lock()
- defer m.beacons.Lock.Unlock()
-
- m.beacons.Beacons[id] = beacon
- }
-
- // GetBeaconEvent returns a beacon event by ID (thread-safe)
- func (m *AppState) GetBeaconEvent(id string) (model.BeaconEvent, bool) {
- m.beaconEvents.Lock.RLock()
- defer m.beaconEvents.Lock.RUnlock()
-
- event, exists := m.beaconEvents.Beacons[id]
- return event, exists
- }
-
- // UpdateBeaconEvent updates a beacon event in the list (thread-safe)
- func (m *AppState) UpdateBeaconEvent(id string, event model.BeaconEvent) {
- m.beaconEvents.Lock.Lock()
- defer m.beaconEvents.Lock.Unlock()
-
- m.beaconEvents.Beacons[id] = event
- }
-
- // GetLatestBeacon returns the latest beacon by ID (thread-safe)
- func (m *AppState) GetLatestBeacon(id string) (model.Beacon, bool) {
- m.latestList.Lock.RLock()
- defer m.latestList.Lock.RUnlock()
-
- beacon, exists := m.latestList.LatestList[id]
- return beacon, exists
- }
-
- // UpdateLatestBeacon updates the latest beacon in the list (thread-safe)
- func (m *AppState) UpdateLatestBeacon(id string, beacon model.Beacon) {
- m.latestList.Lock.Lock()
- defer m.latestList.Lock.Unlock()
-
- m.latestList.LatestList[id] = beacon
- }
-
- // GetAllBeacons returns a copy of all beacons
- func (m *AppState) GetAllBeacons() map[string]model.Beacon {
- m.beacons.Lock.RLock()
- defer m.beacons.Lock.RUnlock()
-
- beacons := make(map[string]model.Beacon)
- for id, beacon := range m.beacons.Beacons {
- beacons[id] = beacon
- }
- return beacons
- }
-
- // GetAllLatestBeacons returns a copy of all latest beacons
- func (m *AppState) GetAllLatestBeacons() map[string]model.Beacon {
- m.latestList.Lock.RLock()
- defer m.latestList.Lock.RUnlock()
-
- beacons := make(map[string]model.Beacon)
- for id, beacon := range m.latestList.LatestList {
- beacons[id] = beacon
- }
- return beacons
- }
-
- // GetBeaconCount returns the number of tracked beacons
- func (m *AppState) GetBeaconCount() int {
- m.beacons.Lock.RLock()
- defer m.beacons.Lock.RUnlock()
-
- return len(m.beacons.Beacons)
- }
-
- // GetSettingsValue returns current settings as a value
- func (m *AppState) GetSettingsValue() model.SettingsVal {
- m.settings.Lock.RLock()
- defer m.settings.Lock.RUnlock()
-
- return m.settings.Settings
- }
-
- // UpdateSettings updates the system settings (thread-safe)
- func (m *AppState) UpdateSettings(newSettings model.SettingsVal) {
- m.settings.Lock.Lock()
- defer m.settings.Lock.Unlock()
-
- m.settings.Settings = newSettings
- }
-
- func (m *AppState) PersistSettings(client *redis.Client, ctx context.Context) {
- d, err := json.Marshal(m.GetSettingsValue())
- if err != nil {
- fmt.Printf("Error in marshalling settings: %v", err)
- return
- }
-
- if err := client.Set(ctx, "settings", d, 0).Err(); err != nil {
- fmt.Printf("Error in persisting settings: %v", err)
- }
- }
|