您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

272 行
7.0 KiB

  1. package appcontext
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "os"
  6. "sync"
  7. "time"
  8. "github.com/AFASystems/presence/internal/pkg/kafkaclient"
  9. "github.com/mitchellh/mapstructure"
  10. )
  11. // AppState provides centralized access to application state
  12. type AppState struct {
  13. beacons BeaconsList
  14. settings Settings
  15. beaconEvents BeaconEventList
  16. beaconsLookup BeaconsLookup
  17. health Health
  18. startTime time.Time
  19. mu sync.RWMutex
  20. }
  21. func getEnv(key, def string) string {
  22. if v := os.Getenv(key); v != "" {
  23. return v
  24. }
  25. return def
  26. }
  27. // NewAppState creates a new application context AppState with default values
  28. func NewAppState() *AppState {
  29. return &AppState{
  30. startTime: time.Now(),
  31. beacons: BeaconsList{
  32. Beacons: make(map[string]Beacon),
  33. },
  34. settings: Settings{
  35. ID: 1,
  36. CurrentAlgorithm: getEnv("ALGORITHM", "filter"),
  37. LocationConfidence: 4,
  38. LastSeenThreshold: 15,
  39. BeaconMetricSize: 30,
  40. HASendInterval: 5,
  41. HASendChangesOnly: false,
  42. RSSIEnforceThreshold: false,
  43. RSSIMinThreshold: 100,
  44. },
  45. beaconEvents: BeaconEventList{
  46. Beacons: make(map[string]BeaconEvent),
  47. },
  48. beaconsLookup: BeaconsLookup{
  49. Lookup: make(map[string]string),
  50. },
  51. health: Health{
  52. Location: LocationHealth{
  53. BaseHealth: BaseHealth{
  54. Uptime: 0,
  55. ActiveReaders: []string{},
  56. ActiveWriters: []string{},
  57. },
  58. ActiveBeacons: []string{},
  59. },
  60. Decoder: DecoderHealth{
  61. BaseHealth: BaseHealth{
  62. Uptime: 0,
  63. ActiveReaders: []string{},
  64. ActiveWriters: []string{},
  65. },
  66. },
  67. Bridge: BridgeHealth{
  68. BaseHealth: BaseHealth{
  69. Uptime: 0,
  70. ActiveReaders: []string{},
  71. ActiveWriters: []string{},
  72. },
  73. ActiveBeacons: []string{},
  74. },
  75. Kafka: ServiceStatus{Status: "unknown"},
  76. Database: ServiceStatus{Status: "unknown"},
  77. },
  78. }
  79. }
  80. func (m *AppState) GetHealth() Health {
  81. m.mu.RLock()
  82. defer m.mu.RUnlock()
  83. return m.health
  84. }
  85. func (m *AppState) UpdateLocationHealth(h LocationHealth) {
  86. m.mu.Lock()
  87. m.health.Location = h
  88. m.mu.Unlock()
  89. }
  90. func (m *AppState) UpdateDecoderHealth(h DecoderHealth) {
  91. m.mu.Lock()
  92. m.health.Decoder = h
  93. m.mu.Unlock()
  94. }
  95. func (m *AppState) UpdateBridgeHealth(h BridgeHealth) {
  96. m.mu.Lock()
  97. m.health.Bridge = h
  98. m.mu.Unlock()
  99. }
  100. // UpdateServerHealth updates Kafka and database status (called by server after checking broker and DB).
  101. func (m *AppState) UpdateServerHealth(kafka, database ServiceStatus) {
  102. m.mu.Lock()
  103. m.health.Kafka = kafka
  104. m.health.Database = database
  105. m.mu.Unlock()
  106. }
  107. func (m *AppState) GetLocationHealth(k *kafkaclient.KafkaManager) ([]byte, error) {
  108. beacons := m.GetAllBeacons()
  109. m.health.Location.ActiveBeacons = make([]string, 0, len(beacons))
  110. for beacon := range beacons {
  111. m.health.Location.ActiveBeacons = append(m.health.Location.ActiveBeacons, beacon)
  112. }
  113. m.health.Location.GetUptime(m.startTime)
  114. m.health.Location.GetActiveReaders(k)
  115. m.health.Location.GetActiveWriters(k)
  116. return m.health.Location.Marshal()
  117. }
  118. func (m *AppState) GetDecoderHealth(k *kafkaclient.KafkaManager) ([]byte, error) {
  119. m.health.Decoder.GetUptime(m.startTime)
  120. m.health.Decoder.GetActiveReaders(k)
  121. m.health.Decoder.GetActiveWriters(k)
  122. return m.health.Decoder.Marshal()
  123. }
  124. func (m *AppState) GetBridgeHealth(k *kafkaclient.KafkaManager) ([]byte, error) {
  125. beacons := m.GetBeaconLookup()
  126. m.health.Bridge.ActiveBeacons = make([]string, 0, len(beacons))
  127. for beacon := range beacons {
  128. m.health.Bridge.ActiveBeacons = append(m.health.Bridge.ActiveBeacons, beacon)
  129. }
  130. m.health.Bridge.GetUptime(m.startTime)
  131. m.health.Bridge.GetActiveReaders(k)
  132. m.health.Bridge.GetActiveWriters(k)
  133. return m.health.Bridge.Marshal()
  134. }
  135. // GetBeacons returns thread-safe access to beacons list
  136. func (m *AppState) GetBeacons() *BeaconsList {
  137. m.beacons.Lock.RLock()
  138. defer m.beacons.Lock.RUnlock()
  139. return &m.beacons
  140. }
  141. // GetSettings returns thread-safe access to settings
  142. func (m *AppState) GetSettings() *Settings {
  143. return &m.settings
  144. }
  145. // GetBeaconEvents returns thread-safe access to beacon events
  146. func (m *AppState) GetBeaconEvents() *BeaconEventList {
  147. m.beaconEvents.Lock.RLock()
  148. defer m.beaconEvents.Lock.RUnlock()
  149. return &m.beaconEvents
  150. }
  151. // AddBeaconToLookup adds a beacon ID to the lookup map
  152. func (m *AppState) AddBeaconToLookup(id, value string) {
  153. m.beaconsLookup.Lock.Lock()
  154. m.beaconsLookup.Lookup[id] = value
  155. m.beaconsLookup.Lock.Unlock()
  156. }
  157. // RemoveBeaconFromLookup removes a beacon ID from the lookup map
  158. func (m *AppState) RemoveBeaconFromLookup(id string) {
  159. m.beaconsLookup.Lock.Lock()
  160. delete(m.beaconsLookup.Lookup, id)
  161. m.beaconsLookup.Lock.Unlock()
  162. }
  163. func (m *AppState) CleanLookup() {
  164. m.beaconsLookup.Lock.Lock()
  165. clear(m.beaconsLookup.Lookup)
  166. m.beaconsLookup.Lock.Unlock()
  167. }
  168. func (m *AppState) GetBeaconLookup() map[string]string {
  169. m.beaconsLookup.Lock.RLock()
  170. defer m.beaconsLookup.Lock.RUnlock()
  171. cp := make(map[string]string, len(m.beaconsLookup.Lookup))
  172. for k, v := range m.beaconsLookup.Lookup {
  173. cp[k] = v
  174. }
  175. return cp
  176. }
  177. // BeaconExists checks if a beacon exists in the lookup
  178. func (m *AppState) BeaconExists(id string) (string, bool) {
  179. m.beaconsLookup.Lock.RLock()
  180. defer m.beaconsLookup.Lock.RUnlock()
  181. val, exists := m.beaconsLookup.Lookup[id]
  182. return val, exists
  183. }
  184. // GetBeacon returns a beacon by ID (thread-safe)
  185. func (m *AppState) GetBeacon(id string) (Beacon, bool) {
  186. m.beacons.Lock.RLock()
  187. defer m.beacons.Lock.RUnlock()
  188. beacon, exists := m.beacons.Beacons[id]
  189. return beacon, exists
  190. }
  191. // UpdateBeacon updates a beacon in the list (thread-safe)
  192. func (m *AppState) UpdateBeacon(id string, beacon Beacon) {
  193. m.beacons.Lock.Lock()
  194. defer m.beacons.Lock.Unlock()
  195. m.beacons.Beacons[id] = beacon
  196. }
  197. // GetBeaconEvent returns a beacon event by ID (thread-safe)
  198. func (m *AppState) GetBeaconEvent(id string) (BeaconEvent, bool) {
  199. m.beaconEvents.Lock.RLock()
  200. defer m.beaconEvents.Lock.RUnlock()
  201. event, exists := m.beaconEvents.Beacons[id]
  202. return event, exists
  203. }
  204. // UpdateBeaconEvent updates a beacon event in the list (thread-safe)
  205. func (m *AppState) UpdateBeaconEvent(id string, event BeaconEvent) {
  206. m.beaconEvents.Lock.Lock()
  207. defer m.beaconEvents.Lock.Unlock()
  208. m.beaconEvents.Beacons[id] = event
  209. }
  210. // GetAllBeacons returns a copy of all beacons
  211. func (m *AppState) GetAllBeacons() map[string]Beacon {
  212. m.beacons.Lock.RLock()
  213. defer m.beacons.Lock.RUnlock()
  214. beacons := make(map[string]Beacon)
  215. for id, beacon := range m.beacons.Beacons {
  216. beacons[id] = beacon
  217. }
  218. return beacons
  219. }
  220. // GetBeaconCount returns the number of tracked beacons
  221. func (m *AppState) GetBeaconCount() int {
  222. m.beacons.Lock.RLock()
  223. defer m.beacons.Lock.RUnlock()
  224. return len(m.beacons.Beacons)
  225. }
  226. // GetSettingsValue returns current settings as a value
  227. func (m *AppState) GetSettingsValue() Settings {
  228. return m.settings
  229. }
  230. // UpdateSettings updates the system settings (thread-safe)
  231. func (m *AppState) UpdateSettings(settings map[string]any) {
  232. if err := mapstructure.Decode(settings, &m.settings); err != nil {
  233. msg := fmt.Sprintf("Error in persisting settings: %v", err)
  234. slog.Error(msg)
  235. }
  236. }