Add sACN universe discovery announcements
This commit is contained in:
6
main.go
6
main.go
@@ -107,13 +107,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer artSender.Close()
|
defer artSender.Close()
|
||||||
|
|
||||||
// Create sACN sender
|
|
||||||
sacnSender, err := sacn.NewSender("artmap", *sacnInterface)
|
sacnSender, err := sacn.NewSender("artmap", *sacnInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("sacn sender error: %v", err)
|
log.Fatalf("sacn sender error: %v", err)
|
||||||
}
|
}
|
||||||
defer sacnSender.Close()
|
defer sacnSender.Close()
|
||||||
|
|
||||||
|
for _, u := range engine.DestSACNUniverses() {
|
||||||
|
sacnSender.RegisterUniverse(u)
|
||||||
|
}
|
||||||
|
sacnSender.StartDiscovery()
|
||||||
|
|
||||||
// Create discovery
|
// Create discovery
|
||||||
destNums := engine.DestArtNetUniverses()
|
destNums := engine.DestArtNetUniverses()
|
||||||
inputUnivs := make([]artnet.Universe, len(destNums))
|
inputUnivs := make([]artnet.Universe, len(destNums))
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ func (e *Engine) SourceArtNetUniverses() []uint16 {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// DestArtNetUniverses returns destination ArtNet universe numbers (for discovery)
|
|
||||||
func (e *Engine) DestArtNetUniverses() []uint16 {
|
func (e *Engine) DestArtNetUniverses() []uint16 {
|
||||||
seen := make(map[uint16]bool)
|
seen := make(map[uint16]bool)
|
||||||
for _, m := range e.mappings {
|
for _, m := range e.mappings {
|
||||||
@@ -106,3 +105,17 @@ func (e *Engine) DestArtNetUniverses() []uint16 {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) DestSACNUniverses() []uint16 {
|
||||||
|
seen := make(map[uint16]bool)
|
||||||
|
for _, m := range e.mappings {
|
||||||
|
if m.To.Protocol == config.ProtocolSACN {
|
||||||
|
seen[m.To.Number] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := make([]uint16, 0, len(seen))
|
||||||
|
for u := range seen {
|
||||||
|
result = append(result, u)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import (
|
|||||||
const (
|
const (
|
||||||
Port = 5568
|
Port = 5568
|
||||||
|
|
||||||
// ACN packet identifiers
|
ACNPacketIdentifier = 0x41534300
|
||||||
ACNPacketIdentifier = 0x41534300 // "ASC\0" + more bytes
|
|
||||||
|
|
||||||
// Vector values
|
VectorRootE131Data = 0x00000004
|
||||||
VectorRootE131Data = 0x00000004
|
VectorRootE131Extended = 0x00000008
|
||||||
VectorE131DataPacket = 0x00000002
|
VectorE131DataPacket = 0x00000002
|
||||||
VectorDMPSetProperty = 0x02
|
VectorE131Discovery = 0x00000002
|
||||||
|
VectorDMPSetProperty = 0x02
|
||||||
|
VectorUniverseDiscovery = 0x00000001
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -92,11 +93,49 @@ func BuildDataPacket(universe uint16, sequence uint8, sourceName string, cid [16
|
|||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// MulticastAddr returns the multicast address for a given universe
|
|
||||||
func MulticastAddr(universe uint16) *net.UDPAddr {
|
func MulticastAddr(universe uint16) *net.UDPAddr {
|
||||||
// 239.255.{universe_high}.{universe_low}
|
|
||||||
return &net.UDPAddr{
|
return &net.UDPAddr{
|
||||||
IP: net.IPv4(239, 255, byte(universe>>8), byte(universe&0xff)),
|
IP: net.IPv4(239, 255, byte(universe>>8), byte(universe&0xff)),
|
||||||
Port: Port,
|
Port: Port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DiscoveryAddr = &net.UDPAddr{
|
||||||
|
IP: net.IPv4(239, 255, 250, 214),
|
||||||
|
Port: Port,
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildDiscoveryPacket(sourceName string, cid [16]byte, page, lastPage uint8, universes []uint16) []byte {
|
||||||
|
universeCount := len(universes)
|
||||||
|
if universeCount > 512 {
|
||||||
|
universeCount = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
pktLen := 120 + universeCount*2
|
||||||
|
buf := make([]byte, pktLen)
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(buf[0:2], 0x0010)
|
||||||
|
binary.BigEndian.PutUint16(buf[2:4], 0x0000)
|
||||||
|
copy(buf[4:16], packetIdentifier[:])
|
||||||
|
rootLen := pktLen - 16
|
||||||
|
binary.BigEndian.PutUint16(buf[16:18], 0x7000|uint16(rootLen))
|
||||||
|
binary.BigEndian.PutUint32(buf[18:22], VectorRootE131Extended)
|
||||||
|
copy(buf[22:38], cid[:])
|
||||||
|
|
||||||
|
framingLen := pktLen - 38
|
||||||
|
binary.BigEndian.PutUint16(buf[38:40], 0x7000|uint16(framingLen))
|
||||||
|
binary.BigEndian.PutUint32(buf[40:44], VectorE131Discovery)
|
||||||
|
copy(buf[44:108], sourceName)
|
||||||
|
binary.BigEndian.PutUint32(buf[108:112], 0)
|
||||||
|
|
||||||
|
discoveryLen := pktLen - 112
|
||||||
|
binary.BigEndian.PutUint16(buf[112:114], 0x7000|uint16(discoveryLen))
|
||||||
|
binary.BigEndian.PutUint32(buf[114:118], VectorUniverseDiscovery)
|
||||||
|
buf[118] = page
|
||||||
|
buf[119] = lastPage
|
||||||
|
for i := 0; i < universeCount; i++ {
|
||||||
|
binary.BigEndian.PutUint16(buf[120+i*2:122+i*2], universes[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,18 +3,21 @@ package sacn
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sender sends sACN (E1.31) packets
|
|
||||||
type Sender struct {
|
type Sender struct {
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
sourceName string
|
sourceName string
|
||||||
cid [16]byte
|
cid [16]byte
|
||||||
sequences map[uint16]uint8
|
sequences map[uint16]uint8
|
||||||
seqMu sync.Mutex
|
seqMu sync.Mutex
|
||||||
|
universes map[uint16]bool
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSender creates a new sACN sender
|
// NewSender creates a new sACN sender
|
||||||
@@ -45,6 +48,8 @@ func NewSender(sourceName string, ifaceName string) (*Sender, error) {
|
|||||||
sourceName: sourceName,
|
sourceName: sourceName,
|
||||||
cid: cid,
|
cid: cid,
|
||||||
sequences: make(map[uint16]uint8),
|
sequences: make(map[uint16]uint8),
|
||||||
|
universes: make(map[uint16]bool),
|
||||||
|
done: make(chan struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +80,65 @@ func (s *Sender) SendDMXUnicast(addr *net.UDPAddr, universe uint16, data []byte)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the sender
|
|
||||||
func (s *Sender) Close() error {
|
func (s *Sender) Close() error {
|
||||||
|
select {
|
||||||
|
case <-s.done:
|
||||||
|
default:
|
||||||
|
close(s.done)
|
||||||
|
}
|
||||||
return s.conn.Close()
|
return s.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Sender) RegisterUniverse(universe uint16) {
|
||||||
|
s.seqMu.Lock()
|
||||||
|
s.universes[universe] = true
|
||||||
|
s.seqMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sender) StartDiscovery() {
|
||||||
|
go s.discoveryLoop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sender) discoveryLoop() {
|
||||||
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
s.sendDiscovery()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.done:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
s.sendDiscovery()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sender) sendDiscovery() {
|
||||||
|
s.seqMu.Lock()
|
||||||
|
universes := make([]uint16, 0, len(s.universes))
|
||||||
|
for u := range s.universes {
|
||||||
|
universes = append(universes, u)
|
||||||
|
}
|
||||||
|
s.seqMu.Unlock()
|
||||||
|
|
||||||
|
if len(universes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(universes, func(i, j int) bool { return universes[i] < universes[j] })
|
||||||
|
|
||||||
|
const maxPerPage = 512
|
||||||
|
totalPages := (len(universes) + maxPerPage - 1) / maxPerPage
|
||||||
|
|
||||||
|
for page := 0; page < totalPages; page++ {
|
||||||
|
start := page * maxPerPage
|
||||||
|
end := start + maxPerPage
|
||||||
|
if end > len(universes) {
|
||||||
|
end = len(universes)
|
||||||
|
}
|
||||||
|
pkt := BuildDiscoveryPacket(s.sourceName, s.cid, uint8(page), uint8(totalPages-1), universes[start:end])
|
||||||
|
s.conn.WriteToUDP(pkt, DiscoveryAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user