Skip to content

Commit cf0395c

Browse files
committed
[refactoring] Move code from controller into separate files
1 parent 574feff commit cf0395c

File tree

4 files changed

+234
-202
lines changed

4 files changed

+234
-202
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package controller
2+
3+
import (
4+
"github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
5+
"log"
6+
"math"
7+
)
8+
9+
func (c *GameController) ProcessAutoRefRequests(id string, request refproto.AutoRefToControllerRequest) error {
10+
c.Mutex.Lock()
11+
defer c.Mutex.Unlock()
12+
log.Printf("Received request from autoRef '%v': %v", id, request)
13+
14+
if request.GameEvent != nil {
15+
details := GameEventDetailsFromProto(*request.GameEvent)
16+
gameEventType := details.EventType()
17+
event := Event{GameEvent: &GameEvent{Type: gameEventType, Details: details}}
18+
19+
if c.Engine.State.GameEventBehavior[event.GameEvent.Type] == GameEventBehaviorMajority {
20+
validUntil := c.Engine.TimeProvider().Add(c.Config.Game.AutoRefProposalTimeout)
21+
newProposal := GameEventProposal{GameEvent: *event.GameEvent, ProposerId: id, ValidUntil: validUntil}
22+
23+
eventPresent := false
24+
for _, proposal := range c.Engine.State.GameEventProposals {
25+
if proposal.GameEvent.Type == event.GameEvent.Type && proposal.ProposerId == newProposal.ProposerId {
26+
// update proposal
27+
*proposal = newProposal
28+
eventPresent = true
29+
}
30+
}
31+
if !eventPresent {
32+
c.Engine.State.GameEventProposals = append(c.Engine.State.GameEventProposals, &newProposal)
33+
}
34+
35+
totalProposals := 0
36+
for _, proposal := range c.Engine.State.GameEventProposals {
37+
if proposal.GameEvent.Type == event.GameEvent.Type && proposal.ValidUntil.After(c.Engine.TimeProvider()) {
38+
totalProposals++
39+
}
40+
}
41+
42+
majority := int(math.Floor(float64(len(c.AutoRefServer.Clients)) / 2.0))
43+
if totalProposals > majority {
44+
c.OnNewEvent(event)
45+
}
46+
} else {
47+
c.OnNewEvent(event)
48+
}
49+
}
50+
51+
return nil
52+
}

internal/app/controller/controller.go

Lines changed: 29 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ import (
44
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/config"
55
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"
66
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"
7-
"github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
87
"github.com/RoboCup-SSL/ssl-game-controller/pkg/timer"
9-
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslproto"
10-
"github.com/pkg/errors"
118
"log"
12-
"math"
139
"sync"
1410
"time"
1511
)
@@ -32,12 +28,6 @@ type GameController struct {
3228
VisionReceiver *vision.Receiver
3329
}
3430

35-
type TeamChoice struct {
36-
Team Team
37-
Event Event
38-
IssueTime time.Time
39-
}
40-
4131
// NewGameController creates a new RefBox
4232
func NewGameController() (c *GameController) {
4333

@@ -86,150 +76,45 @@ func (c *GameController) Run() {
8676
c.Engine.State.TeamState[TeamBlue].Name}
8777

8878
c.timer.Start()
89-
go c.publishApi()
90-
go c.publishNetwork()
79+
go c.mainLoop()
80+
go c.publishToNetwork()
9181
go c.AutoRefServer.Listen(c.Config.Server.AutoRef.Address)
9282
go c.TeamServer.Listen(c.Config.Server.Team.Address)
9383
}
9484

95-
func (c *GameController) ProcessGeometry(data *sslproto.SSL_GeometryData) {
96-
if int32(math.Round(c.Engine.Geometry.FieldWidth*1000.0)) != *data.Field.FieldWidth {
97-
newFieldWidth := float64(*data.Field.FieldWidth) / 1000.0
98-
log.Printf("FieldWidth changed from %v to %v", c.Engine.Geometry.FieldWidth, newFieldWidth)
99-
c.Engine.Geometry.FieldWidth = newFieldWidth
100-
}
101-
if int32(math.Round(c.Engine.Geometry.FieldLength*1000)) != *data.Field.FieldLength {
102-
newFieldLength := float64(*data.Field.FieldLength) / 1000.0
103-
log.Printf("FieldLength changed from %v to %v", c.Engine.Geometry.FieldLength, newFieldLength)
104-
c.Engine.Geometry.FieldLength = newFieldLength
105-
}
106-
for _, line := range data.Field.FieldLines {
107-
if *line.Name == "LeftFieldLeftPenaltyStretch" {
108-
defenseAreaDepth := math.Abs(float64(*line.P1.X-*line.P2.X)) / 1000.0
109-
if math.Abs(defenseAreaDepth-c.Engine.Geometry.DefenseAreaDepth) > 1e-3 {
110-
log.Printf("DefenseAreaDepth changed from %v to %v", c.Engine.Geometry.DefenseAreaDepth, defenseAreaDepth)
111-
c.Engine.Geometry.DefenseAreaDepth = defenseAreaDepth
112-
}
113-
} else if *line.Name == "LeftPenaltyStretch" {
114-
defenseAreaWidth := math.Abs(float64(*line.P1.Y-*line.P2.Y)) / 1000.0
115-
if math.Abs(defenseAreaWidth-c.Engine.Geometry.DefenseAreaWidth) > 1e-3 {
116-
log.Printf("DefenseAreaDepth changed from %v to %v", c.Engine.Geometry.DefenseAreaWidth, defenseAreaWidth)
117-
c.Engine.Geometry.DefenseAreaWidth = defenseAreaWidth
118-
}
119-
}
120-
}
121-
}
122-
123-
func (c *GameController) ProcessAutoRefRequests(id string, request refproto.AutoRefToControllerRequest) error {
124-
c.Mutex.Lock()
125-
defer c.Mutex.Unlock()
126-
log.Printf("Received request from autoRef '%v': %v", id, request)
127-
128-
if request.GameEvent != nil {
129-
details := GameEventDetailsFromProto(*request.GameEvent)
130-
gameEventType := details.EventType()
131-
event := Event{GameEvent: &GameEvent{Type: gameEventType, Details: details}}
132-
133-
if c.Engine.State.GameEventBehavior[event.GameEvent.Type] == GameEventBehaviorMajority {
134-
validUntil := c.Engine.TimeProvider().Add(c.Config.Game.AutoRefProposalTimeout)
135-
newProposal := GameEventProposal{GameEvent: *event.GameEvent, ProposerId: id, ValidUntil: validUntil}
136-
137-
eventPresent := false
138-
for _, proposal := range c.Engine.State.GameEventProposals {
139-
if proposal.GameEvent.Type == event.GameEvent.Type && proposal.ProposerId == newProposal.ProposerId {
140-
// update proposal
141-
*proposal = newProposal
142-
eventPresent = true
143-
}
144-
}
145-
if !eventPresent {
146-
c.Engine.State.GameEventProposals = append(c.Engine.State.GameEventProposals, &newProposal)
147-
}
148-
149-
totalProposals := 0
150-
for _, proposal := range c.Engine.State.GameEventProposals {
151-
if proposal.GameEvent.Type == event.GameEvent.Type && proposal.ValidUntil.After(c.Engine.TimeProvider()) {
152-
totalProposals++
153-
}
154-
}
85+
// mainLoop updates several states every full second and publishes the new state
86+
func (c *GameController) mainLoop() {
87+
defer c.historyPreserver.Close()
88+
for {
89+
c.timer.WaitTillNextFullSecond()
90+
c.Engine.Tick(c.timer.Delta())
15591

156-
majority := int(math.Floor(float64(len(c.AutoRefServer.Clients)) / 2.0))
157-
if totalProposals > majority {
158-
c.OnNewEvent(event)
159-
}
160-
} else {
161-
c.OnNewEvent(event)
162-
}
92+
c.publish()
16393
}
164-
165-
return nil
16694
}
16795

168-
func (c *GameController) ProcessTeamRequests(teamName string, request refproto.TeamToControllerRequest) error {
169-
c.Mutex.Lock()
170-
defer c.Mutex.Unlock()
171-
log.Print("Received request from team: ", request)
172-
173-
if x, ok := request.GetRequest().(*refproto.TeamToControllerRequest_AdvantageResponse_); ok {
174-
if c.outstandingTeamChoice == nil {
175-
return errors.New("No outstanding choice available. You are probably too late.")
176-
}
177-
responseTime := c.Engine.TimeProvider().Sub(c.outstandingTeamChoice.IssueTime)
178-
if x.AdvantageResponse == refproto.TeamToControllerRequest_CONTINUE {
179-
log.Printf("Team %v decided to continue the game within %v", c.outstandingTeamChoice.Team, responseTime)
180-
switch c.outstandingTeamChoice.Event.GameEvent.Type {
181-
case GameEventBotCrashUnique:
182-
c.outstandingTeamChoice.Event.GameEvent.Details.BotCrashUniqueSkipped = c.outstandingTeamChoice.Event.GameEvent.Details.BotCrashUnique
183-
c.outstandingTeamChoice.Event.GameEvent.Details.BotCrashUnique = nil
184-
c.outstandingTeamChoice.Event.GameEvent.Type = GameEventBotCrashUniqueSkipped
185-
case GameEventBotPushedBot:
186-
c.outstandingTeamChoice.Event.GameEvent.Details.BotPushedBotSkipped = c.outstandingTeamChoice.Event.GameEvent.Details.BotPushedBot
187-
c.outstandingTeamChoice.Event.GameEvent.Details.BotPushedBot = nil
188-
c.outstandingTeamChoice.Event.GameEvent.Type = GameEventBotPushedBotSkipped
189-
default:
190-
return errors.Errorf("Unsupported advantage choice game event: %v", c.outstandingTeamChoice.Event.GameEvent.Type)
191-
}
192-
} else {
193-
log.Printf("Team %v decided to stop the game within %v", c.outstandingTeamChoice.Team, responseTime)
194-
}
195-
c.OnNewEvent(c.outstandingTeamChoice.Event)
196-
c.outstandingTeamChoice = nil
197-
return nil
198-
}
199-
200-
team := c.Engine.State.TeamByName(teamName)
201-
if team == TeamUnknown {
202-
return errors.New("Your team is not playing?!")
203-
}
204-
205-
if x, ok := request.GetRequest().(*refproto.TeamToControllerRequest_SubstituteBot); ok {
206-
log.Printf("Team %v updated bot substituation intend to %v", team, x.SubstituteBot)
207-
c.Engine.State.TeamState[team].BotSubstitutionIntend = x.SubstituteBot
208-
}
209-
210-
if c.Engine.State.GameState() != GameStateStopped {
211-
return errors.New("Game is not stopped.")
212-
}
96+
// publish publishes the state to the UI and the teams
97+
func (c *GameController) publish() {
98+
c.updateOnlineStates()
99+
c.historyPreserver.Save(c.Engine.History)
213100

214-
if x, ok := request.GetRequest().(*refproto.TeamToControllerRequest_DesiredKeeper); ok {
215-
log.Printf("Changing goalie for team %v to %v", team, x.DesiredKeeper)
216-
c.Engine.State.TeamState[team].Goalie = int(x.DesiredKeeper)
217-
}
101+
c.TeamServer.AllowedTeamNames = []string{
102+
c.Engine.State.TeamState[TeamYellow].Name,
103+
c.Engine.State.TeamState[TeamBlue].Name}
218104

219-
return nil
105+
c.publishUiProtocol()
106+
c.ApiServer.PublishState(*c.Engine.State)
220107
}
221108

222-
func (c *GameController) publishApi() {
223-
defer c.historyPreserver.Close()
224-
for {
225-
c.timer.WaitTillNextFullSecond()
226-
c.updateOnlineStates()
227-
c.Engine.Tick(c.timer.Delta())
228-
c.historyPreserver.Save(c.Engine.History)
229-
c.publish()
109+
// publishUiProtocol publishes the UI protocol, if it has changed
110+
func (c *GameController) publishUiProtocol() {
111+
if len(c.Engine.UiProtocol) != c.numUiProtocolsLastPublish {
112+
c.ApiServer.PublishUiProtocol(c.Engine.UiProtocol)
113+
c.numUiProtocolsLastPublish = len(c.Engine.UiProtocol)
230114
}
231115
}
232116

117+
// updateOnlineStates checks if teams and autoRefs are online and writes this into the state
233118
func (c *GameController) updateOnlineStates() {
234119
for _, team := range []Team{TeamYellow, TeamBlue} {
235120
c.Engine.State.TeamState[team].Connected = c.teamConnected(team)
@@ -241,21 +126,15 @@ func (c *GameController) updateOnlineStates() {
241126
c.Engine.State.AutoRefsConnected = autoRefs
242127
}
243128

244-
func (c *GameController) teamConnected(team Team) bool {
245-
teamName := c.Engine.State.TeamState[team].Name
246-
if _, ok := c.TeamServer.Clients[teamName]; ok {
247-
return true
248-
}
249-
return false
250-
}
251-
252-
func (c *GameController) publishNetwork() {
129+
// publishToNetwork publishes the current state to the network (multicast) every 100ms
130+
func (c *GameController) publishToNetwork() {
253131
for {
254132
c.timer.WaitTillNextFull(100 * time.Millisecond)
255133
c.Publisher.Publish(c.Engine.State)
256134
}
257135
}
258136

137+
// OnNewEvent processes the given event
259138
func (c *GameController) OnNewEvent(event Event) {
260139

261140
if event.GameEvent != nil && !c.Engine.disabledGameEvent(event.GameEvent.Type) && c.askForTeamDecisionIfRequired(event) {
@@ -266,52 +145,11 @@ func (c *GameController) OnNewEvent(event Event) {
266145
if err != nil {
267146
log.Println("Could not process event:", event, err)
268147
} else {
269-
c.historyPreserver.Save(c.Engine.History)
270148
c.publish()
271149
}
272150
}
273151

274-
func (c *GameController) askForTeamDecisionIfRequired(event Event) (handled bool) {
275-
handled = false
276-
if c.outstandingTeamChoice == nil && c.Engine.State.GameState() == GameStateRunning {
277-
var byTeamProto refproto.Team
278-
var choiceType refproto.ControllerToTeamRequest_AdvantageChoice_Foul
279-
if event.GameEvent.Details.BotCrashUnique != nil {
280-
byTeamProto = *event.GameEvent.Details.BotCrashUnique.ByTeam
281-
choiceType = refproto.ControllerToTeamRequest_AdvantageChoice_COLLISION
282-
} else if event.GameEvent.Details.BotPushedBot != nil {
283-
byTeamProto = *event.GameEvent.Details.BotPushedBot.ByTeam
284-
choiceType = refproto.ControllerToTeamRequest_AdvantageChoice_PUSHING
285-
}
286-
287-
forTeam := NewTeam(byTeamProto).Opposite()
288-
if forTeam != "" {
289-
teamName := c.Engine.State.TeamState[forTeam].Name
290-
choice := refproto.ControllerToTeamRequest_AdvantageChoice{Foul: &choiceType}
291-
requestPayload := refproto.ControllerToTeamRequest_AdvantageChoice_{AdvantageChoice: &choice}
292-
request := refproto.ControllerToTeamRequest{Request: &requestPayload}
293-
err := c.TeamServer.SendRequest(teamName, request)
294-
if err != nil {
295-
log.Print("Failed to ask for advantage choice: ", err)
296-
} else {
297-
c.outstandingTeamChoice = &TeamChoice{Team: forTeam, Event: event, IssueTime: c.Engine.TimeProvider()}
298-
go c.timeoutTeamChoice()
299-
handled = true
300-
}
301-
}
302-
}
303-
return
304-
}
305-
306-
func (c *GameController) timeoutTeamChoice() {
307-
time.Sleep(c.Config.Game.TeamChoiceTimeout)
308-
c.Mutex.Lock()
309-
defer c.Mutex.Unlock()
310-
if c.outstandingTeamChoice != nil {
311-
c.OnNewEvent(c.outstandingTeamChoice.Event)
312-
}
313-
}
314-
152+
// loadPublisher creates a new publisher for multicast
315153
func loadPublisher(config config.Controller) Publisher {
316154
publisher, err := NewPublisher(config.Network.PublishAddress)
317155
if err != nil {
@@ -320,22 +158,11 @@ func loadPublisher(config config.Controller) Publisher {
320158
return publisher
321159
}
322160

161+
// loadConfig loads the controller config
323162
func loadConfig() config.Controller {
324163
cfg, err := config.LoadControllerConfig(configFileName)
325164
if err != nil {
326165
log.Printf("Could not load config: %v", err)
327166
}
328167
return cfg
329168
}
330-
331-
// publish publishes the state to the UI and the teams
332-
func (c *GameController) publish() {
333-
if len(c.Engine.UiProtocol) != c.numUiProtocolsLastPublish {
334-
c.ApiServer.PublishUiProtocol(c.Engine.UiProtocol)
335-
c.numUiProtocolsLastPublish = len(c.Engine.UiProtocol)
336-
}
337-
338-
c.TeamServer.AllowedTeamNames = []string{c.Engine.State.TeamState[TeamYellow].Name,
339-
c.Engine.State.TeamState[TeamBlue].Name}
340-
c.ApiServer.PublishState(*c.Engine.State)
341-
}

internal/app/controller/geometry.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package controller
2+
3+
import (
4+
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslproto"
5+
"log"
6+
"math"
7+
)
8+
9+
func (c *GameController) ProcessGeometry(data *sslproto.SSL_GeometryData) {
10+
if int32(math.Round(c.Engine.Geometry.FieldWidth*1000.0)) != *data.Field.FieldWidth {
11+
newFieldWidth := float64(*data.Field.FieldWidth) / 1000.0
12+
log.Printf("FieldWidth changed from %v to %v", c.Engine.Geometry.FieldWidth, newFieldWidth)
13+
c.Engine.Geometry.FieldWidth = newFieldWidth
14+
}
15+
if int32(math.Round(c.Engine.Geometry.FieldLength*1000)) != *data.Field.FieldLength {
16+
newFieldLength := float64(*data.Field.FieldLength) / 1000.0
17+
log.Printf("FieldLength changed from %v to %v", c.Engine.Geometry.FieldLength, newFieldLength)
18+
c.Engine.Geometry.FieldLength = newFieldLength
19+
}
20+
for _, line := range data.Field.FieldLines {
21+
if *line.Name == "LeftFieldLeftPenaltyStretch" {
22+
defenseAreaDepth := math.Abs(float64(*line.P1.X-*line.P2.X)) / 1000.0
23+
if math.Abs(defenseAreaDepth-c.Engine.Geometry.DefenseAreaDepth) > 1e-3 {
24+
log.Printf("DefenseAreaDepth changed from %v to %v", c.Engine.Geometry.DefenseAreaDepth, defenseAreaDepth)
25+
c.Engine.Geometry.DefenseAreaDepth = defenseAreaDepth
26+
}
27+
} else if *line.Name == "LeftPenaltyStretch" {
28+
defenseAreaWidth := math.Abs(float64(*line.P1.Y-*line.P2.Y)) / 1000.0
29+
if math.Abs(defenseAreaWidth-c.Engine.Geometry.DefenseAreaWidth) > 1e-3 {
30+
log.Printf("DefenseAreaDepth changed from %v to %v", c.Engine.Geometry.DefenseAreaWidth, defenseAreaWidth)
31+
c.Engine.Geometry.DefenseAreaWidth = defenseAreaWidth
32+
}
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)