93 lines
2.2 KiB
Go
93 lines
2.2 KiB
Go
|
|
package artnet
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net"
|
||
|
|
"sync"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Sender sends ArtNet packets
|
||
|
|
type Sender struct {
|
||
|
|
conn *net.UDPConn
|
||
|
|
broadcastAddr *net.UDPAddr
|
||
|
|
sequences map[Universe]uint8
|
||
|
|
seqMu sync.Mutex
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewSender creates a new ArtNet sender
|
||
|
|
func NewSender(broadcastAddr string) (*Sender, error) {
|
||
|
|
// Create a UDP socket for sending
|
||
|
|
conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Enable broadcast
|
||
|
|
if err := conn.SetWriteBuffer(65536); err != nil {
|
||
|
|
conn.Close()
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
broadcast, err := net.ResolveUDPAddr("udp4", broadcastAddr+":"+string(rune(Port)))
|
||
|
|
if err != nil {
|
||
|
|
// Try parsing as IP:Port
|
||
|
|
broadcast, err = net.ResolveUDPAddr("udp4", broadcastAddr)
|
||
|
|
if err != nil {
|
||
|
|
broadcast = &net.UDPAddr{
|
||
|
|
IP: net.ParseIP(broadcastAddr),
|
||
|
|
Port: Port,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return &Sender{
|
||
|
|
conn: conn,
|
||
|
|
broadcastAddr: broadcast,
|
||
|
|
sequences: make(map[Universe]uint8),
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendDMX sends a DMX packet to a specific address
|
||
|
|
func (s *Sender) SendDMX(addr *net.UDPAddr, universe Universe, data []byte) error {
|
||
|
|
s.seqMu.Lock()
|
||
|
|
seq := s.sequences[universe]
|
||
|
|
seq++
|
||
|
|
if seq == 0 {
|
||
|
|
seq = 1 // Skip 0
|
||
|
|
}
|
||
|
|
s.sequences[universe] = seq
|
||
|
|
s.seqMu.Unlock()
|
||
|
|
|
||
|
|
pkt := BuildDMXPacket(universe, seq, data)
|
||
|
|
_, err := s.conn.WriteToUDP(pkt, addr)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendDMXBroadcast sends a DMX packet to the broadcast address
|
||
|
|
func (s *Sender) SendDMXBroadcast(universe Universe, data []byte) error {
|
||
|
|
return s.SendDMX(s.broadcastAddr, universe, data)
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendPoll sends an ArtPoll packet to the broadcast address
|
||
|
|
func (s *Sender) SendPoll() error {
|
||
|
|
pkt := BuildPollPacket()
|
||
|
|
_, err := s.conn.WriteToUDP(pkt, s.broadcastAddr)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendPollReply sends an ArtPollReply to a specific address
|
||
|
|
func (s *Sender) SendPollReply(addr *net.UDPAddr, localIP [4]byte, shortName, longName string, universes []Universe) error {
|
||
|
|
pkt := BuildPollReplyPacket(localIP, shortName, longName, universes)
|
||
|
|
_, err := s.conn.WriteToUDP(pkt, addr)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Close closes the sender
|
||
|
|
func (s *Sender) Close() error {
|
||
|
|
return s.conn.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
// BroadcastAddr returns the configured broadcast address
|
||
|
|
func (s *Sender) BroadcastAddr() *net.UDPAddr {
|
||
|
|
return s.broadcastAddr
|
||
|
|
}
|