Files
sacn/discovery.go

125 lines
2.0 KiB
Go
Raw Normal View History

package sacn
import (
"net"
"sync"
"time"
)
type Source struct {
CID [16]byte
SourceName string
IP net.IP
Universes []uint16
LastSeen time.Time
}
type Discovery struct {
sources map[string]*Source
mu sync.RWMutex
onChange func(*Source)
done chan struct{}
}
func NewDiscovery() *Discovery {
return &Discovery{
sources: map[string]*Source{},
done: make(chan struct{}),
}
}
func (d *Discovery) SetOnChange(fn func(*Source)) {
d.onChange = fn
}
func (d *Discovery) HandleDiscoveryPacket(src *net.UDPAddr, pkt *DiscoveryPacket) {
d.mu.Lock()
defer d.mu.Unlock()
cidStr := FormatCID(pkt.CID)
source, exists := d.sources[cidStr]
if !exists {
source = &Source{
CID: pkt.CID,
}
d.sources[cidStr] = source
}
source.SourceName = pkt.SourceName
source.IP = src.IP
source.Universes = pkt.Universes
source.LastSeen = time.Now()
if d.onChange != nil {
d.onChange(source)
}
}
func (d *Discovery) GetSource(cid string) *Source {
d.mu.RLock()
defer d.mu.RUnlock()
return d.sources[cid]
}
func (d *Discovery) GetSourceByIP(ip net.IP) *Source {
d.mu.RLock()
defer d.mu.RUnlock()
for _, source := range d.sources {
if source.IP != nil && source.IP.Equal(ip) {
return source
}
}
return nil
}
func (d *Discovery) GetAllSources() []*Source {
d.mu.RLock()
defer d.mu.RUnlock()
result := make([]*Source, 0, len(d.sources))
for _, source := range d.sources {
result = append(result, source)
}
return result
}
func (d *Discovery) Expire() {
d.mu.Lock()
defer d.mu.Unlock()
cutoff := time.Now().Add(-60 * time.Second)
for cid, source := range d.sources {
if source.LastSeen.Before(cutoff) {
delete(d.sources, cid)
}
}
}
func (d *Discovery) StartCleanup() {
go d.cleanupLoop()
}
func (d *Discovery) Stop() {
select {
case <-d.done:
default:
close(d.done)
}
}
func (d *Discovery) cleanupLoop() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-d.done:
return
case <-ticker.C:
d.Expire()
}
}
}