Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

198 lignes
4.9 KiB

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "log/slog"
  7. "os/signal"
  8. "sync"
  9. "syscall"
  10. "time"
  11. "github.com/AFASystems/presence/internal/pkg/common/appcontext"
  12. "github.com/AFASystems/presence/internal/pkg/common/utils"
  13. "github.com/AFASystems/presence/internal/pkg/config"
  14. "github.com/AFASystems/presence/internal/pkg/kafkaclient"
  15. "github.com/AFASystems/presence/internal/pkg/logger"
  16. "github.com/AFASystems/presence/internal/pkg/model"
  17. "github.com/segmentio/kafka-go"
  18. )
  19. var wg sync.WaitGroup
  20. func main() {
  21. // Load global context to init beacons and latest list
  22. appState := appcontext.NewAppState()
  23. cfg := config.Load()
  24. // Set logger -> terminal and log file
  25. slog.SetDefault(logger.CreateLogger("location.log"))
  26. // Define context
  27. ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
  28. defer stop()
  29. rawReader := appState.AddKafkaReader(cfg.KafkaURL, "rawbeacons", "gid-raw-loc")
  30. settingsReader := appState.AddKafkaReader(cfg.KafkaURL, "settings", "gid-settings-loc")
  31. writer := appState.AddKafkaWriter(cfg.KafkaURL, "locevents")
  32. slog.Info("Locations algorithm initialized, subscribed to Kafka topics")
  33. locTicker := time.NewTicker(1 * time.Second)
  34. defer locTicker.Stop()
  35. chRaw := make(chan model.BeaconAdvertisement, 2000)
  36. chSettings := make(chan map[string]any, 5)
  37. wg.Add(3)
  38. go kafkaclient.Consume(rawReader, chRaw, ctx, &wg)
  39. go kafkaclient.Consume(settingsReader, chSettings, ctx, &wg)
  40. eventLoop:
  41. for {
  42. select {
  43. case <-ctx.Done():
  44. break eventLoop
  45. case <-locTicker.C:
  46. settings := appState.GetSettings()
  47. fmt.Printf("Settings: %+v\n", settings)
  48. switch settings.CurrentAlgorithm {
  49. case "filter":
  50. getLikelyLocations(appState, writer)
  51. case "ai":
  52. fmt.Println("AI algorithm selected")
  53. }
  54. case msg := <-chRaw:
  55. assignBeaconToList(msg, appState)
  56. case msg := <-chSettings:
  57. fmt.Printf("settings msg: %+v\n", msg)
  58. appState.UpdateSettings(msg)
  59. }
  60. }
  61. slog.Info("broken out of the main event loop")
  62. wg.Wait()
  63. slog.Info("All go routines have stopped, Beggining to close Kafka connections")
  64. appState.CleanKafkaReaders()
  65. appState.CleanKafkaWriters()
  66. }
  67. func getLikelyLocations(appState *appcontext.AppState, writer *kafka.Writer) {
  68. beacons := appState.GetAllBeacons()
  69. settings := appState.GetSettingsValue()
  70. for _, beacon := range beacons {
  71. // Shrinking the model because other properties have nothing to do with the location
  72. r := model.HTTPLocation{
  73. Method: "Standard",
  74. Distance: 999,
  75. ID: beacon.ID,
  76. Location: "",
  77. LastSeen: 999,
  78. }
  79. mSize := len(beacon.BeaconMetrics)
  80. if (int64(time.Now().Unix()) - (beacon.BeaconMetrics[mSize-1].Timestamp)) > settings.LastSeenThreshold {
  81. slog.Warn("beacon is too old")
  82. continue
  83. }
  84. locList := make(map[string]float64)
  85. seenW := 1.5
  86. rssiW := 0.75
  87. for _, metric := range beacon.BeaconMetrics {
  88. res := seenW + (rssiW * (1.0 - (float64(metric.RSSI) / -100.0)))
  89. locList[metric.Location] += res
  90. }
  91. bestLocName := ""
  92. maxScore := 0.0
  93. for locName, score := range locList {
  94. if score > maxScore {
  95. maxScore = score
  96. bestLocName = locName
  97. }
  98. }
  99. if bestLocName == beacon.PreviousLocation {
  100. beacon.LocationConfidence++
  101. } else {
  102. beacon.LocationConfidence = 0
  103. }
  104. r.Distance = beacon.BeaconMetrics[mSize-1].Distance
  105. r.Location = bestLocName
  106. r.LastSeen = beacon.BeaconMetrics[mSize-1].Timestamp
  107. if beacon.LocationConfidence == settings.LocationConfidence && beacon.PreviousConfidentLocation != bestLocName {
  108. beacon.LocationConfidence = 0
  109. }
  110. beacon.PreviousLocation = bestLocName
  111. appState.UpdateBeacon(beacon.ID, beacon)
  112. js, err := json.Marshal(r)
  113. if err != nil {
  114. eMsg := fmt.Sprintf("Error in marshaling location: %v", err)
  115. slog.Error(eMsg)
  116. continue
  117. }
  118. msg := kafka.Message{
  119. Value: js,
  120. }
  121. err = writer.WriteMessages(context.Background(), msg)
  122. if err != nil {
  123. eMsg := fmt.Sprintf("Error in sending Kafka message: %v", err)
  124. slog.Error(eMsg)
  125. }
  126. }
  127. }
  128. func assignBeaconToList(adv model.BeaconAdvertisement, appState *appcontext.AppState) {
  129. id := adv.ID
  130. now := time.Now().Unix()
  131. settings := appState.GetSettingsValue()
  132. if settings.RSSIEnforceThreshold && (int64(adv.RSSI) < settings.RSSIMinThreshold) {
  133. slog.Info("Settings returns")
  134. return
  135. }
  136. beacon, ok := appState.GetBeacon(id)
  137. if !ok {
  138. beacon = model.Beacon{
  139. ID: id,
  140. }
  141. }
  142. beacon.IncomingJSON = adv
  143. beacon.LastSeen = now
  144. if beacon.BeaconMetrics == nil {
  145. beacon.BeaconMetrics = make([]model.BeaconMetric, 0, settings.BeaconMetricSize)
  146. }
  147. metric := model.BeaconMetric{
  148. Distance: utils.CalculateDistance(adv),
  149. Timestamp: now,
  150. RSSI: int64(adv.RSSI),
  151. Location: adv.Hostname,
  152. }
  153. if len(beacon.BeaconMetrics) >= settings.BeaconMetricSize {
  154. copy(beacon.BeaconMetrics, beacon.BeaconMetrics[1:])
  155. beacon.BeaconMetrics[settings.BeaconMetricSize-1] = metric
  156. } else {
  157. beacon.BeaconMetrics = append(beacon.BeaconMetrics, metric)
  158. }
  159. appState.UpdateBeacon(id, beacon)
  160. }