diff --git a/db/migrations/20140524180123_CreateMatches.sql b/db/migrations/20140524180123_CreateMatches.sql index 24af415..0db04b1 100644 --- a/db/migrations/20140524180123_CreateMatches.sql +++ b/db/migrations/20140524180123_CreateMatches.sql @@ -21,7 +21,8 @@ CREATE TABLE matches ( blue3issurrogate bool, status VARCHAR(16), startedat DATETIME, - winner VARCHAR(16) + winner VARCHAR(16), + gamespecificdata VARCHAR(3) ); CREATE UNIQUE INDEX type_displayname ON matches(type, displayname); diff --git a/field/arena.go b/field/arena.go index 9878eab..912bd39 100644 --- a/field/arena.go +++ b/field/arena.go @@ -24,11 +24,12 @@ const ( const ( PreMatch = 0 StartMatch = 1 - AutoPeriod = 2 - PausePeriod = 3 - TeleopPeriod = 4 - EndgamePeriod = 5 - PostMatch = 6 + WarmupPeriod = 2 + AutoPeriod = 3 + PausePeriod = 4 + TeleopPeriod = 5 + EndgamePeriod = 6 + PostMatch = 7 ) type Arena struct { @@ -83,6 +84,7 @@ type ArenaStatus struct { CanStartMatch bool PlcIsHealthy bool FieldEstop bool + GameSpecificData string } type AllianceStation struct { @@ -286,7 +288,12 @@ func (arena *Arena) SubstituteTeam(teamId int, station string) error { func (arena *Arena) StartMatch() error { err := arena.checkCanStartMatch() if err == nil { - // Save the match start time to the database for posterity. + // Generate game-specific data or allow manual input for test matches. + if arena.CurrentMatch.Type != "test" || !game.IsValidGameSpecificData(arena.CurrentMatch.GameSpecificData) { + arena.CurrentMatch.GameSpecificData = game.GenerateGameSpecificData() + } + + // Save the match start time and game-specifc data to the database for posterity. arena.CurrentMatch.StartedAt = time.Now() if arena.CurrentMatch.Type != "test" { arena.Database.SaveMatch(arena.CurrentMatch) @@ -361,23 +368,35 @@ func (arena *Arena) Update() { auto = true enabled = false case StartMatch: - arena.MatchState = AutoPeriod + arena.MatchState = WarmupPeriod arena.MatchStartTime = time.Now() arena.LastMatchTimeSec = -1 auto = true - enabled = true - sendDsPacket = true + enabled = false arena.AudienceDisplayScreen = "match" arena.AudienceDisplayNotifier.Notify(nil) - if !arena.MuteMatchSounds { - arena.PlaySoundNotifier.Notify("match-start") - } arena.FieldTestMode = "" arena.Plc.ResetCounts() + arena.sendGameSpecificDataPacket() + if !arena.MuteMatchSounds { + arena.PlaySoundNotifier.Notify("match-warmup") + } + case WarmupPeriod: + auto = true + enabled = false + if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec) { + arena.MatchState = AutoPeriod + auto = true + enabled = true + sendDsPacket = true + if !arena.MuteMatchSounds { + arena.PlaySoundNotifier.Notify("match-start") + } + } case AutoPeriod: auto = true enabled = true - if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec) { + if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec) { arena.MatchState = PausePeriod auto = false enabled = false @@ -389,7 +408,8 @@ func (arena *Arena) Update() { case PausePeriod: auto = false enabled = false - if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec) { + if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+ + game.MatchTiming.PauseDurationSec) { arena.MatchState = TeleopPeriod auto = false enabled = true @@ -401,8 +421,8 @@ func (arena *Arena) Update() { case TeleopPeriod: auto = false enabled = true - if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+ - game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) { + if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+ + game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) { arena.MatchState = EndgamePeriod sendDsPacket = false if !arena.MuteMatchSounds { @@ -412,8 +432,8 @@ func (arena *Arena) Update() { case EndgamePeriod: auto = false enabled = true - if matchTimeSec >= float64(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+ - game.MatchTiming.TeleopDurationSec) { + if matchTimeSec >= float64(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+ + game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec) { arena.MatchState = PostMatch auto = false enabled = false @@ -481,7 +501,7 @@ func (arena *Arena) BlueScoreSummary() *game.ScoreSummary { func (arena *Arena) GetStatus() *ArenaStatus { return &ArenaStatus{arena.AllianceStations, arena.MatchState, arena.checkCanStartMatch() == nil, - arena.Plc.IsHealthy, arena.Plc.GetFieldEstop()} + arena.Plc.IsHealthy, arena.Plc.GetFieldEstop(), arena.CurrentMatch.GameSpecificData} } // Loads a team into an alliance station, cleaning up the previous team there if there is one. @@ -587,6 +607,19 @@ func (arena *Arena) sendDsPacket(auto bool, enabled bool) { arena.lastDsPacketTime = time.Now() } +func (arena *Arena) sendGameSpecificDataPacket() { + for _, allianceStation := range arena.AllianceStations { + dsConn := allianceStation.DsConn + if dsConn != nil { + err := dsConn.sendGameSpecificDataPacket(arena.CurrentMatch.GameSpecificData) + if err != nil { + log.Printf("Error sending game-specific data packet to Team %d: %v", dsConn.TeamId, err) + } + } + } + arena.lastDsPacketTime = time.Now() +} + // Returns the alliance station identifier for the given team, or the empty string if the team is not present // in the current match. func (arena *Arena) getAssignedAllianceStation(teamId int) string { diff --git a/field/arena_test.go b/field/arena_test.go index 10afd19..9125a2b 100644 --- a/field/arena_test.go +++ b/field/arena_test.go @@ -87,6 +87,15 @@ func TestArenaMatchFlow(t *testing.T) { err = arena.StartMatch() assert.Nil(t, err) arena.Update() + assert.Equal(t, WarmupPeriod, arena.MatchState) + assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto) + assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Enabled) + arena.Update() + assert.Equal(t, WarmupPeriod, arena.MatchState) + assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto) + assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Enabled) + arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second) + arena.Update() assert.Equal(t, AutoPeriod, arena.MatchState) assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto) assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Enabled) @@ -94,7 +103,8 @@ func TestArenaMatchFlow(t *testing.T) { assert.Equal(t, AutoPeriod, arena.MatchState) assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Auto) assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Enabled) - arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec) * time.Second) + arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+ + game.MatchTiming.AutoDurationSec) * time.Second) arena.Update() assert.Equal(t, PausePeriod, arena.MatchState) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto) @@ -103,8 +113,8 @@ func TestArenaMatchFlow(t *testing.T) { assert.Equal(t, PausePeriod, arena.MatchState) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Enabled) - arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec+ - game.MatchTiming.PauseDurationSec) * time.Second) + arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+ + game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec) * time.Second) arena.Update() assert.Equal(t, TeleopPeriod, arena.MatchState) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto) @@ -142,8 +152,9 @@ func TestArenaMatchFlow(t *testing.T) { // Check endgame and match end. arena.MatchStartTime = time.Now(). - Add(-time.Duration(game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+ - game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) * time.Second) + Add(-time.Duration(game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec+ + game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec-game.MatchTiming.EndgameTimeLeftSec) * + time.Second) arena.Update() assert.Equal(t, EndgamePeriod, arena.MatchState) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto) @@ -152,8 +163,9 @@ func TestArenaMatchFlow(t *testing.T) { assert.Equal(t, EndgamePeriod, arena.MatchState) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto) assert.Equal(t, true, arena.AllianceStations["B3"].DsConn.Enabled) - arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec+ - game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec) * time.Second) + arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+ + game.MatchTiming.AutoDurationSec+game.MatchTiming.PauseDurationSec+game.MatchTiming.TeleopDurationSec) * + time.Second) arena.Update() assert.Equal(t, PostMatch, arena.MatchState) assert.Equal(t, false, arena.AllianceStations["B3"].DsConn.Auto) diff --git a/field/driver_station_connection.go b/field/driver_station_connection.go index 4146f80..5f3eebc 100644 --- a/field/driver_station_connection.go +++ b/field/driver_station_connection.go @@ -387,3 +387,25 @@ func (dsConn *DriverStationConnection) handleTcpConnection(arena *Arena) { } } } + +func (dsConn *DriverStationConnection) sendGameSpecificDataPacket(gameSpecificData string) error { + byteData := []byte(gameSpecificData) + size := len(byteData) + packet := make([]byte, size+4) + + packet[0] = 0 // Packet size + packet[1] = byte(size + 2) // Packet size + packet[2] = 28 // Packet type + packet[3] = byte(size) // Data size + + // Fill the rest of the packet with the data. + for i, character := range byteData { + packet[i+4] = character + } + + if dsConn.tcpConn != nil { + _, err := dsConn.tcpConn.Write(packet) + return err + } + return nil +} diff --git a/game/game_specific_data.go b/game/game_specific_data.go new file mode 100644 index 0000000..4a7356b --- /dev/null +++ b/game/game_specific_data.go @@ -0,0 +1,25 @@ +// Copyright 2018 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Logic to generate the 2018 game-specific data. + +package game + +import "math/rand" + +var validGameSpecificDatas = []string{"RRR", "LLL", "RLR", "LRL"} + +// Returns a random configuration. +func GenerateGameSpecificData() string { + return validGameSpecificDatas[rand.Intn(len(validGameSpecificDatas))] +} + +// Returns true if the given game specific data is valid. +func IsValidGameSpecificData(gameSpecificData string) bool { + for _, data := range validGameSpecificDatas { + if data == gameSpecificData { + return true + } + } + return false +} diff --git a/game/game_specific_data_test.go b/game/game_specific_data_test.go new file mode 100644 index 0000000..dc224d1 --- /dev/null +++ b/game/game_specific_data_test.go @@ -0,0 +1,38 @@ +// Copyright 2018 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) + +package game + +import ( + "github.com/stretchr/testify/assert" + "math/rand" + "testing" +) + +func TestGenerateGameSpecificData(t *testing.T) { + rand.Seed(0) + + // Make sure all possibilities are hit at least twice. + assert.Equal(t, "RLR", GenerateGameSpecificData()) + assert.Equal(t, "RLR", GenerateGameSpecificData()) + assert.Equal(t, "LLL", GenerateGameSpecificData()) + assert.Equal(t, "RLR", GenerateGameSpecificData()) + assert.Equal(t, "LRL", GenerateGameSpecificData()) + assert.Equal(t, "RRR", GenerateGameSpecificData()) + assert.Equal(t, "LRL", GenerateGameSpecificData()) + assert.Equal(t, "LLL", GenerateGameSpecificData()) + assert.Equal(t, "RRR", GenerateGameSpecificData()) + assert.Equal(t, "RRR", GenerateGameSpecificData()) +} + +func TestIsValidGameSpecificData(t *testing.T) { + for _, data := range validGameSpecificDatas { + assert.True(t, IsValidGameSpecificData(data)) + } + + assert.False(t, IsValidGameSpecificData("")) + assert.False(t, IsValidGameSpecificData("R")) + assert.False(t, IsValidGameSpecificData("RL")) + assert.False(t, IsValidGameSpecificData("RRL")) + assert.False(t, IsValidGameSpecificData("RRRL")) +} diff --git a/game/match_timing.go b/game/match_timing.go index bcf5466..5207063 100644 --- a/game/match_timing.go +++ b/game/match_timing.go @@ -8,21 +8,23 @@ package game import "time" var MatchTiming = struct { + WarmupDurationSec int AutoDurationSec int PauseDurationSec int TeleopDurationSec int EndgameTimeLeftSec int -}{15, 2, 135, 30} +}{3, 15, 2, 135, 30} func GetAutoEndTime(matchStartTime time.Time) time.Time { - return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec)) + return matchStartTime.Add(time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec) * time.Second) } func GetTeleopStartTime(matchStartTime time.Time) time.Time { - return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec + MatchTiming.PauseDurationSec)) + return matchStartTime.Add(time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec+ + MatchTiming.PauseDurationSec) * time.Second) } func GetMatchEndTime(matchStartTime time.Time) time.Time { - return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+ - MatchTiming.TeleopDurationSec) * time.Second) + return matchStartTime.Add(time.Duration(MatchTiming.WarmupDurationSec+MatchTiming.AutoDurationSec+ + MatchTiming.PauseDurationSec+MatchTiming.TeleopDurationSec) * time.Second) } diff --git a/model/match.go b/model/match.go index d5f8c0f..db3e556 100644 --- a/model/match.go +++ b/model/match.go @@ -34,6 +34,7 @@ type Match struct { Status string StartedAt time.Time Winner string + GameSpecificData string } var ElimRoundNames = map[int]string{1: "F", 2: "SF", 4: "QF", 8: "EF"} diff --git a/model/match_test.go b/model/match_test.go index 55687ab..e069caf 100644 --- a/model/match_test.go +++ b/model/match_test.go @@ -21,7 +21,7 @@ func TestMatchCrud(t *testing.T) { db := setupTestDb(t) match := Match{0, "qualification", "254", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, - 5, false, 6, false, "", time.Now().UTC(), ""} + 5, false, 6, false, "", time.Now().UTC(), "", ""} db.CreateMatch(&match) match2, err := db.GetMatchById(1) assert.Nil(t, err) @@ -46,7 +46,7 @@ func TestTruncateMatches(t *testing.T) { db := setupTestDb(t) match := Match{0, "qualification", "254", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, - 5, false, 6, false, "", time.Now().UTC(), ""} + 5, false, 6, false, "", time.Now().UTC(), "", ""} db.CreateMatch(&match) db.TruncateMatches() match2, err := db.GetMatchById(1) @@ -83,13 +83,13 @@ func TestGetMatchesByType(t *testing.T) { db := setupTestDb(t) match := Match{0, "qualification", "1", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, - 5, false, 6, false, "", time.Now().UTC(), ""} + 5, false, 6, false, "", time.Now().UTC(), "", ""} db.CreateMatch(&match) match2 := Match{0, "practice", "1", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, 5, - false, 6, false, "", time.Now().UTC(), ""} + false, 6, false, "", time.Now().UTC(), "", ""} db.CreateMatch(&match2) match3 := Match{0, "practice", "2", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, 5, - false, 6, false, "", time.Now().UTC(), ""} + false, 6, false, "", time.Now().UTC(), "", ""} db.CreateMatch(&match3) matches, err := db.GetMatchesByType("test") diff --git a/static/audio/match_warmup.wav b/static/audio/match_warmup.wav new file mode 100644 index 0000000..b351d2d Binary files /dev/null and b/static/audio/match_warmup.wav differ diff --git a/static/js/match_play.js b/static/js/match_play.js index 3799a09..6e207d8 100644 --- a/static/js/match_play.js +++ b/static/js/match_play.js @@ -18,7 +18,8 @@ var toggleBypass = function(station) { // Sends a websocket message to start the match. var startMatch = function() { - websocket.send("startMatch", { muteMatchSounds: $("#muteMatchSounds").prop("checked") }); + websocket.send("startMatch", + { muteMatchSounds: $("#muteMatchSounds").prop("checked"), gameSpecificData: $("#gameSpecificData").val() }); }; // Sends a websocket message to abort the match. @@ -135,7 +136,11 @@ var handleStatus = function(data) { $("#plcStatus").text("Not Connected"); $("#plcStatus").attr("data-ready", false); } - $("#fieldEstop").attr("data-ready", !data.FieldEstop) + $("#fieldEstop").attr("data-ready", !data.FieldEstop); + + if (matchStates[data.MatchState] != "PRE_MATCH") { + $("#gameSpecificData").val(data.GameSpecificData); + } }; // Handles a websocket message to update the match time countdown. diff --git a/static/js/match_timing.js b/static/js/match_timing.js index 3760963..a7b3ecb 100644 --- a/static/js/match_timing.js +++ b/static/js/match_timing.js @@ -6,11 +6,12 @@ var matchStates = { 0: "PRE_MATCH", 1: "START_MATCH", - 2: "AUTO_PERIOD", - 3: "PAUSE_PERIOD", - 4: "TELEOP_PERIOD", - 5: "ENDGAME_PERIOD", - 6: "POST_MATCH" + 2: "WARMUP_PERIOD", + 3: "AUTO_PERIOD", + 4: "PAUSE_PERIOD", + 5: "TELEOP_PERIOD", + 6: "ENDGAME_PERIOD", + 7: "POST_MATCH" }; var matchTiming; @@ -28,6 +29,9 @@ var translateMatchTime = function(data, callback) { matchStateText = "PRE-MATCH"; break; case "START_MATCH": + case "WARMUP_PERIOD": + matchStateText = "WARMUP"; + break; case "AUTO_PERIOD": matchStateText = "AUTONOMOUS"; break; @@ -49,14 +53,15 @@ var translateMatchTime = function(data, callback) { var getCountdown = function(matchState, matchTimeSec) { switch (matchStates[matchState]) { case "PRE_MATCH": - return matchTiming.AutoDurationSec; case "START_MATCH": + case "WARMUP_PERIOD": + return matchTiming.AutoDurationSec; case "AUTO_PERIOD": - return matchTiming.AutoDurationSec - matchTimeSec; + return matchTiming.WarmupDurationSec + matchTiming.AutoDurationSec - matchTimeSec; case "TELEOP_PERIOD": case "ENDGAME_PERIOD": - return matchTiming.TeleopDurationSec + matchTiming.AutoDurationSec + matchTiming.PauseDurationSec - - matchTimeSec; + return matchTiming.WarmupDurationSec + matchTiming.AutoDurationSec + matchTiming.TeleopDurationSec + + matchTiming.PauseDurationSec - matchTimeSec; default: return 0; } diff --git a/templates/audience_display.html b/templates/audience_display.html index d465eef..8f1fc1a 100644 --- a/templates/audience_display.html +++ b/templates/audience_display.html @@ -158,6 +158,7 @@ {{"{{/each}}"}} + diff --git a/templates/match_play.html b/templates/match_play.html index 1cabbf7..70aaa58 100644 --- a/templates/match_play.html +++ b/templates/match_play.html @@ -167,7 +167,7 @@
- Alliance Station Display +

Alliance Station Display

+
+

Game-Specific Data

+
{{if .EventSettings.PlcAddress}} PLC Status diff --git a/web/alliance_station_display_test.go b/web/alliance_station_display_test.go index 238064e..689f7b1 100644 --- a/web/alliance_station_display_test.go +++ b/web/alliance_station_display_test.go @@ -4,6 +4,8 @@ package web import ( + "fmt" + "github.com/Team254/cheesy-arena/game" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "sync" @@ -59,7 +61,11 @@ func TestAllianceStationDisplayWebsocket(t *testing.T) { web.arena.AllianceStations["B3"].Bypass = true web.arena.StartMatch() web.arena.Update() + readWebsocketType(t, ws, "matchTime") + web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second) + web.arena.Update() messages := readWebsocketMultiple(t, ws, 2) + fmt.Println(messages) _, ok := messages["status"] assert.True(t, ok) _, ok = messages["matchTime"] diff --git a/web/audience_display_test.go b/web/audience_display_test.go index fbe00f1..201b3a9 100644 --- a/web/audience_display_test.go +++ b/web/audience_display_test.go @@ -55,7 +55,7 @@ func TestAudienceDisplayWebsocket(t *testing.T) { } sound, ok := messages["playSound"] if assert.True(t, ok) { - assert.Equal(t, "match-start", sound) + assert.Equal(t, "match-warmup", sound) } _, ok = messages["matchTime"] assert.True(t, ok) diff --git a/web/match_play.go b/web/match_play.go index bf38143..8213b5f 100644 --- a/web/match_play.go +++ b/web/match_play.go @@ -333,7 +333,8 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request web.arena.AllianceStations[station].Bypass = !web.arena.AllianceStations[station].Bypass case "startMatch": args := struct { - MuteMatchSounds bool + MuteMatchSounds bool + GameSpecificData string }{} err = mapstructure.Decode(data, &args) if err != nil { @@ -341,6 +342,7 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request continue } web.arena.MuteMatchSounds = args.MuteMatchSounds + web.arena.CurrentMatch.GameSpecificData = args.GameSpecificData err = web.arena.StartMatch() if err != nil { websocket.WriteError(err.Error()) diff --git a/web/match_play_test.go b/web/match_play_test.go index 32ffa06..8295499 100644 --- a/web/match_play_test.go +++ b/web/match_play_test.go @@ -351,43 +351,47 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) { web.arena.AllianceStations["B3"].Bypass = true web.arena.StartMatch() web.arena.Update() - messages := readWebsocketMultiple(t, ws, 4) - statusReceived, matchTime := getStatusMatchTime(t, messages) - assert.Equal(t, true, statusReceived) - assert.Equal(t, 2, matchTime.MatchState) - assert.Equal(t, 0, matchTime.MatchTimeSec) - _, ok := messages["setAudienceDisplay"] + messages := readWebsocketMultiple(t, ws, 3) + _, ok := messages["matchTime"] + assert.True(t, ok) + _, ok = messages["setAudienceDisplay"] assert.True(t, ok) _, ok = messages["setAllianceStationDisplay"] + web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec) * time.Second) + web.arena.Update() + messages = readWebsocketMultiple(t, ws, 2) + statusReceived, matchTime := getStatusMatchTime(t, messages) + assert.Equal(t, true, statusReceived) + assert.Equal(t, 3, matchTime.MatchState) + assert.Equal(t, 3, matchTime.MatchTimeSec) assert.True(t, ok) web.arena.ScoringStatusNotifier.Notify(nil) readWebsocketType(t, ws, "scoringStatus") // Should get a tick notification when an integer second threshold is crossed. - web.arena.MatchStartTime = time.Now().Add(-time.Second + 10*time.Millisecond) // Not crossed yet - web.arena.Update() web.arena.MatchStartTime = time.Now().Add(-time.Second - 10*time.Millisecond) // Crossed web.arena.Update() + err = mapstructure.Decode(readWebsocketType(t, ws, "matchTime"), &matchTime) + assert.Nil(t, err) + assert.Equal(t, 3, matchTime.MatchState) + assert.Equal(t, 1, matchTime.MatchTimeSec) web.arena.MatchStartTime = time.Now().Add(-2*time.Second + 10*time.Millisecond) // Not crossed yet web.arena.Update() web.arena.MatchStartTime = time.Now().Add(-2*time.Second - 10*time.Millisecond) // Crossed web.arena.Update() err = mapstructure.Decode(readWebsocketType(t, ws, "matchTime"), &matchTime) assert.Nil(t, err) - assert.Equal(t, 2, matchTime.MatchState) - assert.Equal(t, 1, matchTime.MatchTimeSec) - err = mapstructure.Decode(readWebsocketType(t, ws, "matchTime"), &matchTime) - assert.Nil(t, err) - assert.Equal(t, 2, matchTime.MatchState) + assert.Equal(t, 3, matchTime.MatchState) assert.Equal(t, 2, matchTime.MatchTimeSec) // Check across a match state boundary. - web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.AutoDurationSec) * time.Second) + web.arena.MatchStartTime = time.Now().Add(-time.Duration(game.MatchTiming.WarmupDurationSec+ + game.MatchTiming.AutoDurationSec) * time.Second) web.arena.Update() statusReceived, matchTime = readWebsocketStatusMatchTime(t, ws) assert.Equal(t, true, statusReceived) - assert.Equal(t, 3, matchTime.MatchState) - assert.Equal(t, game.MatchTiming.AutoDurationSec, matchTime.MatchTimeSec) + assert.Equal(t, 4, matchTime.MatchState) + assert.Equal(t, game.MatchTiming.WarmupDurationSec+game.MatchTiming.AutoDurationSec, matchTime.MatchTimeSec) } // Handles the status and matchTime messages arriving in either order.