Skip to content

Commit a755f62

Browse files
committed
Migrate to new multicast server with more robust interface selection
1 parent a19cd64 commit a755f62

File tree

6 files changed

+158
-139
lines changed

6 files changed

+158
-139
lines changed

cmd/ssl-game-controller/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var trackerAddress = flag.String("trackerAddress", "", "The address (ip+port) fr
1919
var publishAddress = flag.String("publishAddress", "", "The address (ip+port) to which referee command should be sent")
2020
var timeAcquisitionMode = flag.String("timeAcquisitionMode", "", "The time acquisitionMode to use (system, ci, vision)")
2121
var skipInterfaces = flag.String("skipInterfaces", "", "Comma separated list of interface names to ignore when receiving multicast packets")
22+
var verbose = flag.Bool("verbose", false, "Verbose output")
2223

2324
const configFileName = "config/ssl-game-controller.yaml"
2425

@@ -54,6 +55,7 @@ func setupGameController() {
5455
}
5556

5657
gameController := gc.NewGameController(cfg)
58+
gameController.SetVerbose(*verbose)
5759
gameController.Start()
5860

5961
sigs := make(chan os.Signal, 1)

internal/app/gc/gc.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,18 @@ func NewGameController(cfg config.Controller) (c *GameController) {
4949
c.ciServer = ci.NewServer(cfg.Server.Ci.Address)
5050
c.visionReceiver = vision.NewReceiver(cfg.Network.VisionAddress)
5151
c.visionReceiver.GeometryCallback = c.gcEngine.ProcessGeometry
52-
c.visionReceiver.MulticastReceiver.SkipInterfaces = cfg.Network.SkipInterfaces
52+
c.visionReceiver.MulticastServer.SkipInterfaces = cfg.Network.SkipInterfaces
5353
c.trackerReceiver = tracker.NewReceiver(cfg.Network.TrackerAddress)
5454
c.trackerReceiver.Callback = c.gcEngine.ProcessTrackerFrame
55-
c.trackerReceiver.MulticastReceiver.SkipInterfaces = cfg.Network.SkipInterfaces
55+
c.trackerReceiver.MulticastServer.SkipInterfaces = cfg.Network.SkipInterfaces
5656
return
5757
}
5858

59+
func (c *GameController) SetVerbose(verbose bool) {
60+
c.visionReceiver.MulticastServer.Verbose = verbose
61+
c.trackerReceiver.MulticastServer.Verbose = verbose
62+
}
63+
5964
// Start starts all go routines
6065
func (c *GameController) Start() {
6166

internal/app/tracker/trackerReceiver.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,29 @@ import (
88
)
99

1010
type Receiver struct {
11-
address string
12-
Callback func(*TrackerWrapperPacket)
13-
mutex sync.Mutex
14-
MulticastReceiver *sslnet.MulticastReceiver
11+
address string
12+
Callback func(*TrackerWrapperPacket)
13+
mutex sync.Mutex
14+
MulticastServer *sslnet.MulticastServer
1515
}
1616

1717
// NewReceiver creates a new receiver
1818
func NewReceiver(address string) (v *Receiver) {
1919
v = new(Receiver)
2020
v.address = address
2121
v.Callback = func(*TrackerWrapperPacket) {}
22-
v.MulticastReceiver = sslnet.NewMulticastReceiver(v.consumeData)
22+
v.MulticastServer = sslnet.NewMulticastServer(v.consumeData)
2323
return
2424
}
2525

2626
// Start starts the receiver
2727
func (v *Receiver) Start() {
28-
v.MulticastReceiver.Start(v.address)
28+
v.MulticastServer.Start(v.address)
2929
}
3030

3131
// Stop stops the receiver
3232
func (v *Receiver) Stop() {
33-
v.MulticastReceiver.Stop()
33+
v.MulticastServer.Stop()
3434
}
3535

3636
func (v *Receiver) consumeData(data []byte) {

internal/app/vision/visionReceiver.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type Receiver struct {
1515
GeometryCallback func(*SSL_GeometryData)
1616
latestTimestamp time.Time
1717
mutex sync.Mutex
18-
MulticastReceiver *sslnet.MulticastReceiver
18+
MulticastServer *sslnet.MulticastServer
1919
}
2020

2121
// NewReceiver creates a new receiver
@@ -24,18 +24,18 @@ func NewReceiver(address string) (v *Receiver) {
2424
v.address = address
2525
v.DetectionCallback = func(*SSL_DetectionFrame) {}
2626
v.GeometryCallback = func(data *SSL_GeometryData) {}
27-
v.MulticastReceiver = sslnet.NewMulticastReceiver(v.consumeData)
27+
v.MulticastServer = sslnet.NewMulticastServer(v.consumeData)
2828
return
2929
}
3030

3131
// Start starts the receiver
3232
func (v *Receiver) Start() {
33-
v.MulticastReceiver.Start(v.address)
33+
v.MulticastServer.Start(v.address)
3434
}
3535

3636
// Stop stops the receiver
3737
func (v *Receiver) Stop() {
38-
v.MulticastReceiver.Stop()
38+
v.MulticastServer.Stop()
3939
}
4040

4141
func (v *Receiver) consumeData(data []byte) {

pkg/sslnet/multicast_receiver.go

Lines changed: 0 additions & 126 deletions
This file was deleted.

pkg/sslnet/multicast_server.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package sslnet
2+
3+
import (
4+
"log"
5+
"net"
6+
"sync"
7+
"time"
8+
)
9+
10+
const maxDatagramSize = 8192
11+
12+
type MulticastServer struct {
13+
connection *net.UDPConn
14+
running bool
15+
consumer func([]byte)
16+
mutex sync.Mutex
17+
SkipInterfaces []string
18+
Verbose bool
19+
}
20+
21+
func NewMulticastServer(consumer func([]byte)) (r *MulticastServer) {
22+
r = new(MulticastServer)
23+
r.consumer = consumer
24+
return
25+
}
26+
27+
func (r *MulticastServer) Start(multicastAddress string) {
28+
r.running = true
29+
go r.receive(multicastAddress)
30+
}
31+
32+
func (r *MulticastServer) Stop() {
33+
r.mutex.Lock()
34+
defer r.mutex.Unlock()
35+
r.running = false
36+
if err := r.connection.Close(); err != nil {
37+
log.Println("Could not close connection: ", err)
38+
}
39+
}
40+
41+
func (r *MulticastServer) receive(multicastAddress string) {
42+
var currentIfiIdx = 0
43+
for r.isRunning() {
44+
ifis := r.interfaces()
45+
currentIfiIdx = currentIfiIdx % len(ifis)
46+
ifi := ifis[currentIfiIdx]
47+
r.receiveOnInterface(multicastAddress, ifi)
48+
currentIfiIdx++
49+
if currentIfiIdx >= len(ifis) {
50+
// cycled though all interfaces once, make a short break to avoid producing endless log messages
51+
time.Sleep(1 * time.Second)
52+
}
53+
}
54+
}
55+
56+
func (r *MulticastServer) isRunning() bool {
57+
r.mutex.Lock()
58+
defer r.mutex.Unlock()
59+
return r.running
60+
}
61+
62+
func (r *MulticastServer) interfaces() (interfaces []net.Interface) {
63+
interfaces = []net.Interface{}
64+
ifis, err := net.Interfaces()
65+
if err != nil {
66+
log.Println("Could not get available interfaces: ", err)
67+
}
68+
for _, ifi := range ifis {
69+
if ifi.Flags&net.FlagMulticast == 0 || // No multicast support
70+
r.skipInterface(ifi.Name) {
71+
continue
72+
}
73+
interfaces = append(interfaces, ifi)
74+
}
75+
return
76+
}
77+
78+
func (r *MulticastServer) skipInterface(ifiName string) bool {
79+
for _, skipIfi := range r.SkipInterfaces {
80+
if skipIfi == ifiName {
81+
return true
82+
}
83+
}
84+
return false
85+
}
86+
87+
func (r *MulticastServer) receiveOnInterface(multicastAddress string, ifi net.Interface) {
88+
addr, err := net.ResolveUDPAddr("udp", multicastAddress)
89+
if err != nil {
90+
log.Printf("Could resolve multicast address %v: %v", multicastAddress, err)
91+
return
92+
}
93+
94+
r.connection, err = net.ListenMulticastUDP("udp", &ifi, addr)
95+
if err != nil {
96+
log.Printf("Could not listen at %v: %v", multicastAddress, err)
97+
return
98+
}
99+
100+
if err := r.connection.SetReadBuffer(maxDatagramSize); err != nil {
101+
log.Println("Could not set read buffer: ", err)
102+
}
103+
104+
if r.Verbose {
105+
log.Printf("Listening on %s (%s)", multicastAddress, ifi.Name)
106+
}
107+
108+
first := true
109+
data := make([]byte, maxDatagramSize)
110+
for {
111+
if err := r.connection.SetDeadline(time.Now().Add(300 * time.Millisecond)); err != nil {
112+
log.Println("Could not set deadline on connection: ", err)
113+
}
114+
n, _, err := r.connection.ReadFromUDP(data)
115+
if err != nil {
116+
if r.Verbose {
117+
log.Println("ReadFromUDP failed:", err)
118+
}
119+
break
120+
}
121+
122+
if first && r.Verbose {
123+
log.Printf("Got first data packets from %s (%s)", multicastAddress, ifi.Name)
124+
first = false
125+
}
126+
127+
r.consumer(data[:n])
128+
}
129+
130+
if r.Verbose {
131+
log.Printf("Stop listening on %s (%s)", multicastAddress, ifi.Name)
132+
}
133+
134+
if err := r.connection.Close(); err != nil {
135+
log.Println("Could not close listener: ", err)
136+
}
137+
return
138+
}

0 commit comments

Comments
 (0)