Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

291 řádky
6.6 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/binary"
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "math"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/AFASystems/presence/internal/pkg/config"
  14. "github.com/AFASystems/presence/internal/pkg/kafkaclient"
  15. "github.com/AFASystems/presence/internal/pkg/model"
  16. "github.com/AFASystems/presence/internal/pkg/mqttclient"
  17. "github.com/segmentio/kafka-go"
  18. )
  19. func main() {
  20. // Load global context to init beacons and latest list
  21. appCtx := model.AppContext{
  22. Beacons: model.BeaconsList{
  23. Beacons: make(map[string]model.Beacon),
  24. },
  25. Settings: model.Settings{
  26. Settings: model.SettingsVal{
  27. Location_confidence: 4,
  28. Last_seen_threshold: 15,
  29. Beacon_metrics_size: 30,
  30. HA_send_interval: 5,
  31. HA_send_changes_only: false,
  32. },
  33. },
  34. BeaconEvents: model.BeaconEventList{
  35. Beacons: make(map[string]model.BeaconEvent),
  36. },
  37. BeaconsLookup: make(map[string]struct{}),
  38. }
  39. cfg := config.Load()
  40. // Kafka reader for Raw MQTT beacons
  41. rawReader := kafkaclient.KafkaReader(cfg.KafkaURL, "rawbeacons", "gid-raw")
  42. defer rawReader.Close()
  43. // Kafka reader for API server updates
  44. apiReader := kafkaclient.KafkaReader(cfg.KafkaURL, "apibeacons", "gid-api")
  45. defer apiReader.Close()
  46. alertWriter := kafkaclient.KafkaWriter(cfg.KafkaURL, "alertbeacons")
  47. defer alertWriter.Close()
  48. fmt.Println("Decoder initialized, subscribed to Kafka topics")
  49. // // Kafka reader for latest list updates
  50. // latestReader := kafkaclient.KafkaReader(cfg.KafkaURL, "latestbeacons", "gid-latest")
  51. // defer latestReader.Close()
  52. // // Kafka reader for settings updates
  53. // settingsReader := kafkaclient.KafkaReader(cfg.KafkaURL, "settings", "gid-settings")
  54. // defer settingsReader.Close()
  55. // declare channel for collecting Kafka messages
  56. chRaw := make(chan model.Incoming_json, 2000)
  57. chApi := make(chan model.ApiUpdate, 2000)
  58. // chLatest := make(chan model.Incoming_json, 2000)
  59. // chSettings := make(chan model.SettingsVal, 10)
  60. go kafkaclient.Consume(rawReader, chRaw)
  61. go kafkaclient.Consume(apiReader, chApi)
  62. // go kafkaclient.Consume(latestReader, chLatest)
  63. // go kafkaclient.Consume(settingsReader, chSettings)
  64. for {
  65. select {
  66. case msg := <-chRaw:
  67. processIncoming(msg, &appCtx, alertWriter)
  68. case msg := <-chApi:
  69. switch msg.Method {
  70. case "POST":
  71. id := msg.Beacon.Beacon_id
  72. appCtx.BeaconsLookup[id] = struct{}{}
  73. case "DELETE":
  74. fmt.Println("Incoming delete message")
  75. }
  76. // case msg := <-chLatest:
  77. // fmt.Println("latest msg: ", msg)
  78. // case msg := <-chSettings:
  79. // appCtx.Settings.Lock.Lock()
  80. // appCtx.Settings.Settings = msg
  81. // fmt.Println("settings channel: ", msg)
  82. // appCtx.Settings.Lock.Unlock()
  83. }
  84. }
  85. }
  86. func processIncoming(incoming model.Incoming_json, ctx *model.AppContext, writer *kafka.Writer) {
  87. id := mqttclient.GetBeaconID(incoming)
  88. _, ok := ctx.BeaconsLookup[id]
  89. if !ok {
  90. return
  91. }
  92. err := decodeBeacon(incoming, ctx, writer)
  93. if err != nil {
  94. fmt.Println("error in decoding")
  95. return
  96. }
  97. }
  98. func decodeBeacon(incoming model.Incoming_json, ctx *model.AppContext, writer *kafka.Writer) error {
  99. beacon := strings.TrimSpace(incoming.Data)
  100. id := mqttclient.GetBeaconID(incoming)
  101. if beacon == "" {
  102. return nil // How to return error?, do I even need to return error
  103. }
  104. b, err := hex.DecodeString(beacon)
  105. if err != nil {
  106. return err
  107. }
  108. // check for flag byte, if first AD structure is flag bytes, remove it
  109. if len(b) > 1 && b[1] == 0x01 {
  110. l := int(b[0]) // length of AD structure
  111. if 1+l <= len(b) {
  112. b = b[1+l:]
  113. }
  114. }
  115. adStructureIndeces := ParseADFast(b)
  116. event := model.BeaconEvent{}
  117. for _, r := range adStructureIndeces {
  118. ad := b[r[0]:r[1]]
  119. if checkIngics(ad) {
  120. event = parseIngicsState(ad)
  121. event.Id = id
  122. event.Name = id
  123. break
  124. } else if checkEddystoneTLM(ad) {
  125. event = parseEddystoneState(ad)
  126. event.Id = id
  127. event.Name = id
  128. break
  129. } else if checkMinewB7(ad) {
  130. fmt.Println("Minew B7 vendor format")
  131. break
  132. }
  133. }
  134. if event.Id != "" {
  135. prevEvent, ok := ctx.BeaconEvents.Beacons[id]
  136. ctx.BeaconEvents.Beacons[id] = event
  137. if ok && bytes.Equal(prevEvent.Hash(), event.Hash()) {
  138. return nil
  139. }
  140. eMsg, err := json.Marshal(event)
  141. if err != nil {
  142. return err
  143. }
  144. err = writer.WriteMessages(context.Background(), kafka.Message{
  145. Value: eMsg,
  146. })
  147. if err != nil {
  148. return err
  149. }
  150. fmt.Println("Message sent")
  151. }
  152. return nil
  153. }
  154. func checkIngics(ad []byte) bool {
  155. if len(ad) >= 6 &&
  156. ad[1] == 0xFF &&
  157. ad[2] == 0x59 &&
  158. ad[3] == 0x00 &&
  159. ad[4] == 0x80 &&
  160. ad[5] == 0xBC {
  161. return true
  162. }
  163. return false
  164. }
  165. func parseIngicsState(ad []byte) model.BeaconEvent {
  166. return model.BeaconEvent{
  167. Battery: uint32(binary.LittleEndian.Uint16(ad[6:8])),
  168. Event: int(ad[8]),
  169. Type: "Ingics",
  170. }
  171. }
  172. func checkEddystoneTLM(ad []byte) bool {
  173. if len(ad) >= 4 &&
  174. ad[1] == 0x16 &&
  175. ad[2] == 0xAA &&
  176. ad[3] == 0xFE &&
  177. ad[4] == 0x20 {
  178. return true
  179. }
  180. return false
  181. }
  182. func parseEddystoneState(ad []byte) model.BeaconEvent {
  183. return model.BeaconEvent{
  184. Battery: uint32(binary.BigEndian.Uint16(ad[6:8])),
  185. Type: "Eddystone",
  186. }
  187. }
  188. // I dont think this is always true, but for testing is ok
  189. func checkMinewB7(ad []byte) bool {
  190. if len(ad) >= 4 &&
  191. ad[1] == 0x16 &&
  192. ad[2] == 0xE1 &&
  193. ad[3] == 0xFF {
  194. return true
  195. }
  196. return false
  197. }
  198. func ParseADFast(b []byte) [][2]int {
  199. var res [][2]int
  200. i := 0
  201. for i < len(b) {
  202. l := int(b[i])
  203. if l == 0 || i+1+l > len(b) {
  204. break
  205. }
  206. res = append(res, [2]int{i, i + 1 + l})
  207. i += 1 + l
  208. }
  209. return res
  210. }
  211. func getBeaconDistance(incoming model.Incoming_json) float64 {
  212. rssi := incoming.RSSI
  213. power := incoming.TX_power
  214. distance := 100.0
  215. ratio := float64(rssi) * (1.0 / float64(twos_comp(power)))
  216. if ratio < 1.0 {
  217. distance = math.Pow(ratio, 10)
  218. } else {
  219. distance = (0.89976)*math.Pow(ratio, 7.7095) + 0.111
  220. }
  221. return distance
  222. }
  223. func updateBeacon(beacon *model.Beacon, incoming model.Incoming_json) {
  224. now := time.Now().Unix()
  225. beacon.Incoming_JSON = incoming
  226. beacon.Last_seen = now
  227. beacon.Beacon_type = incoming.Beacon_type
  228. beacon.HB_ButtonCounter = incoming.HB_ButtonCounter
  229. beacon.HB_Battery = incoming.HB_Battery
  230. beacon.HB_RandomNonce = incoming.HB_RandomNonce
  231. beacon.HB_ButtonMode = incoming.HB_ButtonMode
  232. if beacon.Beacon_metrics == nil {
  233. beacon.Beacon_metrics = make([]model.BeaconMetric, 10)
  234. }
  235. metric := model.BeaconMetric{}
  236. metric.Distance = getBeaconDistance(incoming)
  237. metric.Timestamp = now
  238. metric.Rssi = int64(incoming.RSSI)
  239. metric.Location = incoming.Hostname
  240. beacon.Beacon_metrics = append(beacon.Beacon_metrics, metric)
  241. }
  242. func twos_comp(inp string) int64 {
  243. i, _ := strconv.ParseInt("0x"+inp, 0, 64)
  244. return i - 256
  245. }