144 lines
2.4 KiB
Go
144 lines
2.4 KiB
Go
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/tatsushid/go-fastping"
|
|
)
|
|
|
|
// Art-Net constants
|
|
const (
|
|
artPort = 6454
|
|
)
|
|
|
|
// Art-Net Interface
|
|
type Bus struct {
|
|
ip string `yaml:"ip"`
|
|
port int `yaml:"port"`
|
|
Devices []*Device `yaml:"devices"`
|
|
Data *DMX
|
|
}
|
|
|
|
// adds new Art-Net interface to driver port 0 = 6454 (default art-net)
|
|
func NewBus(ip string, port int) *Bus {
|
|
if port == 0 {
|
|
port = artPort
|
|
}
|
|
|
|
i := Bus{
|
|
ip: ip,
|
|
port: port,
|
|
Data: NewDMXUniverse(),
|
|
}
|
|
return &i
|
|
}
|
|
|
|
// adds new dmx device to interface
|
|
func (i *Bus) AddDevice(address uint, channels uint) (*Device, error) {
|
|
d := NewDevice(address, channels, i.Data)
|
|
i.Devices = append(i.Devices, d)
|
|
return d, nil
|
|
}
|
|
|
|
// start polling dmx data in milliseconds 0 = aprox. 44Hertz
|
|
func (i *Bus) Poll(interval time.Duration) error {
|
|
if interval == 0 {
|
|
interval = 23
|
|
}
|
|
|
|
// Send packet over UDP
|
|
conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
|
|
IP: net.ParseIP(i.ip),
|
|
Port: i.port,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
|
|
var errCount int
|
|
for {
|
|
go func() {
|
|
for {
|
|
if reached, _ := isUDPReachable(i.ip); !reached {
|
|
if errCount > 20 {
|
|
break
|
|
} else {
|
|
errCount += 1
|
|
return
|
|
}
|
|
} else {
|
|
errCount = 0
|
|
break
|
|
}
|
|
}
|
|
}()
|
|
|
|
_, err = conn.Write(NewArtNetPackage(i.Data))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if errCount > 5 {
|
|
return fmt.Errorf("device not reachable")
|
|
}
|
|
time.Sleep(23 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
// start polling dmx data in milliseconds 0 = aprox. 44Hertz
|
|
func (i *Bus) SendData() error {
|
|
// Send packet over UDP
|
|
conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
|
|
IP: net.ParseIP(i.ip),
|
|
Port: i.port,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
|
|
errChan := make(chan error)
|
|
go func() {
|
|
if reached, _ := isUDPReachable(i.ip); !reached {
|
|
errChan <- fmt.Errorf("device not reachable")
|
|
return
|
|
}
|
|
errChan <- nil
|
|
}()
|
|
|
|
err = <-errChan
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = conn.Write(NewArtNetPackage(i.Data))
|
|
|
|
return err
|
|
}
|
|
|
|
const (
|
|
protocolICMP = 1
|
|
)
|
|
|
|
func isUDPReachable(ip string) (recieved bool, err error) {
|
|
p := fastping.NewPinger()
|
|
ra, err := net.ResolveIPAddr("ip4:icmp", ip)
|
|
if err != nil {
|
|
return
|
|
}
|
|
p.AddIPAddr(ra)
|
|
p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
|
|
recieved = true
|
|
return
|
|
}
|
|
p.OnIdle = func() {}
|
|
|
|
err = p.Run()
|
|
return
|
|
}
|