mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Remove game-specific scoring
This commit is contained in:
7
web/alliance_selection.go
Normal file → Executable file
7
web/alliance_selection.go
Normal file → Executable file
@@ -194,13 +194,6 @@ func (web *Web) allianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.
|
||||
return
|
||||
}
|
||||
|
||||
// Reset yellow cards.
|
||||
err = tournament.CalculateTeamCards(web.arena.Database, "elimination")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Back up the database.
|
||||
err = web.arena.Database.Backup(web.arena.EventSettings.Name, "post_alliance_selection")
|
||||
if err != nil {
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestAllianceSelection(t *testing.T) {
|
||||
assert.Contains(t, recorder.Body.String(), ">110<")
|
||||
|
||||
// Finalize alliance selection.
|
||||
web.arena.Database.CreateTeam(&model.Team{Id: 254, YellowCard: true})
|
||||
web.arena.Database.CreateTeam(&model.Team{Id: 254})
|
||||
recorder = web.postHttpResponse("/alliance_selection/finalize", "startTime=2014-01-01 01:00:00 PM")
|
||||
assert.Equal(t, 303, recorder.Code)
|
||||
alliances, err := web.arena.Database.GetAllAlliances()
|
||||
@@ -88,8 +88,6 @@ func TestAllianceSelection(t *testing.T) {
|
||||
matches, err := web.arena.Database.GetMatchesByType("elimination")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 6, len(matches))
|
||||
team, _ := web.arena.Database.GetTeamById(254)
|
||||
assert.False(t, team.YellowCard)
|
||||
}
|
||||
|
||||
func TestAllianceSelectionErrors(t *testing.T) {
|
||||
|
||||
4
web/api.go
Normal file → Executable file
4
web/api.go
Normal file → Executable file
@@ -54,8 +54,8 @@ func (web *Web) matchesApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var matchResultWithSummary *MatchResultWithSummary
|
||||
if matchResult != nil {
|
||||
matchResultWithSummary = &MatchResultWithSummary{MatchResult: *matchResult}
|
||||
matchResultWithSummary.RedSummary = matchResult.RedScoreSummary(true)
|
||||
matchResultWithSummary.BlueSummary = matchResult.BlueScoreSummary(true)
|
||||
matchResultWithSummary.RedSummary = matchResult.RedScoreSummary()
|
||||
matchResultWithSummary.BlueSummary = matchResult.BlueScoreSummary()
|
||||
}
|
||||
matchesWithResults[i].Result = matchResultWithSummary
|
||||
}
|
||||
|
||||
31
web/match_play.go
Normal file → Executable file
31
web/match_play.go
Normal file → Executable file
@@ -76,10 +76,13 @@ func (web *Web) matchPlayHandler(w http.ResponseWriter, r *http.Request) {
|
||||
MatchesByType map[string]MatchPlayList
|
||||
CurrentMatchType string
|
||||
Match *model.Match
|
||||
RedScore *game.Score
|
||||
BlueScore *game.Score
|
||||
AllowSubstitution bool
|
||||
IsReplay bool
|
||||
PlcArmorBlockStatuses map[string]bool
|
||||
}{web.arena.EventSettings, web.arena.Plc.IsEnabled(), matchesByType, currentMatchType, web.arena.CurrentMatch,
|
||||
web.arena.RedScore, web.arena.BlueScore,
|
||||
web.arena.CurrentMatch.ShouldAllowSubstitution(), isReplay, web.arena.Plc.GetArmorBlockStatuses()}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
@@ -177,7 +180,7 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request
|
||||
|
||||
// Subscribe the websocket to the notifiers whose messages will be passed on to the client, in a separate goroutine.
|
||||
go ws.HandleNotifiers(web.arena.MatchTimingNotifier, web.arena.ArenaStatusNotifier, web.arena.MatchTimeNotifier,
|
||||
web.arena.RealtimeScoreNotifier, web.arena.ScoringStatusNotifier, web.arena.AudienceDisplayModeNotifier,
|
||||
web.arena.RealtimeScoreNotifier, web.arena.AudienceDisplayModeNotifier,
|
||||
web.arena.AllianceStationDisplayModeNotifier, web.arena.EventStatusNotifier)
|
||||
|
||||
// Loop, waiting for commands and responding to them, until the client closes the connection.
|
||||
@@ -308,6 +311,15 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request
|
||||
ws.WriteError(err.Error())
|
||||
continue
|
||||
}
|
||||
case "updateRealtimeScore":
|
||||
args := data.(map[string]interface{})
|
||||
web.arena.BlueScore.AutoPoints = int(args["blueAuto"].(float64))
|
||||
web.arena.RedScore.AutoPoints = int(args["redAuto"].(float64))
|
||||
web.arena.BlueScore.TeleopPoints = int(args["blueTeleop"].(float64))
|
||||
web.arena.RedScore.TeleopPoints = int(args["redTeleop"].(float64))
|
||||
web.arena.BlueScore.EndgamePoints = int(args["blueEndgame"].(float64))
|
||||
web.arena.RedScore.EndgamePoints = int(args["redEndgame"].(float64))
|
||||
web.arena.RealtimeScoreNotifier.Notify()
|
||||
default:
|
||||
ws.WriteError(fmt.Sprintf("Invalid message type '%s'.", messageType))
|
||||
continue
|
||||
@@ -326,11 +338,6 @@ func (web *Web) matchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request
|
||||
func (web *Web) commitMatchScore(match *model.Match, matchResult *model.MatchResult, isMatchReviewEdit bool) error {
|
||||
var updatedRankings game.Rankings
|
||||
|
||||
if match.Type == "elimination" {
|
||||
// Adjust the score if necessary for an elimination DQ.
|
||||
matchResult.CorrectEliminationScore()
|
||||
}
|
||||
|
||||
if match.Type != "test" {
|
||||
if matchResult.PlayNumber == 0 {
|
||||
// Determine the play number for this new match result.
|
||||
@@ -359,8 +366,8 @@ func (web *Web) commitMatchScore(match *model.Match, matchResult *model.MatchRes
|
||||
|
||||
// Update and save the match record to the database.
|
||||
match.ScoreCommittedAt = time.Now()
|
||||
redScore := matchResult.RedScoreSummary(true)
|
||||
blueScore := matchResult.BlueScoreSummary(true)
|
||||
redScore := matchResult.RedScoreSummary()
|
||||
blueScore := matchResult.BlueScoreSummary()
|
||||
if redScore.Score > blueScore.Score {
|
||||
match.Status = model.RedWonMatch
|
||||
} else if redScore.Score < blueScore.Score {
|
||||
@@ -373,11 +380,6 @@ func (web *Web) commitMatchScore(match *model.Match, matchResult *model.MatchRes
|
||||
return err
|
||||
}
|
||||
|
||||
if match.ShouldUpdateCards() {
|
||||
// Regenerate the residual yellow cards that teams may carry.
|
||||
tournament.CalculateTeamCards(web.arena.Database, match.Type)
|
||||
}
|
||||
|
||||
if match.ShouldUpdateRankings() {
|
||||
// Recalculate all the rankings.
|
||||
rankings, err := tournament.CalculateRankings(web.arena.Database, isMatchReviewEdit)
|
||||
@@ -458,8 +460,7 @@ func (web *Web) commitMatchScore(match *model.Match, matchResult *model.MatchRes
|
||||
|
||||
func (web *Web) getCurrentMatchResult() *model.MatchResult {
|
||||
return &model.MatchResult{MatchId: web.arena.CurrentMatch.Id, MatchType: web.arena.CurrentMatch.Type,
|
||||
RedScore: &web.arena.RedRealtimeScore.CurrentScore, BlueScore: &web.arena.BlueRealtimeScore.CurrentScore,
|
||||
RedCards: web.arena.RedRealtimeScore.Cards, BlueCards: web.arena.BlueRealtimeScore.Cards}
|
||||
RedScore: web.arena.RedScore, BlueScore: web.arena.BlueScore}
|
||||
}
|
||||
|
||||
// Saves the realtime result as the final score for the match currently loaded into the arena.
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/tournament"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
gorillawebsocket "github.com/gorilla/websocket"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
@@ -133,7 +132,7 @@ func TestCommitMatch(t *testing.T) {
|
||||
web.arena.Database.CreateMatch(match)
|
||||
matchResult = model.NewMatchResult()
|
||||
matchResult.MatchId = match.Id
|
||||
matchResult.BlueScore = &game.Score{ExitedInitiationLine: [3]bool{true, false, false}}
|
||||
matchResult.BlueScore = &game.Score{AutoPoints: 10}
|
||||
err = web.commitMatchScore(match, matchResult, true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, matchResult.PlayNumber)
|
||||
@@ -142,7 +141,7 @@ func TestCommitMatch(t *testing.T) {
|
||||
|
||||
matchResult = model.NewMatchResult()
|
||||
matchResult.MatchId = match.Id
|
||||
matchResult.RedScore = &game.Score{ExitedInitiationLine: [3]bool{true, false, true}}
|
||||
matchResult.RedScore = &game.Score{AutoPoints: 20}
|
||||
err = web.commitMatchScore(match, matchResult, true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, matchResult.PlayNumber)
|
||||
@@ -175,10 +174,8 @@ func TestCommitEliminationTie(t *testing.T) {
|
||||
match := &model.Match{Id: 0, Type: "qualification", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6}
|
||||
web.arena.Database.CreateMatch(match)
|
||||
matchResult := &model.MatchResult{
|
||||
MatchId: match.Id,
|
||||
RedScore: &game.Score{
|
||||
TeleopCellsInner: [4]int{1, 2, 0, 0},
|
||||
Fouls: []game.Foul{{RuleId: 1}, {RuleId: 2}, {RuleId: 4}}},
|
||||
MatchId: match.Id,
|
||||
RedScore: &game.Score{},
|
||||
BlueScore: &game.Score{},
|
||||
}
|
||||
err := web.commitMatchScore(match, matchResult, true)
|
||||
@@ -192,54 +189,6 @@ func TestCommitEliminationTie(t *testing.T) {
|
||||
assert.Equal(t, model.TieMatch, match.Status) // No elimination tiebreakers.
|
||||
}
|
||||
|
||||
func TestCommitCards(t *testing.T) {
|
||||
web := setupTestWeb(t)
|
||||
|
||||
// Check that a yellow card sticks with a team.
|
||||
team := &model.Team{Id: 5}
|
||||
web.arena.Database.CreateTeam(team)
|
||||
match := &model.Match{Id: 0, Type: "qualification", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6}
|
||||
web.arena.Database.CreateMatch(match)
|
||||
matchResult := model.NewMatchResult()
|
||||
matchResult.MatchId = match.Id
|
||||
matchResult.BlueCards = map[string]string{"5": "yellow"}
|
||||
err := web.commitMatchScore(match, matchResult, true)
|
||||
assert.Nil(t, err)
|
||||
team, _ = web.arena.Database.GetTeamById(5)
|
||||
assert.True(t, team.YellowCard)
|
||||
|
||||
// Check that editing a match result removes a yellow card from a team.
|
||||
matchResult = model.NewMatchResult()
|
||||
matchResult.MatchId = match.Id
|
||||
err = web.commitMatchScore(match, matchResult, true)
|
||||
assert.Nil(t, err)
|
||||
team, _ = web.arena.Database.GetTeamById(5)
|
||||
assert.False(t, team.YellowCard)
|
||||
|
||||
// Check that a red card causes a yellow card to stick with a team.
|
||||
matchResult = model.NewMatchResult()
|
||||
matchResult.MatchId = match.Id
|
||||
matchResult.BlueCards = map[string]string{"5": "red"}
|
||||
err = web.commitMatchScore(match, matchResult, true)
|
||||
assert.Nil(t, err)
|
||||
team, _ = web.arena.Database.GetTeamById(5)
|
||||
assert.True(t, team.YellowCard)
|
||||
|
||||
// Check that a red card in eliminations zeroes out the score.
|
||||
tournament.CreateTestAlliances(web.arena.Database, 2)
|
||||
match.Type = "elimination"
|
||||
match.ElimRedAlliance = 1
|
||||
match.ElimBlueAlliance = 2
|
||||
web.arena.Database.SaveMatch(match)
|
||||
matchResult = model.BuildTestMatchResult(match.Id, 10)
|
||||
matchResult.MatchType = match.Type
|
||||
matchResult.RedCards = map[string]string{"1": "red"}
|
||||
err = web.commitMatchScore(match, matchResult, true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, matchResult.RedScoreSummary(true).Score)
|
||||
assert.NotEqual(t, 0, matchResult.BlueScoreSummary(true).Score)
|
||||
}
|
||||
|
||||
func TestMatchPlayWebsocketCommands(t *testing.T) {
|
||||
web := setupTestWeb(t)
|
||||
|
||||
@@ -255,7 +204,6 @@ func TestMatchPlayWebsocketCommands(t *testing.T) {
|
||||
readWebsocketType(t, ws, "arenaStatus")
|
||||
readWebsocketType(t, ws, "matchTime")
|
||||
readWebsocketType(t, ws, "realtimeScore")
|
||||
readWebsocketType(t, ws, "scoringStatus")
|
||||
readWebsocketType(t, ws, "audienceDisplayMode")
|
||||
readWebsocketType(t, ws, "allianceStationDisplayMode")
|
||||
readWebsocketType(t, ws, "eventStatus")
|
||||
@@ -310,12 +258,12 @@ func TestMatchPlayWebsocketCommands(t *testing.T) {
|
||||
readWebsocketType(t, ws, "audienceDisplayMode")
|
||||
readWebsocketType(t, ws, "allianceStationDisplayMode")
|
||||
assert.Equal(t, field.PostMatch, web.arena.MatchState)
|
||||
web.arena.RedRealtimeScore.CurrentScore.TeleopCellsOuter = [4]int{1, 1, 1, 4}
|
||||
web.arena.BlueRealtimeScore.CurrentScore.ExitedInitiationLine = [3]bool{true, false, true}
|
||||
web.arena.RedScore.TeleopPoints = 30
|
||||
web.arena.BlueScore.EndgamePoints = 45
|
||||
ws.Write("commitResults", nil)
|
||||
readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay
|
||||
assert.Equal(t, [4]int{1, 1, 1, 4}, web.arena.SavedMatchResult.RedScore.TeleopCellsOuter)
|
||||
assert.Equal(t, [3]bool{true, false, true}, web.arena.SavedMatchResult.BlueScore.ExitedInitiationLine)
|
||||
assert.Equal(t, 30, web.arena.SavedMatchResult.RedScore.TeleopPoints)
|
||||
assert.Equal(t, 45, web.arena.SavedMatchResult.BlueScore.EndgamePoints)
|
||||
assert.Equal(t, field.PreMatch, web.arena.MatchState)
|
||||
ws.Write("discardResults", nil)
|
||||
readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay
|
||||
@@ -347,7 +295,6 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) {
|
||||
readWebsocketType(t, ws, "arenaStatus")
|
||||
readWebsocketType(t, ws, "matchTime")
|
||||
readWebsocketType(t, ws, "realtimeScore")
|
||||
readWebsocketType(t, ws, "scoringStatus")
|
||||
readWebsocketType(t, ws, "audienceDisplayMode")
|
||||
readWebsocketType(t, ws, "allianceStationDisplayMode")
|
||||
readWebsocketType(t, ws, "eventStatus")
|
||||
@@ -376,8 +323,6 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) {
|
||||
assert.Equal(t, true, statusReceived)
|
||||
assert.Equal(t, field.AutoPeriod, matchTime.MatchState)
|
||||
assert.Equal(t, 3, matchTime.MatchTimeSec)
|
||||
web.arena.ScoringStatusNotifier.Notify()
|
||||
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) // Crossed
|
||||
|
||||
17
web/match_review.go
Normal file → Executable file
17
web/match_review.go
Normal file → Executable file
@@ -7,7 +7,6 @@ package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
@@ -92,8 +91,7 @@ func (web *Web) matchReviewEditGetHandler(w http.ResponseWriter, r *http.Request
|
||||
*model.EventSettings
|
||||
Match *model.Match
|
||||
MatchResultJson *model.MatchResultDb
|
||||
Rules map[int]*game.Rule
|
||||
}{web.arena.EventSettings, match, matchResultJson, game.GetAllRules()}
|
||||
}{web.arena.EventSettings, match, matchResultJson}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
@@ -115,8 +113,7 @@ func (web *Web) matchReviewEditPostHandler(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
matchResultJson := model.MatchResultDb{Id: matchResult.Id, MatchId: match.Id, PlayNumber: matchResult.PlayNumber,
|
||||
MatchType: matchResult.MatchType, RedScoreJson: r.PostFormValue("redScoreJson"),
|
||||
BlueScoreJson: r.PostFormValue("blueScoreJson"), RedCardsJson: r.PostFormValue("redCardsJson"),
|
||||
BlueCardsJson: r.PostFormValue("blueCardsJson")}
|
||||
BlueScoreJson: r.PostFormValue("blueScoreJson")}
|
||||
|
||||
// Deserialize the JSON using the same mechanism as to store scoring information in the database.
|
||||
matchResult, err = matchResultJson.Deserialize()
|
||||
@@ -127,10 +124,8 @@ func (web *Web) matchReviewEditPostHandler(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
if isCurrent {
|
||||
// If editing the current match, just save it back to memory.
|
||||
web.arena.RedRealtimeScore.CurrentScore = *matchResult.RedScore
|
||||
web.arena.BlueRealtimeScore.CurrentScore = *matchResult.BlueScore
|
||||
web.arena.RedRealtimeScore.Cards = matchResult.RedCards
|
||||
web.arena.BlueRealtimeScore.Cards = matchResult.BlueCards
|
||||
*web.arena.RedScore = *matchResult.RedScore
|
||||
*web.arena.BlueScore = *matchResult.BlueScore
|
||||
|
||||
http.Redirect(w, r, "/match_play", 303)
|
||||
} else {
|
||||
@@ -193,8 +188,8 @@ func (web *Web) buildMatchReviewList(matchType string) ([]MatchReviewListItem, e
|
||||
return []MatchReviewListItem{}, err
|
||||
}
|
||||
if matchResult != nil {
|
||||
matchReviewList[i].RedScore = matchResult.RedScoreSummary(true).Score
|
||||
matchReviewList[i].BlueScore = matchResult.BlueScoreSummary(true).Score
|
||||
matchReviewList[i].RedScore = matchResult.RedScoreSummary().Score
|
||||
matchReviewList[i].BlueScore = matchResult.BlueScoreSummary().Score
|
||||
}
|
||||
switch match.Status {
|
||||
case model.RedWonMatch:
|
||||
|
||||
@@ -49,8 +49,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
|
||||
recorder := web.getHttpResponse("/match_review")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), ">QF4-3<")
|
||||
assert.Contains(t, recorder.Body.String(), ">217<") // The red score
|
||||
assert.Contains(t, recorder.Body.String(), ">252<") // The blue score
|
||||
assert.Contains(t, recorder.Body.String(), ">155<") // The red score
|
||||
assert.Contains(t, recorder.Body.String(), ">80<") // The blue score
|
||||
|
||||
// Check response for non-existent match.
|
||||
recorder = web.getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345))
|
||||
@@ -62,8 +62,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
|
||||
assert.Contains(t, recorder.Body.String(), " QF4-3 ")
|
||||
|
||||
// Update the score to something else.
|
||||
postBody := "redScoreJson={\"EndgameStatuses\":[0,2,1]}&blueScoreJson={\"AutoCellsOuter\":[3, 4]," +
|
||||
"\"Fouls\":[{\"TeamId\":973,\"RuleId\":1}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
|
||||
postBody := "redScoreJson={\"AutoPoints\":45,\"TeleopPoints\":80,\"EndgamePoints\":10}" +
|
||||
"&blueScoreJson={\"AutoPoints\":15,\"TeleopPoints\":60,\"EndgamePoints\":50}"
|
||||
recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
|
||||
assert.Equal(t, 303, recorder.Code)
|
||||
|
||||
@@ -71,8 +71,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
|
||||
recorder = web.getHttpResponse("/match_review")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), ">QF4-3<")
|
||||
assert.Contains(t, recorder.Body.String(), ">33<") // The red score
|
||||
assert.Contains(t, recorder.Body.String(), ">28<") // The blue score
|
||||
assert.Contains(t, recorder.Body.String(), ">135<") // The red score
|
||||
assert.Contains(t, recorder.Body.String(), ">125<") // The blue score
|
||||
}
|
||||
|
||||
func TestMatchReviewCreateNewResult(t *testing.T) {
|
||||
@@ -94,8 +94,8 @@ func TestMatchReviewCreateNewResult(t *testing.T) {
|
||||
assert.Contains(t, recorder.Body.String(), " QF4-3 ")
|
||||
|
||||
// Update the score to something else.
|
||||
postBody := "redScoreJson={\"TeleopCellsBottom\":[5,1,7,2]}&blueScoreJson={\"TeleopCellsInner\":[2,2,2,2]," +
|
||||
"\"Fouls\":[{\"TeamId\":973,\"RuleId\":1}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
|
||||
postBody := "redScoreJson={\"AutoPoints\":10,\"TeleopPoints\":20,\"EndgamePoints\":30}" +
|
||||
"&blueScoreJson={\"AutoPoints\":40,\"TeleopPoints\":50,\"EndgamePoints\":60}"
|
||||
recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
|
||||
assert.Equal(t, 303, recorder.Code)
|
||||
|
||||
@@ -103,6 +103,6 @@ func TestMatchReviewCreateNewResult(t *testing.T) {
|
||||
recorder = web.getHttpResponse("/match_review")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), ">QF4-3<")
|
||||
assert.Contains(t, recorder.Body.String(), ">18<") // The red score
|
||||
assert.Contains(t, recorder.Body.String(), ">24<") // The blue score
|
||||
assert.Contains(t, recorder.Body.String(), ">60<") // The red score
|
||||
assert.Contains(t, recorder.Body.String(), ">150<") // The blue score
|
||||
}
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Web handlers for the referee interface.
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Renders the referee interface for assigning fouls.
|
||||
func (web *Web) refereePanelHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !web.userIsAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
template, err := web.parseFiles("templates/referee_panel.html")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
match := web.arena.CurrentMatch
|
||||
matchType := match.CapitalizedType()
|
||||
red1 := web.arena.AllianceStations["R1"].Team
|
||||
if red1 == nil {
|
||||
red1 = &model.Team{}
|
||||
}
|
||||
red2 := web.arena.AllianceStations["R2"].Team
|
||||
if red2 == nil {
|
||||
red2 = &model.Team{}
|
||||
}
|
||||
red3 := web.arena.AllianceStations["R3"].Team
|
||||
if red3 == nil {
|
||||
red3 = &model.Team{}
|
||||
}
|
||||
blue1 := web.arena.AllianceStations["B1"].Team
|
||||
if blue1 == nil {
|
||||
blue1 = &model.Team{}
|
||||
}
|
||||
blue2 := web.arena.AllianceStations["B2"].Team
|
||||
if blue2 == nil {
|
||||
blue2 = &model.Team{}
|
||||
}
|
||||
blue3 := web.arena.AllianceStations["B3"].Team
|
||||
if blue3 == nil {
|
||||
blue3 = &model.Team{}
|
||||
}
|
||||
data := struct {
|
||||
*model.EventSettings
|
||||
MatchType string
|
||||
MatchDisplayName string
|
||||
Red1 *model.Team
|
||||
Red2 *model.Team
|
||||
Red3 *model.Team
|
||||
Blue1 *model.Team
|
||||
Blue2 *model.Team
|
||||
Blue3 *model.Team
|
||||
RedFouls []game.Foul
|
||||
BlueFouls []game.Foul
|
||||
RedCards map[string]string
|
||||
BlueCards map[string]string
|
||||
Rules map[int]*game.Rule
|
||||
EntryEnabled bool
|
||||
}{web.arena.EventSettings, matchType, match.DisplayName, red1, red2, red3, blue1, blue2, blue3,
|
||||
web.arena.RedRealtimeScore.CurrentScore.Fouls, web.arena.BlueRealtimeScore.CurrentScore.Fouls,
|
||||
web.arena.RedRealtimeScore.Cards, web.arena.BlueRealtimeScore.Cards, game.GetAllRules(),
|
||||
!(web.arena.RedRealtimeScore.FoulsCommitted && web.arena.BlueRealtimeScore.FoulsCommitted)}
|
||||
err = template.ExecuteTemplate(w, "referee_panel.html", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// The websocket endpoint for the refereee interface client to send control commands and receive status updates.
|
||||
func (web *Web) refereePanelWebsocketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !web.userIsAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
ws, err := websocket.NewWebsocket(w, r)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
// Subscribe the websocket to the notifiers whose messages will be passed on to the client, in a separate goroutine.
|
||||
go ws.HandleNotifiers(web.arena.MatchLoadNotifier, web.arena.ReloadDisplaysNotifier)
|
||||
|
||||
// Loop, waiting for commands and responding to them, until the client closes the connection.
|
||||
for {
|
||||
messageType, data, err := ws.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// Client has closed the connection; nothing to do here.
|
||||
return
|
||||
}
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch messageType {
|
||||
case "addFoul":
|
||||
args := struct {
|
||||
Alliance string
|
||||
TeamId int
|
||||
RuleId int
|
||||
}{}
|
||||
err = mapstructure.Decode(data, &args)
|
||||
if err != nil {
|
||||
ws.WriteError(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the foul to the correct alliance's list.
|
||||
foul := game.Foul{RuleId: args.RuleId, TeamId: args.TeamId, TimeInMatchSec: web.arena.MatchTimeSec()}
|
||||
if args.Alliance == "red" {
|
||||
web.arena.RedRealtimeScore.CurrentScore.Fouls =
|
||||
append(web.arena.RedRealtimeScore.CurrentScore.Fouls, foul)
|
||||
} else {
|
||||
web.arena.BlueRealtimeScore.CurrentScore.Fouls =
|
||||
append(web.arena.BlueRealtimeScore.CurrentScore.Fouls, foul)
|
||||
}
|
||||
web.arena.RealtimeScoreNotifier.Notify()
|
||||
case "deleteFoul":
|
||||
args := struct {
|
||||
Alliance string
|
||||
TeamId int
|
||||
RuleId int
|
||||
TimeInMatchSec float64
|
||||
}{}
|
||||
err = mapstructure.Decode(data, &args)
|
||||
if err != nil {
|
||||
ws.WriteError(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the foul from the correct alliance's list.
|
||||
deleteFoul := game.Foul{RuleId: args.RuleId, TeamId: args.TeamId, TimeInMatchSec: args.TimeInMatchSec}
|
||||
var fouls *[]game.Foul
|
||||
if args.Alliance == "red" {
|
||||
fouls = &web.arena.RedRealtimeScore.CurrentScore.Fouls
|
||||
} else {
|
||||
fouls = &web.arena.BlueRealtimeScore.CurrentScore.Fouls
|
||||
}
|
||||
for i, foul := range *fouls {
|
||||
if foul == deleteFoul {
|
||||
*fouls = append((*fouls)[:i], (*fouls)[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
web.arena.RealtimeScoreNotifier.Notify()
|
||||
case "card":
|
||||
args := struct {
|
||||
Alliance string
|
||||
TeamId int
|
||||
Card string
|
||||
}{}
|
||||
err = mapstructure.Decode(data, &args)
|
||||
if err != nil {
|
||||
ws.WriteError(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// Set the card in the correct alliance's score.
|
||||
var cards map[string]string
|
||||
if args.Alliance == "red" {
|
||||
cards = web.arena.RedRealtimeScore.Cards
|
||||
} else {
|
||||
cards = web.arena.BlueRealtimeScore.Cards
|
||||
}
|
||||
cards[strconv.Itoa(args.TeamId)] = args.Card
|
||||
continue
|
||||
case "signalVolunteers":
|
||||
if web.arena.MatchState != field.PostMatch {
|
||||
// Don't allow clearing the field until the match is over.
|
||||
continue
|
||||
}
|
||||
web.arena.FieldVolunteers = true
|
||||
continue // Don't reload.
|
||||
case "signalReset":
|
||||
if web.arena.MatchState != field.PostMatch {
|
||||
// Don't allow clearing the field until the match is over.
|
||||
continue
|
||||
}
|
||||
web.arena.FieldReset = true
|
||||
web.arena.AllianceStationDisplayMode = "fieldReset"
|
||||
web.arena.AllianceStationDisplayModeNotifier.Notify()
|
||||
continue // Don't reload.
|
||||
case "commitMatch":
|
||||
if web.arena.MatchState != field.PostMatch {
|
||||
// Don't allow committing the fouls until the match is over.
|
||||
continue
|
||||
}
|
||||
web.arena.RedRealtimeScore.FoulsCommitted = true
|
||||
web.arena.BlueRealtimeScore.FoulsCommitted = true
|
||||
web.arena.FieldReset = true
|
||||
web.arena.AllianceStationDisplayMode = "fieldReset"
|
||||
web.arena.AllianceStationDisplayModeNotifier.Notify()
|
||||
web.arena.ScoringStatusNotifier.Notify()
|
||||
default:
|
||||
ws.WriteError(fmt.Sprintf("Invalid message type '%s'.", messageType))
|
||||
continue
|
||||
}
|
||||
|
||||
// Force a reload of the client to render the updated foul list.
|
||||
err = ws.WriteNotifier(web.arena.ReloadDisplaysNotifier)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
gorillawebsocket "github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRefereePanel(t *testing.T) {
|
||||
web := setupTestWeb(t)
|
||||
|
||||
recorder := web.getHttpResponse("/panels/referee")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "Referee Panel - Untitled Event - Cheesy Arena")
|
||||
}
|
||||
|
||||
func TestRefereePanelWebsocket(t *testing.T) {
|
||||
web := setupTestWeb(t)
|
||||
|
||||
server, wsUrl := web.startTestServer()
|
||||
defer server.Close()
|
||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/panels/referee/websocket", nil)
|
||||
assert.Nil(t, err)
|
||||
defer conn.Close()
|
||||
ws := websocket.NewTestWebsocket(conn)
|
||||
|
||||
// Should get a few status updates right after connection.
|
||||
readWebsocketType(t, ws, "matchLoad")
|
||||
|
||||
// Test foul addition.
|
||||
foulData := struct {
|
||||
Alliance string
|
||||
TeamId int
|
||||
RuleId int
|
||||
TimeInMatchSec float64
|
||||
}{"red", 256, 1, 0}
|
||||
ws.Write("addFoul", foulData)
|
||||
foulData.TeamId = 359
|
||||
foulData.RuleId = 3
|
||||
ws.Write("addFoul", foulData)
|
||||
foulData.Alliance = "blue"
|
||||
foulData.TeamId = 1680
|
||||
ws.Write("addFoul", foulData)
|
||||
readWebsocketType(t, ws, "reload")
|
||||
readWebsocketType(t, ws, "reload")
|
||||
readWebsocketType(t, ws, "reload")
|
||||
if assert.Equal(t, 2, len(web.arena.RedRealtimeScore.CurrentScore.Fouls)) {
|
||||
assert.Equal(t, 256, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].TeamId)
|
||||
assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].RuleId)
|
||||
assert.Equal(t, 0.0, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].TimeInMatchSec)
|
||||
assert.Equal(t, 359, web.arena.RedRealtimeScore.CurrentScore.Fouls[1].TeamId)
|
||||
assert.Equal(t, 3, web.arena.RedRealtimeScore.CurrentScore.Fouls[1].RuleId)
|
||||
}
|
||||
if assert.Equal(t, 1, len(web.arena.BlueRealtimeScore.CurrentScore.Fouls)) {
|
||||
assert.Equal(t, 1680, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].TeamId)
|
||||
assert.Equal(t, 3, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].RuleId)
|
||||
assert.Equal(t, 0.0, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].TimeInMatchSec)
|
||||
}
|
||||
assert.False(t, web.arena.RedRealtimeScore.FoulsCommitted)
|
||||
assert.False(t, web.arena.BlueRealtimeScore.FoulsCommitted)
|
||||
|
||||
// Test foul deletion.
|
||||
ws.Write("deleteFoul", foulData)
|
||||
readWebsocketType(t, ws, "reload")
|
||||
assert.Equal(t, 0, len(web.arena.BlueRealtimeScore.CurrentScore.Fouls))
|
||||
foulData.Alliance = "red"
|
||||
foulData.TeamId = 359
|
||||
foulData.TimeInMatchSec = 29 // Make it not match.
|
||||
ws.Write("deleteFoul", foulData)
|
||||
readWebsocketType(t, ws, "reload")
|
||||
assert.Equal(t, 2, len(web.arena.RedRealtimeScore.CurrentScore.Fouls))
|
||||
foulData.TimeInMatchSec = 0
|
||||
ws.Write("deleteFoul", foulData)
|
||||
readWebsocketType(t, ws, "reload")
|
||||
assert.Equal(t, 1, len(web.arena.RedRealtimeScore.CurrentScore.Fouls))
|
||||
|
||||
// Test card setting.
|
||||
cardData := struct {
|
||||
Alliance string
|
||||
TeamId int
|
||||
Card string
|
||||
}{"red", 256, "yellow"}
|
||||
ws.Write("card", cardData)
|
||||
cardData.Alliance = "blue"
|
||||
cardData.TeamId = 1680
|
||||
cardData.Card = "red"
|
||||
ws.Write("card", cardData)
|
||||
time.Sleep(time.Millisecond * 10) // Allow some time for the command to be processed.
|
||||
if assert.Equal(t, 1, len(web.arena.RedRealtimeScore.Cards)) {
|
||||
assert.Equal(t, "yellow", web.arena.RedRealtimeScore.Cards["256"])
|
||||
}
|
||||
if assert.Equal(t, 1, len(web.arena.BlueRealtimeScore.Cards)) {
|
||||
assert.Equal(t, "red", web.arena.BlueRealtimeScore.Cards["1680"])
|
||||
}
|
||||
|
||||
// Test field reset and match committing.
|
||||
web.arena.MatchState = field.PostMatch
|
||||
ws.Write("signalReset", nil)
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
assert.Equal(t, "fieldReset", web.arena.AllianceStationDisplayMode)
|
||||
assert.False(t, web.arena.RedRealtimeScore.FoulsCommitted)
|
||||
assert.False(t, web.arena.BlueRealtimeScore.FoulsCommitted)
|
||||
web.arena.AllianceStationDisplayMode = "logo"
|
||||
ws.Write("commitMatch", nil)
|
||||
readWebsocketType(t, ws, "reload")
|
||||
assert.Equal(t, "fieldReset", web.arena.AllianceStationDisplayMode)
|
||||
assert.True(t, web.arena.RedRealtimeScore.FoulsCommitted)
|
||||
assert.True(t, web.arena.BlueRealtimeScore.FoulsCommitted)
|
||||
|
||||
// Should refresh the page when the next match is loaded.
|
||||
web.arena.MatchLoadNotifier.Notify()
|
||||
readWebsocketType(t, ws, "matchLoad")
|
||||
}
|
||||
2
web/reports.go
Normal file → Executable file
2
web/reports.go
Normal file → Executable file
@@ -63,7 +63,6 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request)
|
||||
pdf.CellFormat(colWidths["Endgame"], rowHeight, "Endgame", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Teleop"], rowHeight, "Teleop", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["W-L-T"], rowHeight, "W-L-T", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["DQ"], rowHeight, "DQ", "1", 0, "C", true, 0, "")
|
||||
pdf.CellFormat(colWidths["Played"], rowHeight, "Played", "1", 1, "C", true, 0, "")
|
||||
for _, ranking := range rankings {
|
||||
// Render ranking info row.
|
||||
@@ -77,7 +76,6 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request)
|
||||
pdf.CellFormat(colWidths["Teleop"], rowHeight, strconv.Itoa(ranking.TeleopPoints), "1", 0, "C", false, 0, "")
|
||||
record := fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties)
|
||||
pdf.CellFormat(colWidths["W-L-T"], rowHeight, record, "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["DQ"], rowHeight, strconv.Itoa(ranking.Disqualifications), "1", 0, "C", false, 0, "")
|
||||
pdf.CellFormat(colWidths["Played"], rowHeight, strconv.Itoa(ranking.Played), "1", 1, "C", false, 0, "")
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestRankingsCsvReport(t *testing.T) {
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0])
|
||||
expectedBody := "Rank,TeamId,RankingPoints,AutoPoints,EndgamePoints,TeleopPoints,Wins,Losses,Ties," +
|
||||
"Disqualifications,Played\n1,254,20,625,90,554,3,2,1,0,10\n2,1114,18,700,625,90,1,3,2,0,10\n\n"
|
||||
"Played\n1,254,20,625,90,554,3,2,1,10\n2,1114,18,700,625,90,1,3,2,10\n\n"
|
||||
assert.Equal(t, expectedBody, recorder.Body.String())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Web handlers for scoring interface.
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Renders the scoring interface which enables input of scores in real-time.
|
||||
func (web *Web) scoringPanelHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !web.userIsAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
alliance := vars["alliance"]
|
||||
if alliance != "red" && alliance != "blue" {
|
||||
handleWebErr(w, fmt.Errorf("Invalid alliance '%s'.", alliance))
|
||||
return
|
||||
}
|
||||
|
||||
template, err := web.parseFiles("templates/scoring_panel.html", "templates/base.html")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
data := struct {
|
||||
*model.EventSettings
|
||||
PlcIsEnabled bool
|
||||
Alliance string
|
||||
}{web.arena.EventSettings, web.arena.Plc.IsEnabled(), alliance}
|
||||
err = template.ExecuteTemplate(w, "base_no_navbar", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// The websocket endpoint for the scoring interface client to send control commands and receive status updates.
|
||||
func (web *Web) scoringPanelWebsocketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !web.userIsAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
alliance := vars["alliance"]
|
||||
if alliance != "red" && alliance != "blue" {
|
||||
handleWebErr(w, fmt.Errorf("Invalid alliance '%s'.", alliance))
|
||||
return
|
||||
}
|
||||
|
||||
var realtimeScore **field.RealtimeScore
|
||||
if alliance == "red" {
|
||||
realtimeScore = &web.arena.RedRealtimeScore
|
||||
} else {
|
||||
realtimeScore = &web.arena.BlueRealtimeScore
|
||||
}
|
||||
|
||||
ws, err := websocket.NewWebsocket(w, r)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
defer ws.Close()
|
||||
web.arena.ScoringPanelRegistry.RegisterPanel(alliance, ws)
|
||||
web.arena.ScoringStatusNotifier.Notify()
|
||||
defer web.arena.ScoringStatusNotifier.Notify()
|
||||
defer web.arena.ScoringPanelRegistry.UnregisterPanel(alliance, ws)
|
||||
|
||||
// Subscribe the websocket to the notifiers whose messages will be passed on to the client, in a separate goroutine.
|
||||
go ws.HandleNotifiers(web.arena.MatchLoadNotifier, web.arena.MatchTimeNotifier, web.arena.RealtimeScoreNotifier,
|
||||
web.arena.ReloadDisplaysNotifier)
|
||||
|
||||
// Loop, waiting for commands and responding to them, until the client closes the connection.
|
||||
for {
|
||||
command, _, err := ws.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// Client has closed the connection; nothing to do here.
|
||||
return
|
||||
}
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
score := &(*realtimeScore).CurrentScore
|
||||
scoreChanged := false
|
||||
|
||||
if command == "commitMatch" {
|
||||
if web.arena.MatchState != field.PostMatch {
|
||||
// Don't allow committing the score until the match is over.
|
||||
ws.WriteError("Cannot commit score: Match is not over.")
|
||||
continue
|
||||
}
|
||||
web.arena.ScoringPanelRegistry.SetScoreCommitted(alliance, ws)
|
||||
web.arena.ScoringStatusNotifier.Notify()
|
||||
} else if number, err := strconv.Atoi(command); err == nil && number >= 1 && number <= 6 {
|
||||
// Handle per-robot scoring fields.
|
||||
if number <= 3 {
|
||||
index := number - 1
|
||||
score.ExitedInitiationLine[index] = !score.ExitedInitiationLine[index]
|
||||
scoreChanged = true
|
||||
} else {
|
||||
index := number - 4
|
||||
score.EndgameStatuses[index]++
|
||||
if score.EndgameStatuses[index] == 3 {
|
||||
score.EndgameStatuses[index] = 0
|
||||
}
|
||||
scoreChanged = true
|
||||
}
|
||||
} else {
|
||||
switch strings.ToUpper(command) {
|
||||
case "Q":
|
||||
if decrementGoal(score.AutoCellsInner[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "A":
|
||||
if decrementGoal(score.AutoCellsOuter[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "Z":
|
||||
if decrementGoal(score.AutoCellsBottom[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "W":
|
||||
if incrementGoal(score.AutoCellsInner[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "S":
|
||||
if incrementGoal(score.AutoCellsOuter[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "X":
|
||||
if incrementGoal(score.AutoCellsBottom[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "E":
|
||||
if decrementGoal(score.TeleopCellsInner[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "D":
|
||||
if decrementGoal(score.TeleopCellsOuter[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "C":
|
||||
if decrementGoal(score.TeleopCellsBottom[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "R":
|
||||
if incrementGoal(score.TeleopCellsInner[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "F":
|
||||
if incrementGoal(score.TeleopCellsOuter[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "V":
|
||||
if incrementGoal(score.TeleopCellsBottom[:],
|
||||
score.CellCountingStage(web.arena.MatchState >= field.TeleopPeriod)) {
|
||||
scoreChanged = true
|
||||
}
|
||||
case "O":
|
||||
if score.ControlPanelStatus >= game.ControlPanelRotation {
|
||||
score.ControlPanelStatus = game.ControlPanelNone
|
||||
} else if score.StageAtCapacity(game.Stage2, true) {
|
||||
score.ControlPanelStatus = game.ControlPanelRotation
|
||||
}
|
||||
scoreChanged = true
|
||||
case "K":
|
||||
if score.ControlPanelStatus == game.ControlPanelRotation {
|
||||
controlPanel := &(*realtimeScore).ControlPanel
|
||||
controlPanel.CurrentColor++
|
||||
if controlPanel.CurrentColor == 5 {
|
||||
controlPanel.CurrentColor = 1
|
||||
}
|
||||
scoreChanged = true
|
||||
}
|
||||
case "P":
|
||||
if score.ControlPanelStatus == game.ControlPanelPosition {
|
||||
score.ControlPanelStatus = game.ControlPanelRotation
|
||||
} else if score.StageAtCapacity(game.Stage3, true) {
|
||||
score.ControlPanelStatus = game.ControlPanelPosition
|
||||
}
|
||||
scoreChanged = true
|
||||
case "L":
|
||||
score.RungIsLevel = !score.RungIsLevel
|
||||
scoreChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
if scoreChanged {
|
||||
web.arena.RealtimeScoreNotifier.Notify()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increments the power cell count for the given goal, if the preconditions are met.
|
||||
func incrementGoal(goal []int, currentStage game.Stage) bool {
|
||||
if int(currentStage) < len(goal) {
|
||||
goal[currentStage]++
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Decrements the power cell count for the given goal, if the preconditions are met.
|
||||
func decrementGoal(goal []int, currentStage game.Stage) bool {
|
||||
if int(currentStage) < len(goal) && goal[currentStage] > 0 {
|
||||
goal[currentStage]--
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
gorillawebsocket "github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestScoringPanel(t *testing.T) {
|
||||
web := setupTestWeb(t)
|
||||
|
||||
recorder := web.getHttpResponse("/panels/scoring/invalidalliance")
|
||||
assert.Equal(t, 500, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "Invalid alliance")
|
||||
recorder = web.getHttpResponse("/panels/scoring/red")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
recorder = web.getHttpResponse("/panels/scoring/blue")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "Scoring Panel - Untitled Event - Cheesy Arena")
|
||||
}
|
||||
|
||||
func TestScoringPanelWebsocket(t *testing.T) {
|
||||
web := setupTestWeb(t)
|
||||
|
||||
server, wsUrl := web.startTestServer()
|
||||
defer server.Close()
|
||||
_, _, err := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/panels/scoring/blorpy/websocket", nil)
|
||||
assert.NotNil(t, err)
|
||||
redConn, _, err := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/panels/scoring/red/websocket", nil)
|
||||
assert.Nil(t, err)
|
||||
defer redConn.Close()
|
||||
redWs := websocket.NewTestWebsocket(redConn)
|
||||
assert.Equal(t, 1, web.arena.ScoringPanelRegistry.GetNumPanels("red"))
|
||||
assert.Equal(t, 0, web.arena.ScoringPanelRegistry.GetNumPanels("blue"))
|
||||
blueConn, _, err := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/panels/scoring/blue/websocket", nil)
|
||||
assert.Nil(t, err)
|
||||
defer blueConn.Close()
|
||||
blueWs := websocket.NewTestWebsocket(blueConn)
|
||||
assert.Equal(t, 1, web.arena.ScoringPanelRegistry.GetNumPanels("red"))
|
||||
assert.Equal(t, 1, web.arena.ScoringPanelRegistry.GetNumPanels("blue"))
|
||||
|
||||
// Should get a few status updates right after connection.
|
||||
readWebsocketType(t, redWs, "matchLoad")
|
||||
readWebsocketType(t, redWs, "matchTime")
|
||||
readWebsocketType(t, redWs, "realtimeScore")
|
||||
readWebsocketType(t, blueWs, "matchLoad")
|
||||
readWebsocketType(t, blueWs, "matchTime")
|
||||
readWebsocketType(t, blueWs, "realtimeScore")
|
||||
|
||||
// Send some autonomous period scoring commands.
|
||||
web.arena.MatchState = field.AutoPeriod
|
||||
redWs.Write("1", nil)
|
||||
redWs.Write("3", nil)
|
||||
redWs.Write("w", nil)
|
||||
redWs.Write("X", nil)
|
||||
redWs.Write("x", nil)
|
||||
redWs.Write("z", nil)
|
||||
for i := 0; i < 6; i++ {
|
||||
readWebsocketType(t, redWs, "realtimeScore")
|
||||
readWebsocketType(t, blueWs, "realtimeScore")
|
||||
}
|
||||
assert.Equal(t, [3]bool{true, false, true}, web.arena.RedRealtimeScore.CurrentScore.ExitedInitiationLine)
|
||||
assert.Equal(t, [2]int{1, 0}, web.arena.RedRealtimeScore.CurrentScore.AutoCellsBottom)
|
||||
assert.Equal(t, [2]int{0, 0}, web.arena.RedRealtimeScore.CurrentScore.AutoCellsOuter)
|
||||
assert.Equal(t, [2]int{1, 0}, web.arena.RedRealtimeScore.CurrentScore.AutoCellsInner)
|
||||
|
||||
// Send some teleoperated period scoring commands.
|
||||
web.arena.MatchState = field.TeleopPeriod
|
||||
blueWs.Write("f", nil)
|
||||
blueWs.Write("F", nil)
|
||||
blueWs.Write("o", nil)
|
||||
blueWs.Write("5", nil)
|
||||
blueWs.Write("5", nil)
|
||||
blueWs.Write("L", nil)
|
||||
blueWs.Write("k", nil)
|
||||
for i := 0; i < 6; i++ {
|
||||
readWebsocketType(t, redWs, "realtimeScore")
|
||||
readWebsocketType(t, blueWs, "realtimeScore")
|
||||
}
|
||||
assert.Equal(t, [4]int{2, 0, 0, 0}, web.arena.BlueRealtimeScore.CurrentScore.TeleopCellsOuter)
|
||||
assert.Equal(t, [3]game.EndgameStatus{game.EndgameNone, game.EndgameHang, game.EndgameNone},
|
||||
web.arena.BlueRealtimeScore.CurrentScore.EndgameStatuses)
|
||||
assert.Equal(t, true, web.arena.BlueRealtimeScore.CurrentScore.RungIsLevel)
|
||||
|
||||
// Test committing logic.
|
||||
redWs.Write("commitMatch", nil)
|
||||
readWebsocketType(t, redWs, "error")
|
||||
blueWs.Write("commitMatch", nil)
|
||||
readWebsocketType(t, blueWs, "error")
|
||||
assert.Equal(t, 0, web.arena.ScoringPanelRegistry.GetNumScoreCommitted("red"))
|
||||
assert.Equal(t, 0, web.arena.ScoringPanelRegistry.GetNumScoreCommitted("blue"))
|
||||
web.arena.MatchState = field.PostMatch
|
||||
redWs.Write("commitMatch", nil)
|
||||
blueWs.Write("commitMatch", nil)
|
||||
time.Sleep(time.Millisecond * 10) // Allow some time for the commands to be processed.
|
||||
assert.Equal(t, 1, web.arena.ScoringPanelRegistry.GetNumScoreCommitted("red"))
|
||||
assert.Equal(t, 1, web.arena.ScoringPanelRegistry.GetNumScoreCommitted("blue"))
|
||||
|
||||
// Load another match to reset the results.
|
||||
web.arena.ResetMatch()
|
||||
web.arena.LoadTestMatch()
|
||||
readWebsocketType(t, redWs, "matchLoad")
|
||||
readWebsocketType(t, redWs, "realtimeScore")
|
||||
readWebsocketType(t, blueWs, "matchLoad")
|
||||
readWebsocketType(t, blueWs, "realtimeScore")
|
||||
assert.Equal(t, field.NewRealtimeScore(), web.arena.RedRealtimeScore)
|
||||
assert.Equal(t, field.NewRealtimeScore(), web.arena.BlueRealtimeScore)
|
||||
assert.Equal(t, 0, web.arena.ScoringPanelRegistry.GetNumScoreCommitted("red"))
|
||||
assert.Equal(t, 0, web.arena.ScoringPanelRegistry.GetNumScoreCommitted("blue"))
|
||||
}
|
||||
3
web/setup_settings.go
Normal file → Executable file
3
web/setup_settings.go
Normal file → Executable file
@@ -75,9 +75,6 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
eventSettings.PauseDurationSec, _ = strconv.Atoi(r.PostFormValue("pauseDurationSec"))
|
||||
eventSettings.TeleopDurationSec, _ = strconv.Atoi(r.PostFormValue("teleopDurationSec"))
|
||||
eventSettings.WarningRemainingDurationSec, _ = strconv.Atoi(r.PostFormValue("warningRemainingDurationSec"))
|
||||
eventSettings.Stage1Capacity, _ = strconv.Atoi(r.PostFormValue("stage1Capacity"))
|
||||
eventSettings.Stage2Capacity, _ = strconv.Atoi(r.PostFormValue("stage2Capacity"))
|
||||
eventSettings.Stage3Capacity, _ = strconv.Atoi(r.PostFormValue("stage3Capacity"))
|
||||
|
||||
if eventSettings.Ap2TeamChannel != 0 && eventSettings.Ap2TeamChannel == eventSettings.ApTeamChannel {
|
||||
web.renderSettings(w, r, "Cannot use same channel for both access points.")
|
||||
|
||||
4
web/web.go
Normal file → Executable file
4
web/web.go
Normal file → Executable file
@@ -144,10 +144,6 @@ func (web *Web) newHandler() http.Handler {
|
||||
router.HandleFunc("/match_review", web.matchReviewHandler).Methods("GET")
|
||||
router.HandleFunc("/match_review/{matchId}/edit", web.matchReviewEditGetHandler).Methods("GET")
|
||||
router.HandleFunc("/match_review/{matchId}/edit", web.matchReviewEditPostHandler).Methods("POST")
|
||||
router.HandleFunc("/panels/scoring/{alliance}", web.scoringPanelHandler).Methods("GET")
|
||||
router.HandleFunc("/panels/scoring/{alliance}/websocket", web.scoringPanelWebsocketHandler).Methods("GET")
|
||||
router.HandleFunc("/panels/referee", web.refereePanelHandler).Methods("GET")
|
||||
router.HandleFunc("/panels/referee/websocket", web.refereePanelWebsocketHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/rankings", web.rankingsCsvReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/pdf/rankings", web.rankingsPdfReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/schedule/{type}", web.scheduleCsvReportHandler).Methods("GET")
|
||||
|
||||
Reference in New Issue
Block a user