168 lines
4.5 KiB
Go
168 lines
4.5 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"3588AdminBackend/internal/config"
|
|
"3588AdminBackend/internal/models"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const discoveryMagicV1 = "RK3588SYS_DISCOVERY_V1"
|
|
|
|
type DiscoveryService struct {
|
|
cfg *config.Config
|
|
registry *RegistryService
|
|
}
|
|
|
|
func NewDiscoveryService(cfg *config.Config, registry *RegistryService) *DiscoveryService {
|
|
return &DiscoveryService{
|
|
cfg: cfg,
|
|
registry: registry,
|
|
}
|
|
}
|
|
|
|
func (s *DiscoveryService) SearchDefault() ([]*models.Device, error) {
|
|
if s == nil || s.cfg == nil {
|
|
return nil, nil
|
|
}
|
|
timeoutMs := s.cfg.DiscoveryTimeoutMs
|
|
if timeoutMs <= 0 {
|
|
timeoutMs = 1200
|
|
}
|
|
return s.Search(timeoutMs)
|
|
}
|
|
|
|
func (s *DiscoveryService) Search(timeoutMs int) ([]*models.Device, error) {
|
|
reqID := uuid.NewString()
|
|
payload, _ := json.Marshal(map[string]interface{}{
|
|
"type": "discover",
|
|
"req_id": reqID,
|
|
"reply_port": 0,
|
|
})
|
|
msg := []byte(discoveryMagicV1 + "\n" + string(payload))
|
|
|
|
// Listen for replies
|
|
addr, err := net.ResolveUDPAddr("udp", ":0") // Random port for receiving
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn, err := net.ListenUDP("udp", addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Send Broadcast to all interfaces
|
|
interfaces, err := net.Interfaces()
|
|
if err == nil {
|
|
for _, iface := range interfaces {
|
|
if iface.Flags&net.FlagBroadcast != 0 && iface.Flags&net.FlagUp != 0 {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, addr := range addrs {
|
|
if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.To4() != nil {
|
|
// Calculate broadcast address
|
|
ip := ipnet.IP.To4()
|
|
mask := ipnet.Mask
|
|
broadcast := make(net.IP, len(ip))
|
|
for i := 0; i < len(ip); i++ {
|
|
broadcast[i] = ip[i] | ^mask[i]
|
|
}
|
|
broadcastAddr := &net.UDPAddr{
|
|
IP: broadcast,
|
|
Port: s.cfg.DiscoveryPort,
|
|
}
|
|
_, _ = conn.WriteToUDP(msg, broadcastAddr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stop := time.After(time.Duration(timeoutMs) * time.Millisecond)
|
|
found := make(map[string]*models.Device)
|
|
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
// Convert map to slice
|
|
list := make([]*models.Device, 0, len(found))
|
|
for _, d := range found {
|
|
list = append(list, d)
|
|
}
|
|
return list, nil
|
|
default:
|
|
conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
|
buf := make([]byte, 2048)
|
|
n, raddr, err := conn.ReadFromUDP(buf)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
text := strings.TrimSpace(string(buf[:n]))
|
|
lines := strings.SplitN(text, "\n", 3)
|
|
if len(lines) < 2 {
|
|
continue
|
|
}
|
|
if strings.TrimSpace(lines[0]) != discoveryMagicV1 {
|
|
continue
|
|
}
|
|
|
|
var reply struct {
|
|
Type string `json:"type"`
|
|
ReqID string `json:"req_id"`
|
|
DeviceID string `json:"device_id"`
|
|
DeviceName string `json:"device_name"`
|
|
Hostname string `json:"hostname"`
|
|
IP string `json:"ip"`
|
|
AgentPort int `json:"agent_port"`
|
|
MediaPort int `json:"media_port"`
|
|
Version string `json:"version"`
|
|
BuildID string `json:"build_id"`
|
|
GitSha string `json:"git_sha"`
|
|
UptimeSec int64 `json:"uptime_sec"`
|
|
InstanceName string `json:"instance_name"`
|
|
InstanceDisplayName string `json:"instance_display_name"`
|
|
ConfigID string `json:"config_id"`
|
|
ConfigVersion string `json:"config_version"`
|
|
Template string `json:"template"`
|
|
Profile string `json:"profile"`
|
|
Overlays []string `json:"overlays"`
|
|
}
|
|
if err := json.Unmarshal([]byte(strings.TrimSpace(lines[1])), &reply); err != nil {
|
|
continue
|
|
}
|
|
if reply.Type != "discover_reply" || reply.ReqID != reqID || reply.DeviceID == "" {
|
|
continue
|
|
}
|
|
|
|
dev := &models.Device{
|
|
DeviceID: reply.DeviceID,
|
|
DeviceName: reply.DeviceName,
|
|
Hostname: reply.Hostname,
|
|
IP: reply.IP,
|
|
AgentPort: reply.AgentPort,
|
|
MediaPort: reply.MediaPort,
|
|
Version: reply.Version,
|
|
BuildID: reply.BuildID,
|
|
GitSha: reply.GitSha,
|
|
UptimeSec: reply.UptimeSec,
|
|
InstanceName: reply.InstanceName,
|
|
InstanceDisplayName: reply.InstanceDisplayName,
|
|
}
|
|
if dev.IP == "" {
|
|
dev.IP = raddr.IP.String()
|
|
}
|
|
|
|
s.registry.UpdateDevice(dev)
|
|
found[dev.DeviceID] = dev
|
|
}
|
|
}
|
|
}
|