|
- package location
-
- import (
- "context"
- "fmt"
- "log/slog"
- "sync"
- "time"
-
- "github.com/AFASystems/presence/internal/pkg/common/appcontext"
- "github.com/AFASystems/presence/internal/pkg/config"
- "github.com/AFASystems/presence/internal/pkg/kafkaclient"
- "github.com/AFASystems/presence/internal/pkg/logger"
- pkglocation "github.com/AFASystems/presence/internal/pkg/location"
- "github.com/AFASystems/presence/internal/pkg/model"
- )
-
- // LocationApp holds dependencies for the location service.
- type LocationApp struct {
- Cfg *config.Config
- KafkaManager *kafkaclient.KafkaManager
- AppState *appcontext.AppState
- Inferencer pkglocation.Inferencer
- ChRaw chan model.BeaconAdvertisement
- ChSettings chan map[string]any
- Cleanup func()
- wg sync.WaitGroup
- }
-
- // New creates a LocationApp with Kafka readers (rawbeacons, settings) and writer (locevents).
- func New(cfg *config.Config) (*LocationApp, error) {
- appState := appcontext.NewAppState()
- kafkaManager := kafkaclient.InitKafkaManager()
-
- srvLogger, cleanup := logger.CreateLogger("location.log")
- slog.SetDefault(srvLogger)
-
- readerTopics := []string{"rawbeacons", "settings"}
- writerTopics := []string{"locevents"}
- kafkaManager.PopulateKafkaManager(cfg.KafkaURL, "location", readerTopics)
- kafkaManager.PopulateKafkaManager(cfg.KafkaURL, "", writerTopics)
- slog.Info("location service initialized", "readers", readerTopics, "writers", writerTopics)
-
- return &LocationApp{
- Cfg: cfg,
- KafkaManager: kafkaManager,
- AppState: appState,
- Inferencer: pkglocation.NewDefaultInferencer(cfg.TLSInsecureSkipVerify),
- ChRaw: make(chan model.BeaconAdvertisement, config.LARGE_CHANNEL_SIZE),
- ChSettings: make(chan map[string]any, config.SMALL_CHANNEL_SIZE),
- Cleanup: cleanup,
- }, nil
- }
-
- // Run starts consumers and the event loop until ctx is cancelled.
- func (a *LocationApp) Run(ctx context.Context) {
- a.wg.Add(2)
- go kafkaclient.Consume(a.KafkaManager.GetReader("rawbeacons"), a.ChRaw, ctx, &a.wg)
- go kafkaclient.Consume(a.KafkaManager.GetReader("settings"), a.ChSettings, ctx, &a.wg)
-
- locTicker := time.NewTicker(config.SMALL_TICKER_INTERVAL)
- defer locTicker.Stop()
-
- for {
- select {
- case <-ctx.Done():
- return
- case <-locTicker.C:
- settings := a.AppState.GetSettings()
- slog.Info("location tick", "settings", fmt.Sprintf("%+v", settings))
- switch settings.CurrentAlgorithm {
- case "filter":
- pkglocation.GetLikelyLocations(a.AppState, a.KafkaManager.GetWriter("locevents"))
- case "ai":
- inferred, err := a.Inferencer.Infer(ctx, a.Cfg)
- if err != nil {
- slog.Error("AI inference", "err", err)
- continue
- }
- slog.Info("AI algorithm", "count", inferred.Count, "items", len(inferred.Items))
- }
- case msg := <-a.ChRaw:
- pkglocation.AssignBeaconToList(msg, a.AppState)
- case msg := <-a.ChSettings:
- slog.Info("settings update", "msg", msg)
- a.AppState.UpdateSettings(msg)
- }
- }
- }
-
- // Shutdown waits for consumers and cleans up Kafka and logger.
- func (a *LocationApp) Shutdown() {
- a.wg.Wait()
- a.KafkaManager.CleanKafkaReaders()
- a.KafkaManager.CleanKafkaWriters()
- if a.Cleanup != nil {
- a.Cleanup()
- }
- slog.Info("location service shutdown complete")
- }
|