25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

138 lines
2.9 KiB

  1. package model
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "log/slog"
  7. "sync"
  8. )
  9. type ParserConfig struct {
  10. Length int `json:"length"`
  11. Offset int `json:"offset"`
  12. Order string `json:"order"`
  13. }
  14. type BeaconParser struct {
  15. CanParse func([]byte) bool
  16. configs map[string]ParserConfig
  17. }
  18. type ParserRegistry struct {
  19. ParserList map[string]BeaconParser
  20. rw sync.RWMutex
  21. }
  22. type Config struct {
  23. Name string `json:"name" gorm:"primaryKey"`
  24. Min int `json:"min"`
  25. Max int `json:"max"`
  26. Pattern []string `json:"pattern" gorm:"serializer:json"`
  27. Configs map[string]ParserConfig `json:"configs" gorm:"serializer:json"`
  28. }
  29. type KafkaParser struct {
  30. ID string
  31. Name string
  32. Config Config
  33. }
  34. func (pc ParserConfig) GetOrder() any {
  35. if pc.Order == "fixedpoint" {
  36. return "fixedpoint"
  37. }
  38. if pc.Order == "bigendian" {
  39. return binary.BigEndian
  40. }
  41. return binary.LittleEndian
  42. }
  43. func (p *ParserRegistry) Register(name string, c Config) {
  44. p.rw.Lock()
  45. defer p.rw.Unlock()
  46. b := BeaconParser{
  47. CanParse: func(ad []byte) bool {
  48. if len(ad) < 2 {
  49. msg := "Beacon advertisement is too short"
  50. slog.Error(msg)
  51. return false
  52. }
  53. return len(ad) >= c.Min && len(ad) <= c.Max && bytes.HasPrefix(ad[1:], c.GetPatternBytes())
  54. },
  55. configs: c.Configs,
  56. }
  57. p.ParserList[name] = b
  58. }
  59. func (p *ParserRegistry) Unregister(name string) {
  60. p.rw.Lock()
  61. delete(p.ParserList, name)
  62. p.rw.Unlock()
  63. }
  64. // TODO: change this to be dynamic, maybe event is interface with no predefined properties
  65. // or types
  66. func (b *BeaconParser) Parse(name string, ad []byte) (BeaconEvent, bool) {
  67. flag := false
  68. event := BeaconEvent{Type: name}
  69. if cfg, ok := b.configs["battery"]; ok {
  70. event.Battery = uint32(b.extract(ad, cfg).(uint16))
  71. flag = true
  72. }
  73. if cfg, ok := b.configs["accX"]; ok {
  74. val := b.extract(ad, cfg).(float64)
  75. event.AccX = int16(val)
  76. flag = true
  77. }
  78. if cfg, ok := b.configs["accY"]; ok {
  79. val := b.extract(ad, cfg).(float64)
  80. event.AccY = int16(val)
  81. flag = true
  82. }
  83. if cfg, ok := b.configs["accZ"]; ok {
  84. val := b.extract(ad, cfg).(float64)
  85. event.AccZ = int16(val)
  86. flag = true
  87. }
  88. if cfg, ok := b.configs["temperature"]; ok {
  89. val := b.extract(ad, cfg).(float64)
  90. event.Temperature = uint16(val)
  91. flag = true
  92. }
  93. return event, flag
  94. }
  95. func (b *BeaconParser) extract(ad []byte, pc ParserConfig) any {
  96. if len(ad) < pc.Offset+pc.Length {
  97. return 0
  98. }
  99. data := ad[pc.Offset : pc.Offset+pc.Length]
  100. if pc.Length == 1 {
  101. return uint16(data[0])
  102. }
  103. order := pc.GetOrder()
  104. if order == "fixedpoint" {
  105. val := int16(data[0])<<8 | int16(data[1])
  106. return float64(val) / 256.0
  107. }
  108. o := order.(binary.ByteOrder)
  109. return o.Uint16(data)
  110. }
  111. func (c Config) GetPatternBytes() []byte {
  112. res := make([]byte, len(c.Pattern))
  113. for i, s := range c.Pattern {
  114. fmt.Sscanf(s, "0x%02x", &res[i])
  115. }
  116. return res
  117. }