mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Add generation and sending of game-specific data.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
25
game/game_specific_data.go
Normal file
25
game/game_specific_data.go
Normal file
@@ -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
|
||||
}
|
||||
38
game/game_specific_data_test.go
Normal file
38
game/game_specific_data_test.go
Normal file
@@ -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"))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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")
|
||||
|
||||
BIN
static/audio/match_warmup.wav
Normal file
BIN
static/audio/match_warmup.wav
Normal file
Binary file not shown.
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@
|
||||
{{"{{/each}}"}}
|
||||
</table>
|
||||
</script>
|
||||
<audio id="match-warmup" src="/static/audio/match_warmup.wav" preload="auto"></audio>
|
||||
<audio id="match-start" src="/static/audio/match_start.wav" preload="auto"></audio>
|
||||
<audio id="match-end" src="/static/audio/match_end.wav" preload="auto"></audio>
|
||||
<audio id="match-abort" src="/static/audio/match_abort.mp3" preload="auto"></audio>
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
Alliance Station Display
|
||||
<p>Alliance Station Display</p>
|
||||
<div class="form-group">
|
||||
<div class="radio">
|
||||
<label>
|
||||
@@ -187,6 +187,10 @@
|
||||
onclick="setAllianceStationDisplay();">Logo
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<p>Game-Specific Data</p>
|
||||
<input type="text" id="gameSpecificData" size="10"
|
||||
{{if eq .CurrentMatchType "qualification" }}disabled{{end}} />
|
||||
</div>
|
||||
{{if .EventSettings.PlcAddress}}
|
||||
PLC Status
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user