Skip to content

Commit dba9124

Browse files
committed
Implement CI mode
1 parent d202dab commit dba9124

File tree

18 files changed

+447
-180
lines changed

18 files changed

+447
-180
lines changed

generateProto.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ protoc -I"./proto" -I"$GOPATH/src" --go_out="$GOPATH/src" proto/ssl_gc_change.pr
3838
protoc -I"./proto" -I"$GOPATH/src" --go_out="$GOPATH/src" proto/ssl_gc_api.proto
3939
protoc -I"./proto" -I"$GOPATH/src" --go_out="$GOPATH/src" proto/ssl_gc_engine.proto
4040

41+
# continuous integration communication
42+
protoc -I"./proto" -I"$GOPATH/src" --go_out="$GOPATH/src" proto/ssl_gc_ci.proto
43+
4144
# generate javascript code
4245
pbjs -t static-module -w es6 -o src/proto.js \
4346
proto/ssl_gc_common.proto \

internal/app/api/server.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (a *Server) WsHandler(w http.ResponseWriter, r *http.Request) {
6969
}
7070

7171
func (s *ServerConnection) publish() {
72-
hook := make(chan *statemachine.StateChange)
72+
hook := make(chan engine.HookOut)
7373
s.gcEngine.RegisterHook(hook)
7474
defer func() {
7575
s.gcEngine.UnregisterHook(hook)
@@ -84,14 +84,16 @@ func (s *ServerConnection) publish() {
8484
select {
8585
case <-s.quit:
8686
return
87-
case change := <-hook:
88-
s.publishState(change.State)
89-
s.publishProtocolDelta()
90-
case <-time.After(100 * time.Millisecond):
91-
s.publishGcState()
92-
s.publishState(s.gcEngine.CurrentState())
93-
if s.gcEngine.LatestChangeId() != s.lastProtocolId {
94-
s.publishProtocolFull()
87+
case hookOut := <-hook:
88+
if hookOut.Change != nil {
89+
s.publishState(hookOut.State)
90+
s.publishProtocolDelta()
91+
} else if hookOut.State != nil {
92+
s.publishGcState()
93+
s.publishState(hookOut.State)
94+
if s.gcEngine.LatestChangeId() != s.lastProtocolId {
95+
s.publishProtocolFull()
96+
}
9597
}
9698
}
9799
}

internal/app/ci/ci.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package ci
2+
3+
import (
4+
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/state"
5+
"github.com/RoboCup-SSL/ssl-game-controller/internal/app/tracker"
6+
"github.com/RoboCup-SSL/ssl-go-tools/pkg/sslconn"
7+
"log"
8+
"net"
9+
"time"
10+
)
11+
12+
type Handler func(time.Time, *tracker.TrackerWrapperPacket) *state.Referee
13+
14+
type Server struct {
15+
address string
16+
listener net.Listener
17+
conn net.Conn
18+
latestTime time.Time
19+
tickChan chan time.Time
20+
TrackerConsumer func(*tracker.TrackerWrapperPacket)
21+
}
22+
23+
func NewServer(address string) *Server {
24+
return &Server{address: address, tickChan: make(chan time.Time, 1)}
25+
}
26+
27+
func (s *Server) Start() {
28+
go s.listen(s.address)
29+
}
30+
31+
func (s *Server) Stop() {
32+
if err := s.listener.Close(); err != nil {
33+
log.Printf("Could not close listener: %v", err)
34+
}
35+
}
36+
37+
func (s *Server) listen(address string) {
38+
listener, err := net.Listen("tcp", address)
39+
if err != nil {
40+
log.Print("Failed to listen on ", address)
41+
return
42+
}
43+
log.Print("Listening on ", address)
44+
s.listener = listener
45+
46+
for {
47+
conn, err := listener.Accept()
48+
if err != nil {
49+
log.Print("Could not accept connection: ", err)
50+
} else {
51+
log.Println("CI connection established")
52+
s.conn = conn
53+
s.serve(conn)
54+
log.Println("CI connection closed")
55+
}
56+
}
57+
}
58+
59+
func (s *Server) serve(conn net.Conn) {
60+
defer func() {
61+
if err := conn.Close(); err != nil {
62+
log.Printf("Could not close CI client connection: %v", err)
63+
}
64+
}()
65+
66+
for {
67+
input := CiInput{}
68+
if err := sslconn.ReceiveMessage(conn, &input); err != nil {
69+
log.Println("Could not receive message from CI connection: ", err)
70+
return
71+
}
72+
73+
if input.TrackerPacket != nil {
74+
s.TrackerConsumer(input.TrackerPacket)
75+
}
76+
77+
if input.Timestamp != nil {
78+
sec := *input.Timestamp / 1e9
79+
nSec := *input.Timestamp - sec*1e9
80+
s.latestTime = time.Unix(sec, nSec)
81+
select {
82+
case s.tickChan <- time.Now():
83+
default:
84+
}
85+
}
86+
}
87+
}
88+
89+
func (s *Server) SendMessage(refMsg *state.Referee) {
90+
if s.conn == nil {
91+
log.Println("Could not send message to CI client: Not connected")
92+
return
93+
}
94+
output := CiOutput{RefereeMsg: refMsg}
95+
if err := sslconn.SendMessage(s.conn, &output); err != nil {
96+
log.Printf("Could not send message: %v", err)
97+
return
98+
}
99+
}
100+
101+
func (s *Server) Time() time.Time {
102+
return s.latestTime
103+
}
104+
105+
func (s *Server) TickChanProvider() <-chan time.Time {
106+
return s.tickChan
107+
}

internal/app/ci/ssl_gc_ci.pb.go

Lines changed: 139 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/app/engine/engine.go

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ type Engine struct {
2323
currentState *state.State
2424
stateMachine *statemachine.StateMachine
2525
queue chan *statemachine.Change
26-
hooks []chan *statemachine.StateChange
26+
hooks []chan HookOut
2727
timeProvider timer.TimeProvider
2828
lastTimeUpdate time.Time
2929
gcState *GcState
3030
gcStateMutex sync.Mutex
3131
noProgressDetector NoProgressDetector
3232
ballPlacementCoordinator BallPlacementCoordinator
33+
tickChanProvider func() <-chan time.Time
3334
}
3435

3536
// NewEngine creates a new engine
@@ -39,9 +40,8 @@ func NewEngine(gameConfig config.Game) (e *Engine) {
3940
e.stateStore = store.NewStore(gameConfig.StateStoreFile)
4041
e.stateMachine = statemachine.NewStateMachine(gameConfig)
4142
e.queue = make(chan *statemachine.Change, 100)
42-
e.hooks = []chan *statemachine.StateChange{}
43-
e.timeProvider = func() time.Time { return time.Now() }
44-
e.lastTimeUpdate = e.timeProvider()
43+
e.hooks = []chan HookOut{}
44+
e.SetTimeProvider(func() time.Time { return time.Now() })
4545
e.gcState = new(GcState)
4646
e.gcState.TeamState = map[string]*GcStateTeam{
4747
state.Team_YELLOW.String(): new(GcStateTeam),
@@ -51,6 +51,9 @@ func NewEngine(gameConfig config.Game) (e *Engine) {
5151
e.gcState.TrackerStateGc = new(GcStateTracker)
5252
e.noProgressDetector = NoProgressDetector{gcEngine: e}
5353
e.ballPlacementCoordinator = BallPlacementCoordinator{gcEngine: e}
54+
e.tickChanProvider = func() <-chan time.Time {
55+
return time.After(25 * time.Millisecond)
56+
}
5457
return
5558
}
5659

@@ -64,26 +67,6 @@ func (e *Engine) Enqueue(change *statemachine.Change) {
6467
e.queue <- change
6568
}
6669

67-
// RegisterHook registers given hook for post processing after each change
68-
func (e *Engine) RegisterHook(hook chan *statemachine.StateChange) {
69-
e.hooks = append(e.hooks, hook)
70-
}
71-
72-
// UnregisterHook unregisters hooks that were registered before
73-
func (e *Engine) UnregisterHook(hook chan *statemachine.StateChange) bool {
74-
for i, h := range e.hooks {
75-
if h == hook {
76-
e.hooks = append(e.hooks[:i], e.hooks[i+1:]...)
77-
select {
78-
case <-hook:
79-
case <-time.After(10 * time.Millisecond):
80-
}
81-
return true
82-
}
83-
}
84-
return false
85-
}
86-
8770
// SetGeometry sets a new geometry
8871
func (e *Engine) SetGeometry(geometry config.Geometry) {
8972
e.stateMachine.Geometry = geometry
@@ -94,6 +77,17 @@ func (e *Engine) GetGeometry() config.Geometry {
9477
return e.stateMachine.Geometry
9578
}
9679

80+
// SetTimeProvider sets a new time provider for this engine
81+
func (e *Engine) SetTimeProvider(provider timer.TimeProvider) {
82+
e.timeProvider = provider
83+
e.lastTimeUpdate = e.timeProvider()
84+
}
85+
86+
// SetTickChanProvider sets an alternative provider for the tick channel
87+
func (e *Engine) SetTickChanProvider(provider func() <-chan time.Time) {
88+
e.tickChanProvider = provider
89+
}
90+
9791
// Start loads the state store and runs a go routine that consumes the change queue
9892
func (e *Engine) Start() error {
9993
if err := e.stateStore.Open(); err != nil {
@@ -166,13 +160,8 @@ func (e *Engine) processChanges() {
166160
return
167161
}
168162
e.processChange(change)
169-
case <-time.After(10 * time.Millisecond):
163+
case <-e.tickChanProvider():
170164
e.processTick()
171-
172-
e.noProgressDetector.process()
173-
e.ballPlacementCoordinator.process()
174-
e.processPrepare()
175-
e.processBotNumber()
176165
}
177166
}
178167
}
@@ -240,7 +229,7 @@ func (e *Engine) processChange(change *statemachine.Change) {
240229
log.Println("Could not add new state to store: ", err)
241230
}
242231
for _, hook := range e.hooks {
243-
hook <- &entry
232+
hook <- HookOut{Change: entry.Change, State: e.CurrentState()}
244233
}
245234
}
246235

0 commit comments

Comments
 (0)