Skip to content

Commit 3cd6e91

Browse files
committed
[feature] Add a CI server interface to update time from outside
1 parent 0f0c474 commit 3cd6e91

File tree

5 files changed

+127
-16
lines changed

5 files changed

+127
-16
lines changed

config/ssl-game-controller.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
timeFromVision: false
1+
timeAcquisitionMode: system
22
network:
33
publish-address: 224.5.23.1:10003
44
vision-address: 224.5.23.2:10006
@@ -11,6 +11,8 @@ server:
1111
address: :10008
1212
address-tls: :10108
1313
trusted-keys-dir: config/trusted_keys/team
14+
ci:
15+
address: :10009
1416
game:
1517
yellow-card-duration: 2m
1618
multiple-card-step: 3

internal/app/config/config.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type Network struct {
5656
type Server struct {
5757
AutoRef ServerAutoRef `yaml:"auto-ref"`
5858
Team ServerTeam `yaml:"team"`
59+
Ci ServerCi `yaml:"ci"`
5960
}
6061

6162
// ServerAutoRef holds configs for the autoRef server
@@ -72,14 +73,27 @@ type ServerTeam struct {
7273
TrustedKeysDir string `yaml:"trusted-keys-dir"`
7374
}
7475

76+
// ServerCi holds configs for the CI server
77+
type ServerCi struct {
78+
Address string `yaml:"address"`
79+
}
80+
7581
// Controller structure for the game controller
7682
type Controller struct {
77-
Network Network `yaml:"network"`
78-
Game Game `yaml:"game"`
79-
Server Server `yaml:"server"`
80-
TimeFromVision bool `yaml:"timeFromVision"`
83+
Network Network `yaml:"network"`
84+
Game Game `yaml:"game"`
85+
Server Server `yaml:"server"`
86+
TimeAcquisitionMode TimeAcquisitionMode `yaml:"timeAcquisitionMode"`
8187
}
8288

89+
type TimeAcquisitionMode string
90+
91+
const (
92+
TimeAcquisitionModeSystem TimeAcquisitionMode = "system"
93+
TimeAcquisitionModeVision TimeAcquisitionMode = "vision"
94+
TimeAcquisitionModeCi TimeAcquisitionMode = "ci"
95+
)
96+
8397
// LoadControllerConfig loads a config from given file
8498
func LoadControllerConfig(fileName string) (config Controller, err error) {
8599

@@ -137,6 +151,7 @@ func DefaultControllerConfig() (c Controller) {
137151
c.Server.Team.Address = ":10008"
138152
c.Server.Team.AddressTls = ":10108"
139153
c.Server.Team.TrustedKeysDir = "config/trusted_keys/team"
154+
c.Server.Ci.Address = ":10009"
140155

141156
c.Game.DefaultGeometry = map[Division]*Geometry{}
142157
c.Game.DefaultGeometry[DivA] = new(Geometry)
@@ -161,7 +176,7 @@ func DefaultControllerConfig() (c Controller) {
161176

162177
c.Game.MaxBots = map[Division]int{DivA: 8, DivB: 6}
163178

164-
c.TimeFromVision = false
179+
c.TimeAcquisitionMode = TimeAcquisitionModeSystem
165180

166181
return
167182
}

internal/app/config/testdata/config.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
timeFromVision: false
1+
timeAcquisitionMode: system
22
network:
33
publish-address: 224.5.23.1:10003
44
vision-address: 224.5.23.2:10006
@@ -11,6 +11,8 @@ server:
1111
address: :10008
1212
address-tls: :10108
1313
trusted-keys-dir: config/trusted_keys/team
14+
ci:
15+
address: :10009
1416
game:
1517
yellow-card-duration: 2m
1618
multiple-card-step: 3

internal/app/controller/controller.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ 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"
78
"github.com/RoboCup-SSL/ssl-game-controller/pkg/timer"
89
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslproto"
910
"log"
@@ -20,6 +21,7 @@ type GameController struct {
2021
ApiServer ApiServer
2122
AutoRefServer *rcon.AutoRefServer
2223
TeamServer *rcon.TeamServer
24+
CiServer rcon.CiServer
2325
Engine Engine
2426
historyPreserver HistoryPreserver
2527
numUiProtocolsLastPublish int
@@ -49,6 +51,8 @@ func NewGameController() (c *GameController) {
4951
c.TeamServer.LoadTrustedKeys(c.Config.Server.Team.TrustedKeysDir)
5052
c.TeamServer.ProcessTeamRequest = c.ProcessTeamRequests
5153

54+
c.CiServer = rcon.NewCiServer()
55+
5256
c.Engine = NewEngine(c.Config.Game, time.Now().Unix())
5357

5458
c.setupTimeProvider()
@@ -78,17 +82,29 @@ func (c *GameController) Run() {
7882
c.TeamServer.AllowedTeamNames = []string{c.Engine.State.TeamState[TeamYellow].Name,
7983
c.Engine.State.TeamState[TeamBlue].Name}
8084

81-
go c.mainLoop()
82-
go c.publishToNetwork()
8385
go c.AutoRefServer.Listen(c.Config.Server.AutoRef.Address)
8486
go c.AutoRefServer.ListenTls(c.Config.Server.AutoRef.AddressTls)
8587
go c.TeamServer.Listen(c.Config.Server.Team.Address)
8688
go c.TeamServer.ListenTls(c.Config.Server.Team.AddressTls)
89+
90+
if c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeSystem ||
91+
c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeVision {
92+
go c.updateLoop()
93+
go c.publishToNetwork()
94+
} else if c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeCi {
95+
// do not send multicast packages - mainly for network performance issues, because publish will be called
96+
// more frequently in the CI mode
97+
c.Publisher.Message.Send = func() {}
98+
c.CiServer.TimeConsumer = c.updateCi
99+
go c.CiServer.Listen(c.Config.Server.Ci.Address)
100+
} else {
101+
log.Println("Unknown time acquisition mode: ", c.Config.TimeAcquisitionMode)
102+
}
87103
}
88104

89105
// setupTimeProvider changes the time provider to the vision receiver, if configured
90106
func (c *GameController) setupTimeProvider() {
91-
if c.Config.TimeFromVision {
107+
if c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeVision {
92108
c.Engine.TimeProvider = func() time.Time {
93109
return time.Unix(0, 0)
94110
}
@@ -98,15 +114,27 @@ func (c *GameController) setupTimeProvider() {
98114
}
99115
}
100116

101-
// mainLoop updates several states every full second and publishes the new state
102-
func (c *GameController) mainLoop() {
117+
// updateLoop calls update() regularly
118+
func (c *GameController) updateLoop() {
103119
for {
104120
time.Sleep(time.Millisecond * 10)
121+
c.update()
122+
}
123+
}
105124

106-
newFullSecond, eventTriggered := c.Engine.Update()
107-
if eventTriggered || newFullSecond {
108-
c.publish()
109-
}
125+
// updateCi updates the current time to the given time and returns the updated referee message
126+
func (c *GameController) updateCi(t time.Time) *refproto.Referee {
127+
c.Engine.TimeProvider = func() time.Time { return t }
128+
c.update()
129+
c.Publisher.Publish(c.Engine.State)
130+
return c.Publisher.Message.ProtoMsg
131+
}
132+
133+
// update updates several states and publishes the new state to the UI every full second or on events
134+
func (c *GameController) update() {
135+
newFullSecond, eventTriggered := c.Engine.Update()
136+
if eventTriggered || newFullSecond {
137+
c.publish()
110138
}
111139
}
112140

internal/app/rcon/ciServer.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package rcon
2+
3+
import (
4+
"bufio"
5+
"encoding/binary"
6+
"github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
7+
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslconn"
8+
"log"
9+
"net"
10+
"time"
11+
)
12+
13+
type CiServer struct {
14+
TimeConsumer CiTimeConsumer
15+
}
16+
17+
type CiTimeConsumer func(time.Time) *refproto.Referee
18+
19+
func NewCiServer() CiServer {
20+
return CiServer{}
21+
}
22+
23+
func (s *CiServer) Listen(address string) {
24+
listener, err := net.Listen("tcp", address)
25+
if err != nil {
26+
log.Print("Failed to listen on ", address)
27+
return
28+
}
29+
log.Print("Listening on ", address)
30+
31+
for {
32+
conn, err := listener.Accept()
33+
if err != nil {
34+
log.Print("Could not accept connection: ", err)
35+
} else {
36+
log.Println("CI connection established")
37+
s.serve(conn)
38+
log.Println("CI connection closed")
39+
}
40+
}
41+
}
42+
43+
func (s *CiServer) serve(conn net.Conn) {
44+
defer conn.Close()
45+
46+
for {
47+
reader := bufio.NewReaderSize(conn, 1)
48+
timestamp, err := binary.ReadVarint(reader)
49+
if err != nil {
50+
log.Println("Error reading from CI connection: ", err)
51+
return
52+
}
53+
54+
sec := int64(timestamp / 1e9)
55+
nSec := timestamp - sec*1e9
56+
refMessage := s.TimeConsumer(time.Unix(sec, nSec))
57+
58+
err = sslconn.SendMessage(conn, refMessage)
59+
if err != nil {
60+
log.Printf("Could not send message: %v", err)
61+
return
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)