2026-01-28 10:27:25 -08:00
|
|
|
package artnet
|
|
|
|
|
|
|
|
|
|
import (
|
2026-01-30 09:13:57 -08:00
|
|
|
"context"
|
2026-01-28 10:27:25 -08:00
|
|
|
"net"
|
2026-01-30 09:13:57 -08:00
|
|
|
"syscall"
|
2026-01-28 10:27:25 -08:00
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Handler interface {
|
|
|
|
|
HandleDMX(src *net.UDPAddr, pkt *DMXPacket)
|
|
|
|
|
HandlePoll(src *net.UDPAddr, pkt *PollPacket)
|
|
|
|
|
HandlePollReply(src *net.UDPAddr, pkt *PollReplyPacket)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Receiver struct {
|
|
|
|
|
conn *net.UDPConn
|
|
|
|
|
handler Handler
|
|
|
|
|
done chan struct{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewReceiver(addr *net.UDPAddr, handler Handler) (*Receiver, error) {
|
|
|
|
|
conn, err := net.ListenUDP("udp4", addr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Receiver{
|
|
|
|
|
conn: conn,
|
|
|
|
|
handler: handler,
|
|
|
|
|
done: make(chan struct{}),
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewDefaultReceiver(handler Handler) (*Receiver, error) {
|
|
|
|
|
return NewReceiver(&net.UDPAddr{Port: Port}, handler)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 09:13:57 -08:00
|
|
|
func NewInterfaceReceiver(ifaceName string, handler Handler) (*Receiver, error) {
|
|
|
|
|
lc := net.ListenConfig{
|
|
|
|
|
Control: func(network, address string, c syscall.RawConn) error {
|
|
|
|
|
var err error
|
|
|
|
|
c.Control(func(fd uintptr) {
|
|
|
|
|
err = syscall.SetsockoptString(int(fd), syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, ifaceName)
|
|
|
|
|
})
|
|
|
|
|
return err
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conn, err := lc.ListenPacket(context.Background(), "udp4", ":6454")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Receiver{
|
|
|
|
|
conn: conn.(*net.UDPConn),
|
|
|
|
|
handler: handler,
|
|
|
|
|
done: make(chan struct{}),
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-28 10:27:25 -08:00
|
|
|
func (r *Receiver) Start() {
|
|
|
|
|
go r.loop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Receiver) Stop() {
|
|
|
|
|
close(r.done)
|
|
|
|
|
r.conn.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Receiver) Conn() *net.UDPConn {
|
|
|
|
|
return r.conn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Receiver) LocalAddr() net.Addr {
|
|
|
|
|
return r.conn.LocalAddr()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Receiver) SendTo(data []byte, addr *net.UDPAddr) error {
|
|
|
|
|
_, err := r.conn.WriteToUDP(data, addr)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Receiver) loop() {
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-r.done:
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
|
|
|
|
n, src, err := r.conn.ReadFromUDP(buf)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
select {
|
|
|
|
|
case <-r.done:
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.handle(src, buf[:n])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Receiver) handle(src *net.UDPAddr, data []byte) {
|
|
|
|
|
opCode, pkt, err := ParsePacket(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch opCode {
|
|
|
|
|
case OpDmx:
|
|
|
|
|
if dmx, ok := pkt.(*DMXPacket); ok {
|
|
|
|
|
r.handler.HandleDMX(src, dmx)
|
|
|
|
|
}
|
|
|
|
|
case OpPoll:
|
|
|
|
|
if poll, ok := pkt.(*PollPacket); ok {
|
|
|
|
|
r.handler.HandlePoll(src, poll)
|
|
|
|
|
}
|
|
|
|
|
case OpPollReply:
|
|
|
|
|
if reply, ok := pkt.(*PollReplyPacket); ok {
|
|
|
|
|
r.handler.HandlePollReply(src, reply)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|