Skip to content

Commit 8a1f8be

Browse files
committed
[feature] Preserve refereeEvent history as well
1 parent 7e2b9cd commit 8a1f8be

File tree

5 files changed

+152
-115
lines changed

5 files changed

+152
-115
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ yarn-debug.log*
2121
yarn-error.log*
2222

2323
# app-specific ignores
24-
logs
24+
history.json

internal/app/controller/apiServer.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ import (
88
)
99

1010
type ApiServer struct {
11-
Consumer EventConsumer
12-
connections []*websocket.Conn
11+
Consumer EventConsumer
12+
connections []*websocket.Conn
13+
latestState State
14+
latestRefereeEvents []RefereeEvent
1315
}
1416

1517
type EventConsumer interface {
1618
OnNewEvent(event Event)
1719
}
1820

1921
type MessageWrapper struct {
20-
State *State `json:"state"`
21-
GameEvents *[]RefereeEvent `json:"gameEvents"`
22+
State *State `json:"state"`
23+
RefereeEvents *[]RefereeEvent `json:"gameEvents"`
2224
}
2325

2426
// WsHandler handles incoming web socket connections
@@ -40,6 +42,8 @@ func (a *ApiServer) WsHandler(w http.ResponseWriter, r *http.Request) {
4042

4143
a.connections = append(a.connections, conn)
4244

45+
a.publishFullWrapper(conn)
46+
4347
a.listenForNewEvents(conn)
4448
}
4549

@@ -57,13 +61,27 @@ func (a *ApiServer) PublishWrapper(wrapper MessageWrapper) {
5761
}
5862
}
5963

64+
func (a *ApiServer) publishFullWrapper(conn *websocket.Conn) {
65+
wrapper := MessageWrapper{&a.latestState, &a.latestRefereeEvents}
66+
b, err := json.Marshal(wrapper)
67+
if err != nil {
68+
log.Println("Marshal error:", err)
69+
}
70+
err = conn.WriteMessage(websocket.TextMessage, b)
71+
if err != nil {
72+
log.Println("Could not write message.", err)
73+
}
74+
}
75+
6076
func (a *ApiServer) PublishState(state State) {
77+
a.latestState = state
6178
wrapper := MessageWrapper{State: &state}
6279
a.PublishWrapper(wrapper)
6380
}
6481

65-
func (a *ApiServer) PublishGameEvents(gameEvents []RefereeEvent) {
66-
wrapper := MessageWrapper{GameEvents: &gameEvents}
82+
func (a *ApiServer) PublishGameEvents(events []RefereeEvent) {
83+
a.latestRefereeEvents = events
84+
wrapper := MessageWrapper{RefereeEvents: &events}
6785
a.PublishWrapper(wrapper)
6886
}
6987

internal/app/controller/controller.go

Lines changed: 24 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
11
package controller
22

33
import (
4-
"encoding/json"
54
"github.com/RoboCup-SSL/ssl-game-controller/pkg/timer"
6-
"io"
75
"log"
8-
"os"
9-
"time"
106
)
117

12-
const logDir = "logs"
13-
const lastStateFileName = logDir + "/lastState.json"
148
const configFileName = "config/ssl-game-controller.yaml"
159

1610
// GameController controls a game
1711
type GameController struct {
1812
Config Config
19-
stateHistoryFile *os.File
20-
lastStateFile *os.File
2113
Publisher Publisher
2214
ApiServer ApiServer
2315
Engine Engine
2416
timer timer.Timer
17+
historyPreserver HistoryPreserver
2518
}
2619

2720
// NewGameController creates a new RefBox
@@ -42,34 +35,41 @@ func NewGameController() (r *GameController) {
4235
// Run the GameController by starting an endless loop in the background
4336
func (r *GameController) Run() (err error) {
4437

45-
r.openStateFiles()
46-
r.readLastState()
38+
if err := r.historyPreserver.Open(); err != nil {
39+
log.Print("Could not open history", err)
40+
} else {
41+
history, err := r.historyPreserver.Load()
42+
if err != nil {
43+
log.Print("Could not load history", err)
44+
} else if len(*history) > 0 {
45+
r.Engine.History = *history
46+
*r.Engine.State = r.Engine.History[len(r.Engine.History)-1].State
47+
r.Engine.RefereeEvents = r.Engine.History[len(r.Engine.History)-1].RefereeEvents
48+
}
49+
}
50+
51+
r.ApiServer.PublishState(*r.Engine.State)
52+
r.ApiServer.PublishGameEvents(r.Engine.RefereeEvents)
4753

4854
go func() {
49-
if r.stateHistoryFile != nil {
50-
defer r.stateHistoryFile.Close()
51-
}
52-
if r.lastStateFile != nil {
53-
defer r.lastStateFile.Close()
54-
}
55+
defer r.historyPreserver.Close()
5556
for {
5657
r.timer.WaitTillNextFullSecond()
5758
r.Engine.Tick(r.timer.Delta())
58-
r.saveLatestState()
59-
r.publish(nil)
59+
r.historyPreserver.Save(r.Engine.History)
60+
r.publish()
6061
}
6162
}()
6263
return nil
6364
}
6465

6566
func (r *GameController) OnNewEvent(event Event) {
66-
cmd, err := r.Engine.Process(event)
67+
err := r.Engine.Process(event)
6768
if err != nil {
6869
log.Println("Could not process event:", event, err)
6970
} else {
70-
r.saveLatestState()
71-
r.saveStateHistory()
72-
r.publish(cmd)
71+
r.historyPreserver.Save(r.Engine.History)
72+
r.publish()
7373
r.publishGameEvents()
7474
}
7575
}
@@ -90,81 +90,13 @@ func loadConfig() Config {
9090
return config
9191
}
9292

93-
func (r *GameController) openStateFiles() {
94-
os.MkdirAll(logDir, os.ModePerm)
95-
96-
stateHistoryLogFileName := logDir + "/state-history_" + time.Now().Format("2006-01-02_15-04-05") + ".log"
97-
f, err := os.OpenFile(stateHistoryLogFileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
98-
if err != nil {
99-
log.Fatal("Can not open state history log file", err)
100-
}
101-
r.stateHistoryFile = f
102-
103-
f, err = os.OpenFile(lastStateFileName, os.O_RDWR|os.O_CREATE, 0600)
104-
if err != nil {
105-
log.Fatal("Can not open last state file", err)
106-
}
107-
r.lastStateFile = f
108-
}
109-
110-
func (r *GameController) readLastState() {
111-
bufSize := 10000
112-
b := make([]byte, bufSize)
113-
n, err := r.lastStateFile.Read(b)
114-
if err != nil && err != io.EOF {
115-
log.Fatal("Could not read from last state file ", err)
116-
}
117-
if n == bufSize {
118-
log.Fatal("Buffer size too small")
119-
}
120-
if n > 0 {
121-
err = json.Unmarshal(b[:n], r.Engine.State)
122-
if err != nil {
123-
log.Fatalf("Could not read last state: %v %v", string(b), err)
124-
}
125-
}
126-
}
127-
12893
// publish publishes the state to the UI and the teams
129-
func (r *GameController) publish(command *EventCommand) {
94+
func (r *GameController) publish() {
13095
r.ApiServer.PublishState(*r.Engine.State)
13196
r.Publisher.Publish(r.Engine.State)
13297
}
13398

13499
// publishGameEvents publishes the current list of game events
135100
func (r *GameController) publishGameEvents() {
136-
r.ApiServer.PublishGameEvents(r.Engine.RefereeEvent)
137-
}
138-
139-
// saveLatestState writes the current state into a file
140-
func (r *GameController) saveLatestState() {
141-
jsonState, err := json.MarshalIndent(r.Engine.State, "", " ")
142-
if err != nil {
143-
log.Print("Can not marshal state ", err)
144-
return
145-
}
146-
147-
err = r.lastStateFile.Truncate(0)
148-
if err != nil {
149-
log.Fatal("Can not truncate last state file ", err)
150-
}
151-
_, err = r.lastStateFile.WriteAt(jsonState, 0)
152-
if err != nil {
153-
log.Print("Could not write last state ", err)
154-
}
155-
r.lastStateFile.Sync()
156-
}
157-
158-
// saveStateHistory writes the current state to the history file
159-
func (r *GameController) saveStateHistory() {
160-
161-
jsonState, err := json.Marshal(r.Engine.State)
162-
if err != nil {
163-
log.Print("Can not marshal state ", err)
164-
return
165-
}
166-
167-
r.stateHistoryFile.Write(jsonState)
168-
r.stateHistoryFile.WriteString("\n")
169-
r.stateHistoryFile.Sync()
101+
r.ApiServer.PublishGameEvents(r.Engine.RefereeEvents)
170102
}

internal/app/controller/engine.go

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import (
88
"time"
99
)
1010

11+
const maxHistorySize = 10
12+
1113
type Engine struct {
12-
State *State
13-
StageTimes map[Stage]time.Duration
14-
config ConfigGame
15-
StateHistory []State
16-
TimeProvider func() time.Time
17-
RefereeEvent []RefereeEvent
14+
State *State
15+
RefereeEvents []RefereeEvent
16+
StageTimes map[Stage]time.Duration
17+
config ConfigGame
18+
TimeProvider func() time.Time
19+
History History
1820
}
1921

2022
func NewEngine(config ConfigGame) (e Engine) {
@@ -31,7 +33,7 @@ func (e *Engine) ResetGame() {
3133
e.State.TeamState[TeamYellow].TimeoutTimeLeft = e.config.Normal.TimeoutDuration
3234
e.State.TeamState[TeamBlue].TimeoutsLeft = e.config.Normal.Timeouts
3335
e.State.TeamState[TeamYellow].TimeoutsLeft = e.config.Normal.Timeouts
34-
e.RefereeEvent = []RefereeEvent{}
36+
e.History = History{}
3537
}
3638

3739
// Tick updates the times of the state and removes cards, if necessary
@@ -45,22 +47,26 @@ func (e *Engine) Tick(delta time.Duration) {
4547

4648
// UndoLastAction restores the last state from internal history
4749
func (e *Engine) UndoLastAction() {
48-
lastIndex := len(e.StateHistory) - 2
50+
lastIndex := len(e.History) - 2
4951
if lastIndex >= 0 {
50-
*e.State = e.StateHistory[lastIndex]
51-
e.StateHistory = e.StateHistory[0:lastIndex]
52+
*e.State = e.History[lastIndex].State
53+
e.History = e.History[0:lastIndex]
5254
}
5355
}
5456

55-
func (e *Engine) Process(event Event) (*EventCommand, error) {
57+
func (e *Engine) Process(event Event) error {
5658
cmd, err := e.processEvent(event)
57-
if err == nil {
58-
e.StateHistory = append(e.StateHistory, *e.State)
59+
if err != nil {
60+
return err
5961
}
6062
if cmd != nil {
6163
e.LogCommand(cmd)
6264
}
63-
return cmd, err
65+
e.History = append(e.History, HistoryEntry{*e.State, e.RefereeEvents})
66+
if len(e.History) > maxHistorySize {
67+
e.History = e.History[1:]
68+
}
69+
return nil
6470
}
6571

6672
func (e *Engine) LogGameEvent(eventType GameEventType) {
@@ -70,7 +76,7 @@ func (e *Engine) LogGameEvent(eventType GameEventType) {
7076
Type: RefereeEventGameEvent,
7177
GameEventType: &eventType,
7278
}
73-
e.RefereeEvent = append(e.RefereeEvent, gameEvent)
79+
e.RefereeEvents = append(e.RefereeEvents, gameEvent)
7480
}
7581

7682
func (e *Engine) LogCommand(command *EventCommand) {
@@ -81,7 +87,7 @@ func (e *Engine) LogCommand(command *EventCommand) {
8187
Command: &command.Type,
8288
Team: command.ForTeam,
8389
}
84-
e.RefereeEvent = append(e.RefereeEvent, gameEvent)
90+
e.RefereeEvents = append(e.RefereeEvents, gameEvent)
8591
}
8692

8793
func (e *Engine) loadStages() {

0 commit comments

Comments
 (0)