Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

166 rindas
4.3 KiB

  1. package mqttclient
  2. import (
  3. "encoding/json"
  4. "log"
  5. "time"
  6. "github.com/AFASystems/presence/internal/pkg/model"
  7. "github.com/AFASystems/presence/internal/pkg/persistence"
  8. "github.com/yosssi/gmq/mqtt"
  9. "github.com/yosssi/gmq/mqtt/client"
  10. )
  11. func getLikelyLocations(settings *model.Settings, ctx *model.AppContext, cl *client.Client) {
  12. ctx.HTTPResults.HTTPResultsLock.Lock()
  13. defer ctx.HTTPResults.HTTPResultsLock.Unlock()
  14. ctx.HTTPResults.HTTPResults = model.HTTPLocationsList{Beacons: []model.HTTPLocation{}}
  15. shouldPersist := false
  16. for id, beacon := range ctx.Beacons.Beacons {
  17. if len(beacon.Beacon_metrics) == 0 {
  18. continue
  19. }
  20. if isExpired(&beacon, settings) {
  21. handleExpiredBeacon(&beacon, cl, ctx)
  22. continue
  23. }
  24. best := calculateBestLocation(&beacon)
  25. updateBeaconState(&beacon, best, settings, ctx, cl)
  26. appendHTTPResult(ctx, beacon, best)
  27. ctx.Beacons.Beacons[id] = beacon
  28. shouldPersist = true
  29. }
  30. if shouldPersist {
  31. persistence.PersistBeacons(&ctx.Beacons)
  32. }
  33. }
  34. func isExpired(b *model.Beacon, s *model.Settings) bool {
  35. return time.Now().Unix()-b.Beacon_metrics[len(b.Beacon_metrics)-1].Timestamp > s.Last_seen_threshold
  36. }
  37. func handleExpiredBeacon(b *model.Beacon, cl *client.Client, ctx *model.AppContext) {
  38. if b.Expired_location == "expired" {
  39. return
  40. }
  41. b.Expired_location = "expired"
  42. msg := model.Message{
  43. Email: b.Previous_confident_location,
  44. Username: b.Name,
  45. Message: "expired",
  46. }
  47. data, _ := json.Marshal(msg)
  48. log.Println(string(data))
  49. ctx.Broadcast <- msg
  50. }
  51. func calculateBestLocation(b *model.Beacon) model.BestLocation {
  52. locScores := map[string]float64{}
  53. for _, m := range b.Beacon_metrics {
  54. score := 1.5 + 0.75*(1.0-(float64(m.Rssi)/-100.0))
  55. locScores[m.Location] += score
  56. }
  57. bestName, bestScore := "", 0.0
  58. for name, score := range locScores {
  59. if score > bestScore {
  60. bestName, bestScore = name, score
  61. }
  62. }
  63. last := b.Beacon_metrics[len(b.Beacon_metrics)-1]
  64. return model.BestLocation{Name: bestName, Distance: last.Distance, Last_seen: last.Timestamp}
  65. }
  66. func updateBeaconState(b *model.Beacon, best model.BestLocation, s *model.Settings, ctx *model.AppContext, cl *client.Client) {
  67. updateLocationHistory(b, best.Name)
  68. updateConfidence(b, best.Name, s)
  69. if locationChanged(b, best, s) {
  70. publishLocationChange(b, best, cl)
  71. b.Location_confidence = 0
  72. b.Previous_confident_location = best.Name
  73. }
  74. }
  75. func updateLocationHistory(b *model.Beacon, loc string) {
  76. b.Location_history = append(b.Location_history, loc)
  77. if len(b.Location_history) > 10 {
  78. b.Location_history = b.Location_history[1:]
  79. }
  80. }
  81. func updateConfidence(b *model.Beacon, loc string, s *model.Settings) {
  82. counts := map[string]int{}
  83. for _, l := range b.Location_history {
  84. counts[l]++
  85. }
  86. maxCount, mostCommon := 0, ""
  87. for l, c := range counts {
  88. if c > maxCount {
  89. maxCount, mostCommon = c, l
  90. }
  91. }
  92. if maxCount >= 7 {
  93. if mostCommon == b.Previous_confident_location {
  94. b.Location_confidence++
  95. } else {
  96. b.Location_confidence = 1
  97. b.Previous_confident_location = mostCommon
  98. }
  99. }
  100. }
  101. func locationChanged(b *model.Beacon, best model.BestLocation, s *model.Settings) bool {
  102. return (b.Location_confidence == s.Location_confidence &&
  103. b.Previous_confident_location != best.Name) ||
  104. b.Expired_location == "expired"
  105. }
  106. func publishLocationChange(b *model.Beacon, best model.BestLocation, cl *client.Client) {
  107. location := best.Name
  108. if b.Expired_location == "expired" {
  109. location = "expired"
  110. }
  111. js, err := json.Marshal(model.LocationChange{
  112. Beacon_ref: *b,
  113. Name: b.Name,
  114. Previous_location: b.Previous_confident_location,
  115. New_location: location,
  116. Timestamp: time.Now().Unix(),
  117. })
  118. if err != nil {
  119. return
  120. }
  121. err = cl.Publish(&client.PublishOptions{
  122. QoS: mqtt.QoS1,
  123. TopicName: []byte("afa-systems/presence/changes"),
  124. Message: js,
  125. })
  126. if err != nil {
  127. log.Printf("mqtt publish error: %v", err)
  128. }
  129. }
  130. func appendHTTPResult(ctx *model.AppContext, b model.Beacon, best model.BestLocation) {
  131. ctx.HTTPResults.HTTPResultsLock.Lock()
  132. defer ctx.HTTPResults.HTTPResultsLock.Unlock()
  133. r := model.HTTPLocation{
  134. Name: b.Name,
  135. Beacon_id: b.Beacon_id,
  136. Location: best.Name,
  137. Distance: best.Distance,
  138. Last_seen: best.Last_seen,
  139. }
  140. ctx.HTTPResults.HTTPResults.Beacons = append(ctx.HTTPResults.HTTPResults.Beacons, r)
  141. }