package service import ( "context" "encoding/json" "fmt" "log/slog" "slices" "strings" "time" "github.com/AFASystems/presence/internal/pkg/model" "github.com/segmentio/kafka-go" "gorm.io/gorm" ) // KafkaWriter defines the interface for writing Kafka messages (allows mocking in tests) type KafkaWriter interface { WriteMessages(ctx context.Context, msgs ...kafka.Message) error } func LocationToBeaconService(msg model.HTTPLocation, db *gorm.DB, writer KafkaWriter, ctx context.Context) { if msg.ID == "" { msg := "empty ID" slog.Error(msg) return } var zones []model.TrackerZones if err := db.Select("zoneList").Where("tracker = ?", msg.ID).Find(&zones).Error; err != nil { msg := fmt.Sprintf("Error in selecting zones: %v", err) slog.Error(msg) return } var tracker model.Tracker if err := db.Where("id = ?", msg.ID).Find(&tracker).Error; err != nil { msg := fmt.Sprintf("Error in selecting tracker: %v", err) slog.Error(msg) return } var allowedZones []string for _, z := range zones { allowedZones = append(allowedZones, z.ZoneList...) } var gw model.Gateway mac := formatMac(msg.Location) if err := db.Select("id").Where("mac = ?", mac).First(&gw).Error; err != nil { msg := fmt.Sprintf("Gateway not found for MAC: %s", mac) slog.Error(msg) return } if len(allowedZones) != 0 && !slices.Contains(allowedZones, gw.ID) { alert := model.Alert{ ID: msg.ID, Type: "Restricted zone", Value: gw.ID, } eMsg, err := json.Marshal(alert) if err != nil { msg := "Error in marshaling" slog.Error(msg) return } else { msg := kafka.Message{ Value: eMsg, } writer.WriteMessages(ctx, msg) return } } // status, subject, subject name? if err := db.Create(&model.Tracks{UUID: msg.ID, Timestamp: time.Now(), Gateway: gw.ID, GatewayMac: gw.MAC, Tracker: msg.ID, Floor: gw.Floor, Building: gw.Building, TrackerMac: tracker.MAC, Signal: msg.RSSI}).Error; err != nil { msg := fmt.Sprintf("Error in saving distance for beacon: %v", err) slog.Error(msg) return } if err := db.Updates(&model.Tracker{ID: msg.ID, Location: gw.ID, Distance: msg.Distance, X: gw.X, Y: gw.Y}).Error; err != nil { msg := fmt.Sprintf("Error in saving distance for beacon: %v", err) slog.Error(msg) return } } func formatMac(MAC string) string { var res strings.Builder for i := 0; i < len(MAC); i += 2 { if i > 0 { res.WriteByte(':') } end := min(i+2, len(MAC)) res.WriteString(MAC[i:end]) } return res.String() }