Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

207 righe
4.1 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/binary"
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "strings"
  10. "github.com/AFASystems/presence/internal/pkg/common/appcontext"
  11. "github.com/AFASystems/presence/internal/pkg/config"
  12. "github.com/AFASystems/presence/internal/pkg/kafkaclient"
  13. "github.com/AFASystems/presence/internal/pkg/model"
  14. "github.com/segmentio/kafka-go"
  15. )
  16. func main() {
  17. // Load global context to init beacons and latest list
  18. appState := appcontext.NewAppState()
  19. cfg := config.Load()
  20. // Kafka reader for Raw MQTT beacons
  21. rawReader := kafkaclient.KafkaReader(cfg.KafkaURL, "rawbeacons", "gid-raw")
  22. defer rawReader.Close()
  23. // Kafka reader for API server updates
  24. apiReader := kafkaclient.KafkaReader(cfg.KafkaURL, "apibeacons", "gid-api")
  25. defer apiReader.Close()
  26. alertWriter := kafkaclient.KafkaWriter(cfg.KafkaURL, "alertbeacons")
  27. defer alertWriter.Close()
  28. fmt.Println("Decoder initialized, subscribed to Kafka topics")
  29. chRaw := make(chan model.BeaconAdvertisement, 2000)
  30. chApi := make(chan model.ApiUpdate, 2000)
  31. go kafkaclient.Consume(rawReader, chRaw)
  32. go kafkaclient.Consume(apiReader, chApi)
  33. for {
  34. select {
  35. case msg := <-chRaw:
  36. processIncoming(msg, appState, alertWriter)
  37. case msg := <-chApi:
  38. switch msg.Method {
  39. case "POST":
  40. id := msg.Beacon.ID
  41. appState.AddBeaconToLookup(id)
  42. case "DELETE":
  43. fmt.Println("Incoming delete message")
  44. }
  45. }
  46. }
  47. }
  48. func processIncoming(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer) {
  49. id := adv.MAC
  50. ok := appState.BeaconExists(id)
  51. if !ok {
  52. return
  53. }
  54. err := decodeBeacon(adv, appState, writer)
  55. if err != nil {
  56. fmt.Println("error in decoding")
  57. return
  58. }
  59. }
  60. func decodeBeacon(adv model.BeaconAdvertisement, appState *appcontext.AppState, writer *kafka.Writer) error {
  61. beacon := strings.TrimSpace(adv.Data)
  62. id := adv.MAC
  63. if beacon == "" {
  64. return nil // How to return error?, do I even need to return error
  65. }
  66. b, err := hex.DecodeString(beacon)
  67. if err != nil {
  68. return err
  69. }
  70. // check for flag byte, if first AD structure is flag bytes, remove it
  71. if len(b) > 1 && b[1] == 0x01 {
  72. l := int(b[0]) // length of AD structure
  73. if 1+l <= len(b) {
  74. b = b[1+l:]
  75. }
  76. }
  77. adStructureIndeces := ParseADFast(b)
  78. event := model.BeaconEvent{}
  79. for _, r := range adStructureIndeces {
  80. ad := b[r[0]:r[1]]
  81. if checkIngics(ad) {
  82. event = parseIngicsState(ad)
  83. event.ID = id
  84. event.Name = id
  85. break
  86. } else if checkEddystoneTLM(ad) {
  87. event = parseEddystoneState(ad)
  88. event.ID = id
  89. event.Name = id
  90. break
  91. } else if checkMinewB7(ad) {
  92. fmt.Println("Minew B7 vendor format")
  93. break
  94. }
  95. }
  96. if event.ID != "" {
  97. prevEvent, ok := appState.GetBeaconEvent(id)
  98. appState.UpdateBeaconEvent(id, event)
  99. if ok && bytes.Equal(prevEvent.Hash(), event.Hash()) {
  100. return nil
  101. }
  102. eMsg, err := json.Marshal(event)
  103. if err != nil {
  104. return err
  105. }
  106. err = writer.WriteMessages(context.Background(), kafka.Message{
  107. Value: eMsg,
  108. })
  109. if err != nil {
  110. return err
  111. }
  112. fmt.Println("Message sent")
  113. }
  114. return nil
  115. }
  116. func checkIngics(ad []byte) bool {
  117. if len(ad) >= 6 &&
  118. ad[1] == 0xFF &&
  119. ad[2] == 0x59 &&
  120. ad[3] == 0x00 &&
  121. ad[4] == 0x80 &&
  122. ad[5] == 0xBC {
  123. return true
  124. }
  125. return false
  126. }
  127. func parseIngicsState(ad []byte) model.BeaconEvent {
  128. return model.BeaconEvent{
  129. Battery: uint32(binary.LittleEndian.Uint16(ad[6:8])),
  130. Event: int(ad[8]),
  131. Type: "Ingics",
  132. }
  133. }
  134. func checkEddystoneTLM(ad []byte) bool {
  135. if len(ad) >= 4 &&
  136. ad[1] == 0x16 &&
  137. ad[2] == 0xAA &&
  138. ad[3] == 0xFE &&
  139. ad[4] == 0x20 {
  140. return true
  141. }
  142. return false
  143. }
  144. func parseEddystoneState(ad []byte) model.BeaconEvent {
  145. return model.BeaconEvent{
  146. Battery: uint32(binary.BigEndian.Uint16(ad[6:8])),
  147. Type: "Eddystone",
  148. }
  149. }
  150. // I dont think this is always true, but for testing is ok
  151. func checkMinewB7(ad []byte) bool {
  152. if len(ad) >= 4 &&
  153. ad[1] == 0x16 &&
  154. ad[2] == 0xE1 &&
  155. ad[3] == 0xFF {
  156. return true
  157. }
  158. return false
  159. }
  160. func ParseADFast(b []byte) [][2]int {
  161. var res [][2]int
  162. i := 0
  163. for i < len(b) {
  164. l := int(b[i])
  165. if l == 0 || i+1+l > len(b) {
  166. break
  167. }
  168. res = append(res, [2]int{i, i + 1 + l})
  169. i += 1 + l
  170. }
  171. return res
  172. }