Skip to content

Commit

Permalink
[feature] Add a CI server interface to update time from outside
Browse files Browse the repository at this point in the history
  • Loading branch information
g3force committed Feb 17, 2019
1 parent 0f0c474 commit 3cd6e91
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 16 deletions.
4 changes: 3 additions & 1 deletion config/ssl-game-controller.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
timeFromVision: false
timeAcquisitionMode: system
network:
publish-address: 224.5.23.1:10003
vision-address: 224.5.23.2:10006
Expand All @@ -11,6 +11,8 @@ server:
address: :10008
address-tls: :10108
trusted-keys-dir: config/trusted_keys/team
ci:
address: :10009
game:
yellow-card-duration: 2m
multiple-card-step: 3
Expand Down
25 changes: 20 additions & 5 deletions internal/app/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Network struct {
type Server struct {
AutoRef ServerAutoRef `yaml:"auto-ref"`
Team ServerTeam `yaml:"team"`
Ci ServerCi `yaml:"ci"`
}

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

// ServerCi holds configs for the CI server
type ServerCi struct {
Address string `yaml:"address"`
}

// Controller structure for the game controller
type Controller struct {
Network Network `yaml:"network"`
Game Game `yaml:"game"`
Server Server `yaml:"server"`
TimeFromVision bool `yaml:"timeFromVision"`
Network Network `yaml:"network"`
Game Game `yaml:"game"`
Server Server `yaml:"server"`
TimeAcquisitionMode TimeAcquisitionMode `yaml:"timeAcquisitionMode"`
}

type TimeAcquisitionMode string

const (
TimeAcquisitionModeSystem TimeAcquisitionMode = "system"
TimeAcquisitionModeVision TimeAcquisitionMode = "vision"
TimeAcquisitionModeCi TimeAcquisitionMode = "ci"
)

// LoadControllerConfig loads a config from given file
func LoadControllerConfig(fileName string) (config Controller, err error) {

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

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

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

c.TimeFromVision = false
c.TimeAcquisitionMode = TimeAcquisitionModeSystem

return
}
Expand Down
4 changes: 3 additions & 1 deletion internal/app/config/testdata/config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
timeFromVision: false
timeAcquisitionMode: system
network:
publish-address: 224.5.23.1:10003
vision-address: 224.5.23.2:10006
Expand All @@ -11,6 +11,8 @@ server:
address: :10008
address-tls: :10108
trusted-keys-dir: config/trusted_keys/team
ci:
address: :10009
game:
yellow-card-duration: 2m
multiple-card-step: 3
Expand Down
46 changes: 37 additions & 9 deletions internal/app/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/config"
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/rcon"
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/vision"
"github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
"github.com/RoboCup-SSL/ssl-game-controller/pkg/timer"
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslproto"
"log"
Expand All @@ -20,6 +21,7 @@ type GameController struct {
ApiServer ApiServer
AutoRefServer *rcon.AutoRefServer
TeamServer *rcon.TeamServer
CiServer rcon.CiServer
Engine Engine
historyPreserver HistoryPreserver
numUiProtocolsLastPublish int
Expand Down Expand Up @@ -49,6 +51,8 @@ func NewGameController() (c *GameController) {
c.TeamServer.LoadTrustedKeys(c.Config.Server.Team.TrustedKeysDir)
c.TeamServer.ProcessTeamRequest = c.ProcessTeamRequests

c.CiServer = rcon.NewCiServer()

c.Engine = NewEngine(c.Config.Game, time.Now().Unix())

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

go c.mainLoop()
go c.publishToNetwork()
go c.AutoRefServer.Listen(c.Config.Server.AutoRef.Address)
go c.AutoRefServer.ListenTls(c.Config.Server.AutoRef.AddressTls)
go c.TeamServer.Listen(c.Config.Server.Team.Address)
go c.TeamServer.ListenTls(c.Config.Server.Team.AddressTls)

if c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeSystem ||
c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeVision {
go c.updateLoop()
go c.publishToNetwork()
} else if c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeCi {
// do not send multicast packages - mainly for network performance issues, because publish will be called
// more frequently in the CI mode
c.Publisher.Message.Send = func() {}
c.CiServer.TimeConsumer = c.updateCi
go c.CiServer.Listen(c.Config.Server.Ci.Address)
} else {
log.Println("Unknown time acquisition mode: ", c.Config.TimeAcquisitionMode)
}
}

// setupTimeProvider changes the time provider to the vision receiver, if configured
func (c *GameController) setupTimeProvider() {
if c.Config.TimeFromVision {
if c.Config.TimeAcquisitionMode == config.TimeAcquisitionModeVision {
c.Engine.TimeProvider = func() time.Time {
return time.Unix(0, 0)
}
Expand All @@ -98,15 +114,27 @@ func (c *GameController) setupTimeProvider() {
}
}

// mainLoop updates several states every full second and publishes the new state
func (c *GameController) mainLoop() {
// updateLoop calls update() regularly
func (c *GameController) updateLoop() {
for {
time.Sleep(time.Millisecond * 10)
c.update()
}
}

newFullSecond, eventTriggered := c.Engine.Update()
if eventTriggered || newFullSecond {
c.publish()
}
// updateCi updates the current time to the given time and returns the updated referee message
func (c *GameController) updateCi(t time.Time) *refproto.Referee {
c.Engine.TimeProvider = func() time.Time { return t }
c.update()
c.Publisher.Publish(c.Engine.State)
return c.Publisher.Message.ProtoMsg
}

// update updates several states and publishes the new state to the UI every full second or on events
func (c *GameController) update() {
newFullSecond, eventTriggered := c.Engine.Update()
if eventTriggered || newFullSecond {
c.publish()
}
}

Expand Down
64 changes: 64 additions & 0 deletions internal/app/rcon/ciServer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package rcon

import (
"bufio"
"encoding/binary"
"github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto"
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslconn"
"log"
"net"
"time"
)

type CiServer struct {
TimeConsumer CiTimeConsumer
}

type CiTimeConsumer func(time.Time) *refproto.Referee

func NewCiServer() CiServer {
return CiServer{}
}

func (s *CiServer) Listen(address string) {
listener, err := net.Listen("tcp", address)
if err != nil {
log.Print("Failed to listen on ", address)
return
}
log.Print("Listening on ", address)

for {
conn, err := listener.Accept()
if err != nil {
log.Print("Could not accept connection: ", err)
} else {
log.Println("CI connection established")
s.serve(conn)
log.Println("CI connection closed")
}
}
}

func (s *CiServer) serve(conn net.Conn) {
defer conn.Close()

for {
reader := bufio.NewReaderSize(conn, 1)
timestamp, err := binary.ReadVarint(reader)
if err != nil {
log.Println("Error reading from CI connection: ", err)
return
}

sec := int64(timestamp / 1e9)
nSec := timestamp - sec*1e9
refMessage := s.TimeConsumer(time.Unix(sec, nSec))

err = sslconn.SendMessage(conn, refMessage)
if err != nil {
log.Printf("Could not send message: %v", err)
return
}
}
}

0 comments on commit 3cd6e91

Please sign in to comment.