Skip to content

Commit 1bd94a7

Browse files
committed
feature: Implement challenge flag, emergency stop and timeout request
1 parent 1486ea4 commit 1bd94a7

22 files changed

+1203
-454
lines changed

config/ssl-game-controller.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ game:
4444
ball-placement-min-robot-distance: 0.05
4545
distance-to-ball-in-stop: 0.5
4646
auto-approve-goals: false
47+
challenge-flags: 3
48+
emergency-stop-grace-period: 10s
4749
normal:
4850
half-duration: 5m
4951
half-time-duration: 5m

internal/app/api/server.go

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -284,13 +284,7 @@ func stateChanged(s1, s2 *state.State) bool {
284284
}
285285

286286
func teamStateChanged(s1, s2 *state.TeamInfo) bool {
287-
if *s1.Name != *s2.Name {
288-
return true
289-
}
290-
if *s1.Goals != *s2.Goals {
291-
return true
292-
}
293-
if *s1.Goalkeeper != *s2.Goalkeeper {
287+
if s1 == nil || s2 == nil {
294288
return true
295289
}
296290
if len(s1.YellowCards) != len(s2.YellowCards) {
@@ -307,35 +301,18 @@ func teamStateChanged(s1, s2 *state.TeamInfo) bool {
307301
return true
308302
}
309303
}
310-
if !reflect.DeepEqual(s1.RedCards, s2.RedCards) {
311-
return true
312-
}
313-
if *s1.TimeoutsLeft != *s2.TimeoutsLeft {
314-
return true
315-
}
316304
if s1.TimeoutTimeLeft.Seconds != s2.TimeoutTimeLeft.Seconds {
317305
return true
318306
}
319-
if *s1.OnPositiveHalf != *s2.OnPositiveHalf {
320-
return true
321-
}
322-
if !reflect.DeepEqual(s1.Fouls, s2.Fouls) {
323-
return true
324-
}
325-
if *s1.BallPlacementFailures != *s2.BallPlacementFailures {
326-
return true
327-
}
328-
if *s1.BallPlacementFailuresReached != *s2.BallPlacementFailuresReached {
329-
return true
330-
}
331-
if *s1.CanPlaceBall != *s2.CanPlaceBall {
332-
return true
333-
}
334-
if *s1.MaxAllowedBots != *s2.MaxAllowedBots {
335-
return true
336-
}
337-
if *s1.RequestsBotSubstitution != *s2.RequestsBotSubstitution {
338-
return true
339-
}
340-
return false
307+
308+
s1c := new(state.TeamInfo)
309+
s2c := new(state.TeamInfo)
310+
proto.Merge(s1c, s1)
311+
proto.Merge(s2c, s2)
312+
s1c.YellowCards = []*state.YellowCard{}
313+
s2c.YellowCards = []*state.YellowCard{}
314+
s1c.TimeoutTimeLeft = nil
315+
s2c.TimeoutTimeLeft = nil
316+
317+
return !proto.Equal(s1c, s2c)
341318
}

internal/app/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type Game struct {
5959
BallPlacementMinRobotDistance float64 `yaml:"ball-placement-min-robot-distance"`
6060
DistanceToBallInStop float64 `yaml:"distance-to-ball-in-stop"`
6161
AutoApproveGoals bool `yaml:"auto-approve-goals"`
62+
ChallengeFlags int32 `yaml:"challenge-flags"`
63+
EmergencyStopGracePeriod time.Duration `yaml:"emergency-stop-grace-period"`
6264
}
6365

6466
// Network holds configs for network communication
@@ -184,6 +186,8 @@ func DefaultControllerConfig() (c Controller) {
184186
c.Game.BallPlacementMinRobotDistance = 0.05
185187
c.Game.DistanceToBallInStop = 0.5
186188
c.Game.AutoApproveGoals = false
189+
c.Game.ChallengeFlags = 3
190+
c.Game.EmergencyStopGracePeriod = 10 * time.Second
187191

188192
c.Game.Normal.HalfDuration = 5 * time.Minute
189193
c.Game.Normal.HalfTimeDuration = 5 * time.Minute

internal/app/config/testdata/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ game:
4444
ball-placement-min-robot-distance: 0.05
4545
distance-to-ball-in-stop: 0.5
4646
auto-approve-goals: false
47+
challenge-flags: 3
48+
emergency-stop-grace-period: 10s
4749
normal:
4850
half-duration: 5m
4951
half-time-duration: 5m

internal/app/engine/engine.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func (e *Engine) SetTimeProvider(provider timer.TimeProvider) {
125125
defer e.mutex.Unlock()
126126
e.timeProvider = provider
127127
e.lastTimeUpdate = e.timeProvider()
128+
e.stateMachine.SetTimeProvider(provider)
128129
}
129130

130131
// SetTickChanProvider sets an alternative provider for the tick channel
@@ -315,6 +316,7 @@ func (e *Engine) createInitialState() (s *state.State) {
315316
*s.TeamInfo(team).TimeoutsLeft = e.gameConfig.Normal.Timeouts
316317
s.TeamInfo(team).TimeoutTimeLeft = ptypes.DurationProto(e.gameConfig.Normal.TimeoutDuration)
317318
*s.TeamInfo(team).MaxAllowedBots = e.gameConfig.MaxBots[e.gameConfig.DefaultDivision]
319+
*s.TeamInfo(team).ChallengeFlags = e.gameConfig.ChallengeFlags
318320
}
319321
return
320322
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package engine
2+
3+
import (
4+
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"
5+
)
6+
7+
func (e *Engine) processEmergencyStop() {
8+
if *e.currentState.GameState.Type != state.GameState_RUNNING {
9+
return
10+
}
11+
12+
for _, team := range state.BothTeams() {
13+
emergencyStopSince := e.currentState.TeamInfo(team).RequestsEmergencyStopSince
14+
if emergencyStopSince != nil {
15+
now := e.timeProvider()
16+
if now.Sub(goTime(emergencyStopSince)) > e.gameConfig.EmergencyStopGracePeriod {
17+
eventType := state.GameEvent_EMERGENCY_STOP
18+
e.EnqueueGameEvent(&state.GameEvent{
19+
Type: &eventType,
20+
Event: &state.GameEvent_EmergencyStop_{
21+
EmergencyStop: &state.GameEvent_EmergencyStop{
22+
ByTeam: &team,
23+
},
24+
},
25+
})
26+
}
27+
}
28+
}
29+
}

internal/app/engine/process_tick.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func (e *Engine) processTick() {
6464
e.processPenalty()
6565
e.processProposals()
6666
e.processTrackerSources()
67+
e.processEmergencyStop()
6768

6869
stateCopy := e.currentState.Clone()
6970
hookOut := HookOut{State: stateCopy}

internal/app/publish/messagegenerator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func updateTeam(teamInfo *state.Referee_TeamInfo, teamState *state.TeamInfo) {
123123
*teamInfo.BallPlacementFailuresReached = *teamState.BallPlacementFailuresReached
124124
*teamInfo.CanPlaceBall = *teamState.CanPlaceBall
125125
*teamInfo.MaxAllowedBots = unsigned32(*teamState.MaxAllowedBots)
126-
*teamInfo.BotSubstitutionIntent = *teamState.RequestsBotSubstitution
126+
*teamInfo.BotSubstitutionIntent = teamState.RequestsBotSubstitutionSince != nil
127127
timeoutTime, _ := ptypes.Duration(teamState.TimeoutTimeLeft)
128128
*teamInfo.TimeoutTime = mapTime(timeoutTime)
129129
}

internal/app/rcon/server_remotecontrol.go

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/statemachine"
77
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslconn"
88
"github.com/golang/protobuf/proto"
9+
"github.com/golang/protobuf/ptypes/timestamp"
910
"github.com/odeke-em/go-uuid"
1011
"github.com/pkg/errors"
1112
"io"
@@ -137,16 +138,32 @@ func (c *RemoteControlClient) reply(reply ControllerReply) {
137138

138139
teamState := c.gcEngine.CurrentState().TeamState[c.team.String()]
139140
response.Keeper = teamState.Goalkeeper
140-
response.SubstituteBot = teamState.RequestsBotSubstitution
141-
response.EmergencyStop = teamState.RequestsEmergencyStop
142-
response.Timeout = teamState.RequestsTimeout
143-
response.ChallengeFlag = teamState.RequestsChallenge
141+
response.SubstituteBot = timeSet(teamState.RequestsBotSubstitutionSince)
142+
response.EmergencyStop = c.gameEventPresent(state.GameEvent_EMERGENCY_STOP)
143+
response.Timeout = timeSet(teamState.RequestsTimeoutSince)
144+
response.ChallengeFlag = c.gameEventPresent(state.GameEvent_CHALLENGE_FLAG)
144145

145146
if err := sslconn.SendMessage(c.conn, &response); err != nil {
146147
log.Print("Failed to send reply: ", err)
147148
}
148149
}
149150

151+
func timeSet(t *timestamp.Timestamp) (set *bool) {
152+
set = new(bool)
153+
*set = t != nil
154+
return
155+
}
156+
157+
func (c *RemoteControlClient) gameEventPresent(eventType state.GameEvent_Type) (present *bool) {
158+
present = new(bool)
159+
for _, event := range c.gcEngine.CurrentState().GameEvents {
160+
if *event.Type == eventType && event.ByTeam() == *c.team {
161+
*present = true
162+
}
163+
}
164+
return
165+
}
166+
150167
func (s *RemoteControlServer) processRequest(team state.Team, request RemoteControlToController) error {
151168

152169
if request.GetPing() {
@@ -168,7 +185,7 @@ func (s *RemoteControlServer) processRequest(team state.Team, request RemoteCont
168185
}
169186

170187
if x, ok := request.GetMsg().(*RemoteControlToController_SubstituteBot); ok {
171-
if *teamState.RequestsBotSubstitution != x.SubstituteBot {
188+
if *timeSet(teamState.RequestsBotSubstitutionSince) != x.SubstituteBot {
172189
s.updateTeamConfig(team, &statemachine.UpdateTeamState{
173190
RequestsBotSubstitution: &x.SubstituteBot,
174191
})
@@ -177,7 +194,7 @@ func (s *RemoteControlServer) processRequest(team state.Team, request RemoteCont
177194
}
178195

179196
if x, ok := request.GetMsg().(*RemoteControlToController_Timeout); ok {
180-
if *teamState.RequestsTimeout != x.Timeout {
197+
if *timeSet(teamState.RequestsTimeoutSince) != x.Timeout {
181198
s.updateTeamConfig(team, &statemachine.UpdateTeamState{
182199
RequestsTimeout: &x.Timeout,
183200
})
@@ -186,17 +203,30 @@ func (s *RemoteControlServer) processRequest(team state.Team, request RemoteCont
186203
}
187204

188205
if x, ok := request.GetMsg().(*RemoteControlToController_ChallengeFlag); ok {
189-
if *teamState.RequestsChallenge != x.ChallengeFlag {
190-
s.updateTeamConfig(team, &statemachine.UpdateTeamState{
191-
RequestsChallenge: &x.ChallengeFlag,
206+
if x.ChallengeFlag {
207+
if *teamState.ChallengeFlags <= 0 {
208+
return errors.New("No more challenge flags left")
209+
} else if *teamState.TimeoutsLeft <= 0 {
210+
return errors.New("No more timeouts left")
211+
}
212+
eventType := state.GameEvent_CHALLENGE_FLAG
213+
s.gcEngine.EnqueueGameEvent(&state.GameEvent{
214+
Type: &eventType,
215+
Event: &state.GameEvent_ChallengeFlag_{
216+
ChallengeFlag: &state.GameEvent_ChallengeFlag{
217+
ByTeam: &team,
218+
},
219+
},
192220
})
193221
}
194222
return nil
195223
}
196224

197225
if x, ok := request.GetMsg().(*RemoteControlToController_EmergencyStop); ok {
198-
// note: emergency stop can not be disabled again
199226
if x.EmergencyStop {
227+
if teamState.RequestsEmergencyStopSince != nil {
228+
return errors.Errorf("Emergency stop already requested at %v", *teamState.RequestsEmergencyStopSince)
229+
}
200230
s.updateTeamConfig(team, &statemachine.UpdateTeamState{
201231
RequestsEmergencyStop: &x.EmergencyStop,
202232
})

internal/app/rcon/server_team.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func (s *TeamServer) processRequest(teamName string, request TeamToController) e
170170
teamState := *currentState.TeamInfo(team)
171171

172172
if x, ok := request.GetMsg().(*TeamToController_SubstituteBot); ok {
173-
if *teamState.RequestsBotSubstitution != x.SubstituteBot {
173+
if *timeSet(teamState.RequestsBotSubstitutionSince) != x.SubstituteBot {
174174
log.Printf("Team %v requests to change bot substituation intent to %v", team, x.SubstituteBot)
175175
s.gcEngine.Enqueue(&statemachine.Change{
176176
Origin: &teamName,

0 commit comments

Comments
 (0)