88 "github.com/RoboCup-SSL/ssl-game-controller/internal/app/statemachine"
99 "github.com/google/uuid"
1010 "github.com/pkg/errors"
11- "google.golang.org/protobuf/types/known/timestamppb"
1211 "io"
1312 "log"
1413 "net"
@@ -135,23 +134,8 @@ func (s *RemoteControlServer) SendRequest(teamName string, request *ControllerTo
135134 return errors .Errorf ("Remote control client '%v' not connected" , teamName )
136135}
137136
138- func (c * RemoteControlClient ) replyWithState (reply * ControllerReply ) {
139- teamState := c .gcEngine .CurrentState ().TeamState [c .team .String ()]
140- emergencyStopDueIn := c .gcEngine .EmergencyStopDueIn (* c .team )
141- var emergencyStopIn * float32
142- if emergencyStopDueIn != nil {
143- emergencyStopIn = new (float32 )
144- * emergencyStopIn = float32 (emergencyStopDueIn .Seconds ())
145- }
137+ func (c * RemoteControlClient ) reply (reply * ControllerReply ) {
146138 response := & ControllerToRemoteControl {
147- State : & RemoteControlTeamState {
148- KeeperId : teamState .Goalkeeper ,
149- SubstituteBot : timeSet (teamState .RequestsBotSubstitutionSince ),
150- EmergencyStop : timeSet (teamState .RequestsEmergencyStopSince ),
151- EmergencyStopIn : emergencyStopIn ,
152- Timeout : timeSet (teamState .RequestsTimeoutSince ),
153- ChallengeFlag : gameEventPresent (c .gcEngine .CurrentState ().GameEvents , state .GameEvent_CHALLENGE_FLAG , * c .team ),
154- },
155139 ControllerReply : reply ,
156140 }
157141
@@ -160,8 +144,24 @@ func (c *RemoteControlClient) replyWithState(reply *ControllerReply) {
160144 }
161145}
162146
163- func (c * RemoteControlClient ) reply (reply * ControllerReply ) {
147+ func (c * RemoteControlClient ) replyWithState (reply * ControllerReply ) {
148+ teamState := c .gcEngine .CurrentState ().TeamState [c .team .String ()]
149+ emergencyStopIn := c .findEmergencyStopDueIn ()
150+ yellowCardsDue := c .findYellowCardDueTimes ()
151+ availableRequests := c .findAvailableRequestTypes ()
152+ activeRequests := c .findActiveRequestTypes ()
153+
164154 response := & ControllerToRemoteControl {
155+ State : & RemoteControlTeamState {
156+ KeeperId : teamState .Goalkeeper ,
157+ AvailableRequests : availableRequests ,
158+ ActiveRequests : activeRequests ,
159+ EmergencyStopIn : & emergencyStopIn ,
160+ TimeoutsLeft : teamState .TimeoutsLeft ,
161+ ChallengeFlagsLeft : teamState .ChallengeFlags ,
162+ MaxRobots : teamState .MaxAllowedBots ,
163+ YellowCardsDue : yellowCardsDue ,
164+ },
165165 ControllerReply : reply ,
166166 }
167167
@@ -170,62 +170,159 @@ func (c *RemoteControlClient) reply(reply *ControllerReply) {
170170 }
171171}
172172
173- func timeSet (t * timestamppb.Timestamp ) (set * bool ) {
174- set = new (bool )
175- * set = t != nil
176- return
173+ func (c * RemoteControlClient ) findEmergencyStopDueIn () float32 {
174+ emergencyStopDueIn := c .gcEngine .EmergencyStopDueIn (* c .team )
175+ var emergencyStopIn float32
176+ if emergencyStopDueIn != nil {
177+ emergencyStopIn = float32 (emergencyStopDueIn .Seconds ())
178+ }
179+ return emergencyStopIn
177180}
178181
179- func gameEventPresent (events []* state.GameEvent , eventType state.GameEvent_Type , team state.Team ) (present * bool ) {
180- present = new (bool )
181- for _ , event := range events {
182- if * event .Type == eventType && event .ByTeam () == team {
183- * present = true
182+ func (c * RemoteControlClient ) findYellowCardDueTimes () []float32 {
183+ teamState := c .gcEngine .CurrentState ().TeamState [c .team .String ()]
184+ var yellowCardsDue []float32
185+ for _ , yc := range teamState .YellowCards {
186+ if yc .TimeRemaining != nil && yc .TimeRemaining .AsDuration () > 0 {
187+ yellowCardsDue = append (yellowCardsDue , float32 (yc .TimeRemaining .AsDuration ().Seconds ()))
184188 }
185189 }
186- return
190+ return yellowCardsDue
191+ }
192+
193+ func (c * RemoteControlClient ) findActiveRequestTypes () []RemoteControlRequestType {
194+ currentState := c .gcEngine .CurrentState ()
195+ teamState := currentState .TeamState [c .team .String ()]
196+
197+ var activeRequests []RemoteControlRequestType
198+ if teamState .RequestsBotSubstitutionSince != nil {
199+ activeRequests = append (activeRequests , RemoteControlRequestType_ROBOT_SUBSTITUTION )
200+ }
201+ if teamState .RequestsTimeoutSince != nil {
202+ activeRequests = append (activeRequests , RemoteControlRequestType_TIMEOUT )
203+ }
204+ if currentState .HasGameEventByTeam (state .GameEvent_CHALLENGE_FLAG , * c .team ) {
205+ activeRequests = append (activeRequests , RemoteControlRequestType_CHALLENGE_FLAG )
206+ }
207+ if teamState .RequestsEmergencyStopSince != nil {
208+ activeRequests = append (activeRequests , RemoteControlRequestType_EMERGENCY_STOP )
209+ }
210+ return activeRequests
211+ }
212+
213+ func (c * RemoteControlClient ) findAvailableRequestTypes () []RemoteControlRequestType {
214+ var availableRequests []RemoteControlRequestType
215+ if c .checkRequestEmergencyStop () == nil {
216+ availableRequests = append (availableRequests , RemoteControlRequestType_EMERGENCY_STOP )
217+ }
218+ if c .checkRequestTimeout () == nil {
219+ availableRequests = append (availableRequests , RemoteControlRequestType_TIMEOUT )
220+ }
221+ if c .checkRequestRobotSubstitution () == nil {
222+ availableRequests = append (availableRequests , RemoteControlRequestType_ROBOT_SUBSTITUTION )
223+ }
224+ if c .checkRequestChallengeFlag () == nil {
225+ availableRequests = append (availableRequests , RemoteControlRequestType_CHALLENGE_FLAG )
226+ }
227+ if c .checkChangeKeeper () == nil {
228+ availableRequests = append (availableRequests , RemoteControlRequestType_CHANGE_KEEPER_ID )
229+ }
230+ return availableRequests
231+ }
232+
233+ func (c * RemoteControlClient ) checkRequestEmergencyStop () error {
234+ gameStateType := * c .gcEngine .CurrentState ().GameState .Type
235+ switch gameStateType {
236+ case state .GameState_RUNNING ,
237+ state .GameState_KICKOFF ,
238+ state .GameState_PENALTY ,
239+ state .GameState_FREE_KICK :
240+ return nil
241+ }
242+ return errors .Errorf ("Game state is invalid: %s" , gameStateType )
243+ }
244+
245+ func (c * RemoteControlClient ) checkRequestTimeout () error {
246+ gameStateType := * c .gcEngine .CurrentState ().GameState .Type
247+ if gameStateType == state .GameState_TIMEOUT {
248+ return errors .New ("Timeout is active" )
249+ }
250+ return nil
251+ }
252+
253+ func (c * RemoteControlClient ) checkRequestRobotSubstitution () error {
254+ gameStateType := * c .gcEngine .CurrentState ().GameState .Type
255+ if gameStateType == state .GameState_HALT {
256+ return errors .New ("Game is halted" )
257+ }
258+ return nil
259+ }
260+
261+ func (c * RemoteControlClient ) checkRequestChallengeFlag () error {
262+ currentState := c .gcEngine .CurrentState ()
263+ teamState := currentState .TeamState [c .team .String ()]
264+ if currentState .HasGameEventByTeam (state .GameEvent_CHALLENGE_FLAG , * c .team ) {
265+ return errors .New ("Challenge flag already requested" )
266+ }
267+ if * teamState .ChallengeFlags <= 0 {
268+ return errors .New ("No more challenge flags left" )
269+ } else if * teamState .TimeoutsLeft <= 0 {
270+ return errors .New ("No more timeouts left" )
271+ }
272+ return nil
273+ }
274+
275+ func (c * RemoteControlClient ) checkChangeKeeper () error {
276+ currentState := c .gcEngine .CurrentState ()
277+ teamState := currentState .TeamState [c .team .String ()]
278+ return mayChangeKeeper (c .gcEngine .CurrentGcState (), teamState )
187279}
188280
189281func (c * RemoteControlClient ) processRequest (request * RemoteControlToController ) error {
190282
191283 currentState := c .gcEngine .CurrentState ()
192- teamState := * currentState .TeamInfo (* c .team )
284+ teamState := currentState .TeamInfo (* c .team )
193285
194286 if x , ok := request .GetMsg ().(* RemoteControlToController_DesiredKeeper ); ok && * teamState .Goalkeeper != x .DesiredKeeper {
195- if err := mayChangeKeeper (c .gcEngine .CurrentGcState (), & teamState ); err != nil {
196- return errors .Wrap (err , "Remote control requests to change keeper, but: " )
287+ if err := mayChangeKeeper (c .gcEngine .CurrentGcState (), teamState ); err != nil {
288+ return errors .Wrap (err , "Can not change keeper id " )
197289 }
198290 c .updateTeamConfig (& statemachine.UpdateTeamState {
199291 Goalkeeper : & x .DesiredKeeper ,
200292 })
201293 }
202294
203- if x , ok := request .GetMsg ().(* RemoteControlToController_SubstituteBot ); ok {
204- if * timeSet (teamState .RequestsBotSubstitutionSince ) != x .SubstituteBot {
295+ if x , ok := request .GetMsg ().(* RemoteControlToController_RequestRobotSubstitution ); ok {
296+ robotSubstitutionRequested := teamState .RequestsBotSubstitutionSince != nil
297+ if robotSubstitutionRequested != x .RequestRobotSubstitution {
298+ if err := c .checkRequestRobotSubstitution (); err != nil {
299+ return errors .Wrap (err , "Can not request robot substitution" )
300+ }
205301 c .updateTeamConfig (& statemachine.UpdateTeamState {
206- RequestsBotSubstitution : & x .SubstituteBot ,
302+ RequestsBotSubstitution : & x .RequestRobotSubstitution ,
207303 })
208304 }
209305 return nil
210306 }
211307
212- if x , ok := request .GetMsg ().(* RemoteControlToController_Timeout ); ok {
213- if (* timeSet (teamState .RequestsTimeoutSince )) != x .Timeout {
308+ if x , ok := request .GetMsg ().(* RemoteControlToController_RequestTimeout ); ok {
309+ timeoutRequested := teamState .RequestsTimeoutSince != nil
310+ if timeoutRequested != x .RequestTimeout {
311+ if err := c .checkRequestTimeout (); err != nil {
312+ return errors .Wrap (err , "Can not request timeout" )
313+ }
214314 c .updateTeamConfig (& statemachine.UpdateTeamState {
215- RequestsTimeout : & x .Timeout ,
315+ RequestsTimeout : & x .RequestTimeout ,
216316 })
217317 }
218318 return nil
219319 }
220320
221321 if request .GetRequest () == RemoteControlToController_CHALLENGE_FLAG {
222- if * teamState .ChallengeFlags <= 0 {
223- return errors .New ("No more challenge flags left" )
224- } else if * teamState .TimeoutsLeft <= 0 {
225- return errors .New ("No more timeouts left" )
322+ if err := c .checkRequestChallengeFlag (); err != nil {
323+ return errors .Wrap (err , "Can not request challenge flag" )
226324 }
227325 eventType := state .GameEvent_CHALLENGE_FLAG
228- log .Println ("Request challenge flag via remote control" )
229326 c .gcEngine .EnqueueGameEvent (& state.GameEvent {
230327 Type : & eventType ,
231328 Origin : []string {* origin (c .team )},
@@ -238,13 +335,14 @@ func (c *RemoteControlClient) processRequest(request *RemoteControlToController)
238335 return nil
239336 }
240337
241- if x , ok := request .GetMsg ().(* RemoteControlToController_EmergencyStop ); ok {
242- if * currentState .GameState .Type != state .GameState_RUNNING {
243- return errors .New ("Game is not running, can not request emergency stop" )
244- }
245- if * timeSet (teamState .RequestsEmergencyStopSince ) != x .EmergencyStop {
338+ if x , ok := request .GetMsg ().(* RemoteControlToController_RequestEmergencyStop ); ok {
339+ emergencyStopRequested := teamState .RequestsEmergencyStopSince != nil
340+ if emergencyStopRequested != x .RequestEmergencyStop {
341+ if err := c .checkRequestEmergencyStop (); err != nil {
342+ return errors .Wrap (err , "Can not request emergency stop" )
343+ }
246344 c .updateTeamConfig (& statemachine.UpdateTeamState {
247- RequestsEmergencyStop : & x .EmergencyStop ,
345+ RequestsEmergencyStop : & x .RequestEmergencyStop ,
248346 })
249347 }
250348 return nil
@@ -256,12 +354,15 @@ func (c *RemoteControlClient) processRequest(request *RemoteControlToController)
256354func (c * RemoteControlClient ) updateTeamConfig (update * statemachine.UpdateTeamState ) {
257355 log .Println ("Update team config via remote control: " , update .String ())
258356 update .ForTeam = c .team
259- c .gcEngine .Enqueue (& statemachine.Change {
357+ err := c .gcEngine .EnqueueBlocking (& statemachine.Change {
260358 Origin : origin (c .team ),
261359 Change : & statemachine.Change_UpdateTeamState {
262360 UpdateTeamState : update ,
263361 },
264362 })
363+ if err != nil {
364+ log .Println ("Failed to update team state: " , err )
365+ }
265366}
266367
267368func origin (team * state.Team ) * string {
0 commit comments