Skip to content

Commit 69f0b6a

Browse files
committed
Publish referee state with protobuf
1 parent 499764b commit 69f0b6a

File tree

5 files changed

+200
-16
lines changed

5 files changed

+200
-16
lines changed

config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
publish:
2-
address: :224.5.23.1
3-
port: 10003
2+
address: 224.5.23.1:10003
43
global:
54
yellow-card-duration: 2m
65
normal:

refBox.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,26 @@ const lastStateFileName = logDir + "/lastState.json"
1313
const configFileName = "config.yaml"
1414

1515
type RefBox struct {
16-
State *RefBoxState
17-
timer Timer
18-
MatchTimeStart time.Time
19-
newEventChannel chan RefBoxEvent
20-
StateHistory []RefBoxState
21-
Config RefBoxConfig
22-
stateHistoryFile *os.File
23-
lastStateFile *os.File
24-
StageTimes map[RefBoxStage]time.Duration
16+
State *RefBoxState
17+
timer Timer
18+
MatchTimeStart time.Time
19+
notifyUpdateState chan struct{}
20+
StateHistory []RefBoxState
21+
Config RefBoxConfig
22+
stateHistoryFile *os.File
23+
lastStateFile *os.File
24+
StageTimes map[RefBoxStage]time.Duration
25+
Publisher RefBoxPublisher
2526
}
2627

2728
func NewRefBox() (refBox *RefBox) {
2829
refBox = new(RefBox)
2930
refBox.timer = NewTimer()
30-
refBox.newEventChannel = make(chan RefBoxEvent)
31+
refBox.notifyUpdateState = make(chan struct{})
3132
refBox.MatchTimeStart = time.Unix(0, 0)
3233
refBox.Config = LoadRefBoxConfig(configFileName)
3334
refBox.State = NewRefBoxState(refBox.Config)
35+
refBox.Publisher = NewRefBoxPublisher(refBox.Config.Publish.Address)
3436

3537
return
3638
}
@@ -53,7 +55,7 @@ func (r *RefBox) Run() (err error) {
5355
for {
5456
r.timer.WaitTillNextFullSecond()
5557
r.Tick()
56-
r.newEventChannel <- RefBoxEvent{}
58+
r.Update(nil)
5759
}
5860
}()
5961
return nil
@@ -100,6 +102,11 @@ func (r *RefBox) Tick() {
100102
}
101103
}
102104

105+
func (r *RefBox) Update(command *RefBoxEventCommand) {
106+
r.notifyUpdateState <- struct{}{}
107+
refBox.Publisher.Publish(refBox.State, command)
108+
}
109+
103110
func (r *RefBox) SaveState() {
104111
r.SaveLatestState()
105112
r.SaveStateHistory()

refBoxConfig.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ type RefBoxConfigGlobal struct {
2121

2222
type RefBoxConfigPublish struct {
2323
Address string `yaml:"address"`
24-
Port int `yaml:"port"`
2524
}
2625

2726
type RefBoxConfig struct {

refBoxPublisher.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package main
2+
3+
import (
4+
"github.com/RoboCup-SSL/ssl-go-tools/sslproto"
5+
"github.com/golang/protobuf/proto"
6+
"log"
7+
"net"
8+
"time"
9+
)
10+
11+
const maxDatagramSize = 8192
12+
13+
type RefBoxPublisher struct {
14+
Conn *net.UDPConn
15+
Message sslproto.SSL_Referee
16+
}
17+
18+
// NewBroadcaster creates a new UDP multicast connection on which to broadcast
19+
func NewRefBoxPublisher(address string) RefBoxPublisher {
20+
addr, err := net.ResolveUDPAddr("udp", address)
21+
if err != nil {
22+
log.Fatalln(err)
23+
}
24+
25+
conn, err := net.DialUDP("udp", nil, addr)
26+
if err != nil {
27+
log.Fatalln(err)
28+
}
29+
conn.SetReadBuffer(maxDatagramSize)
30+
log.Println("Connected to", address)
31+
32+
publisher := RefBoxPublisher{}
33+
publisher.Conn = conn
34+
35+
initRefereeMessage(&publisher.Message)
36+
37+
return publisher
38+
}
39+
40+
func initRefereeMessage(m *sslproto.SSL_Referee) {
41+
m.PacketTimestamp = new(uint64)
42+
m.Stage = new(sslproto.SSL_Referee_Stage)
43+
m.StageTimeLeft = new(int32)
44+
m.Command = new(sslproto.SSL_Referee_Command)
45+
m.CommandCounter = new(uint32)
46+
m.CommandTimestamp = new(uint64)
47+
m.Yellow = new(sslproto.SSL_Referee_TeamInfo)
48+
initTeamInfo(m.Yellow)
49+
m.Blue = new(sslproto.SSL_Referee_TeamInfo)
50+
initTeamInfo(m.Blue)
51+
m.BlueTeamOnPositiveHalf = new(bool)
52+
}
53+
54+
func initTeamInfo(t *sslproto.SSL_Referee_TeamInfo) {
55+
t.Name = new(string)
56+
t.Score = new(uint32)
57+
t.RedCards = new(uint32)
58+
t.YellowCards = new(uint32)
59+
t.Timeouts = new(uint32)
60+
t.TimeoutTime = new(uint32)
61+
t.Goalie = new(uint32)
62+
}
63+
64+
func (p *RefBoxPublisher) Publish(state *RefBoxState, command *RefBoxEventCommand) {
65+
66+
updateMessage(&p.Message, state, command)
67+
bytes, err := proto.Marshal(&p.Message)
68+
if err != nil {
69+
log.Printf("Could not marshal referee message: %v\nError: %v", state, err)
70+
return
71+
}
72+
_, err = p.Conn.Write(bytes)
73+
if err != nil {
74+
log.Printf("Could not write message: %v", err)
75+
}
76+
}
77+
78+
func updateMessage(r *sslproto.SSL_Referee, state *RefBoxState, command *RefBoxEventCommand) {
79+
80+
*r.PacketTimestamp = uint64(time.Now().UnixNano() / 1000)
81+
*r.Stage = mapStage(state.Stage)
82+
*r.StageTimeLeft = int32(state.GameTimeLeft.Nanoseconds() / 1000)
83+
*r.BlueTeamOnPositiveHalf = state.TeamState[TeamBlue].OnPositiveHalf
84+
updateTeam(r.Yellow, state.TeamState[TeamYellow])
85+
updateTeam(r.Blue, state.TeamState[TeamBlue])
86+
87+
if command != nil {
88+
*r.Command = mapCommand(command)
89+
*r.CommandCounter++
90+
*r.CommandTimestamp = uint64(time.Now().UnixNano() / 1000)
91+
}
92+
}
93+
func mapCommand(c *RefBoxEventCommand) sslproto.SSL_Referee_Command {
94+
switch c.Type {
95+
case CommandHalt:
96+
return sslproto.SSL_Referee_HALT
97+
case CommandStop:
98+
return sslproto.SSL_Referee_STOP
99+
case CommandNormalStart:
100+
return sslproto.SSL_Referee_NORMAL_START
101+
case CommandForceStart:
102+
return sslproto.SSL_Referee_FORCE_START
103+
case CommandDirect:
104+
return commandByTeam(c, sslproto.SSL_Referee_DIRECT_FREE_BLUE, sslproto.SSL_Referee_DIRECT_FREE_YELLOW)
105+
case CommandIndirect:
106+
return commandByTeam(c, sslproto.SSL_Referee_INDIRECT_FREE_BLUE, sslproto.SSL_Referee_INDIRECT_FREE_YELLOW)
107+
case CommandKickoff:
108+
return commandByTeam(c, sslproto.SSL_Referee_PREPARE_KICKOFF_BLUE, sslproto.SSL_Referee_PREPARE_KICKOFF_YELLOW)
109+
case CommandPenalty:
110+
return commandByTeam(c, sslproto.SSL_Referee_PREPARE_PENALTY_BLUE, sslproto.SSL_Referee_PREPARE_PENALTY_YELLOW)
111+
case CommandTimeout:
112+
return commandByTeam(c, sslproto.SSL_Referee_TIMEOUT_BLUE, sslproto.SSL_Referee_TIMEOUT_YELLOW)
113+
case CommandBallPlacement:
114+
return commandByTeam(c, sslproto.SSL_Referee_BALL_PLACEMENT_BLUE, sslproto.SSL_Referee_BALL_PLACEMENT_YELLOW)
115+
case CommandGoal:
116+
return commandByTeam(c, sslproto.SSL_Referee_GOAL_BLUE, sslproto.SSL_Referee_GOAL_YELLOW)
117+
}
118+
return -1
119+
}
120+
121+
func commandByTeam(command *RefBoxEventCommand, blueCommand sslproto.SSL_Referee_Command, yellowCommand sslproto.SSL_Referee_Command) sslproto.SSL_Referee_Command {
122+
if *command.ForTeam == TeamBlue {
123+
return blueCommand
124+
}
125+
return yellowCommand
126+
}
127+
128+
func updateTeam(team *sslproto.SSL_Referee_TeamInfo, state *RefBoxTeamState) {
129+
*team.Name = state.Name
130+
*team.Score = uint32(state.Goals)
131+
*team.RedCards = uint32(state.RedCards)
132+
team.YellowCardTimes = mapTimes(state.YellowCardTimes)
133+
*team.YellowCards = uint32(state.YellowCards)
134+
*team.Timeouts = uint32(state.TimeoutsLeft)
135+
*team.TimeoutTime = uint32(state.TimeoutTimeLeft.Nanoseconds() / 1000)
136+
*team.Goalie = uint32(state.Goalie)
137+
}
138+
139+
func mapTimes(durations []time.Duration) []uint32 {
140+
times := make([]uint32, len(durations))
141+
for i, d := range durations {
142+
times[i] = uint32(d.Nanoseconds() / 1000)
143+
}
144+
return times
145+
}
146+
147+
func mapStage(stage RefBoxStage) sslproto.SSL_Referee_Stage {
148+
switch stage {
149+
case StagePreGame:
150+
return sslproto.SSL_Referee_NORMAL_FIRST_HALF_PRE
151+
case StageFirstHalf:
152+
return sslproto.SSL_Referee_NORMAL_FIRST_HALF
153+
case StageHalfTime:
154+
return sslproto.SSL_Referee_NORMAL_HALF_TIME
155+
case StageSecondHalfPre:
156+
return sslproto.SSL_Referee_NORMAL_SECOND_HALF_PRE
157+
case StageSecondHalf:
158+
return sslproto.SSL_Referee_NORMAL_SECOND_HALF
159+
case StageOvertimeBreak:
160+
return sslproto.SSL_Referee_EXTRA_TIME_BREAK
161+
case StageOvertimeFirstHalfPre:
162+
return sslproto.SSL_Referee_EXTRA_FIRST_HALF_PRE
163+
case StageOvertimeFirstHalf:
164+
return sslproto.SSL_Referee_EXTRA_FIRST_HALF
165+
case StageOvertimeHalfTime:
166+
return sslproto.SSL_Referee_EXTRA_HALF_TIME
167+
case StageOvertimeSecondHalfPre:
168+
return sslproto.SSL_Referee_EXTRA_SECOND_HALF_PRE
169+
case StageOvertimeSecondHalf:
170+
return sslproto.SSL_Referee_EXTRA_SECOND_HALF
171+
case StageShootoutBreak:
172+
return sslproto.SSL_Referee_PENALTY_SHOOTOUT_BREAK
173+
case StageShootout:
174+
return sslproto.SSL_Referee_PENALTY_SHOOTOUT
175+
case StagePostGame:
176+
return sslproto.SSL_Referee_POST_GAME
177+
}
178+
return -1
179+
}

server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
3939
}
4040

4141
// wait for a new event
42-
<-refBox.newEventChannel
42+
<-refBox.notifyUpdateState
4343
}
4444
}
4545

@@ -61,7 +61,7 @@ func checkForNewEvent(conn *websocket.Conn) {
6161
log.Println("Could not process event:", string(b), err)
6262
} else {
6363
refBox.SaveState()
64-
refBox.newEventChannel <- event
64+
refBox.Update(event.Command)
6565
}
6666
}
6767
}

0 commit comments

Comments
 (0)