diff --git a/.gitignore b/.gitignore index 5871479..b317012 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ _testmain.go *.test *.db *.out +.DS_Store diff --git a/displays.go b/displays.go index 3abde4c..49b5048 100644 --- a/displays.go +++ b/displays.go @@ -217,40 +217,9 @@ func AnnouncerDisplayHandler(w http.ResponseWriter, r *http.Request) { handleWebErr(w, err) return } - - // Assemble info about the current match. - matchType := mainArena.currentMatch.CapitalizedType() - red1 := mainArena.AllianceStations["R1"].team - red2 := mainArena.AllianceStations["R2"].team - red3 := mainArena.AllianceStations["R3"].team - blue1 := mainArena.AllianceStations["B1"].team - blue2 := mainArena.AllianceStations["B2"].team - blue3 := mainArena.AllianceStations["B3"].team - - // Assemble info about the saved match result. - var redScoreSummary, blueScoreSummary *ScoreSummary - var savedMatchType, savedMatchDisplayName string - savedMatchType = mainArena.savedMatch.CapitalizedType() - savedMatchDisplayName = mainArena.savedMatch.DisplayName - redScoreSummary = mainArena.savedMatchResult.RedScoreSummary() - blueScoreSummary = mainArena.savedMatchResult.BlueScoreSummary() data := struct { *EventSettings - MatchType string - MatchDisplayName string - Red1 *Team - Red2 *Team - Red3 *Team - Blue1 *Team - Blue2 *Team - Blue3 *Team - SavedMatchResult *MatchResult - SavedMatchType string - SavedMatchDisplayName string - RedScoreSummary *ScoreSummary - BlueScoreSummary *ScoreSummary - }{eventSettings, matchType, mainArena.currentMatch.DisplayName, red1, red2, red3, blue1, blue2, blue3, - mainArena.savedMatchResult, savedMatchType, savedMatchDisplayName, redScoreSummary, blueScoreSummary} + }{eventSettings} err = template.ExecuteTemplate(w, "base", data) if err != nil { handleWebErr(w, err) @@ -271,10 +240,33 @@ func AnnouncerDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { defer close(matchLoadTeamsListener) matchTimeListener := mainArena.matchTimeNotifier.Listen() defer close(matchTimeListener) + realtimeScoreListener := mainArena.realtimeScoreNotifier.Listen() + defer close(realtimeScoreListener) scorePostedListener := mainArena.scorePostedNotifier.Listen() defer close(scorePostedListener) + audienceDisplayListener := mainArena.audienceDisplayNotifier.Listen() + defer close(audienceDisplayListener) // Send the various notifications immediately upon connection. + var data interface{} + data = struct { + MatchType string + MatchDisplayName string + Red1 *Team + Red2 *Team + Red3 *Team + Blue1 *Team + Blue2 *Team + Blue3 *Team + }{mainArena.currentMatch.CapitalizedType(), mainArena.currentMatch.DisplayName, + mainArena.AllianceStations["R1"].team, mainArena.AllianceStations["R2"].team, + mainArena.AllianceStations["R3"].team, mainArena.AllianceStations["B1"].team, + mainArena.AllianceStations["B2"].team, mainArena.AllianceStations["B3"].team} + err = websocket.Write("setMatch", data) + if err != nil { + log.Printf("Websocket error: %s", err) + return + } err = websocket.Write("matchTiming", mainArena.matchTiming) if err != nil { log.Printf("Websocket error: %s", err) @@ -285,6 +277,15 @@ func AnnouncerDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { log.Printf("Websocket error: %s", err) return } + data = struct { + RedScore int + BlueScore int + }{mainArena.redRealtimeScore.Score(), mainArena.blueRealtimeScore.Score()} + err = websocket.Write("realtimeScore", data) + if err != nil { + log.Printf("Websocket error: %s", err) + return + } // Spin off a goroutine to listen for notifications and pass them on through the websocket. go func() { @@ -296,20 +297,56 @@ func AnnouncerDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { if !ok { return } - messageType = "reload" - message = nil + messageType = "setMatch" + message = struct { + MatchType string + MatchDisplayName string + Red1 *Team + Red2 *Team + Red3 *Team + Blue1 *Team + Blue2 *Team + Blue3 *Team + }{mainArena.currentMatch.CapitalizedType(), mainArena.currentMatch.DisplayName, + mainArena.AllianceStations["R1"].team, mainArena.AllianceStations["R2"].team, + mainArena.AllianceStations["R3"].team, mainArena.AllianceStations["B1"].team, + mainArena.AllianceStations["B2"].team, mainArena.AllianceStations["B3"].team} case matchTimeSec, ok := <-matchTimeListener: if !ok { return } messageType = "matchTime" message = MatchTimeMessage{mainArena.MatchState, matchTimeSec.(int)} + case _, ok := <-realtimeScoreListener: + if !ok { + return + } + messageType = "realtimeScore" + message = struct { + RedScore int + BlueScore int + }{mainArena.redRealtimeScore.Score(), mainArena.blueRealtimeScore.Score()} case _, ok := <-scorePostedListener: if !ok { return } - messageType = "reload" - message = nil + messageType = "setFinalScore" + message = struct { + MatchType string + MatchDisplayName string + RedScoreSummary *ScoreSummary + BlueScoreSummary *ScoreSummary + RedFouls []Foul + BlueFouls []Foul + }{mainArena.savedMatch.CapitalizedType(), mainArena.savedMatch.DisplayName, + mainArena.savedMatchResult.RedScoreSummary(), mainArena.savedMatchResult.BlueScoreSummary(), + mainArena.savedMatchResult.RedFouls, mainArena.savedMatchResult.BlueFouls} + case _, ok := <-audienceDisplayListener: + if !ok { + return + } + messageType = "setAudienceDisplay" + message = mainArena.audienceDisplayScreen } err = websocket.Write(messageType, message) if err != nil { diff --git a/displays_test.go b/displays_test.go index 67d47a9..a998d99 100644 --- a/displays_test.go +++ b/displays_test.go @@ -123,11 +123,13 @@ func TestAnnouncerDisplayWebsocket(t *testing.T) { ws := &Websocket{conn} // Should get a few status updates right after connection. + readWebsocketType(t, ws, "setMatch") readWebsocketType(t, ws, "matchTiming") readWebsocketType(t, ws, "matchTime") + readWebsocketType(t, ws, "realtimeScore") mainArena.matchLoadTeamsNotifier.Notify(nil) - readWebsocketType(t, ws, "reload") + readWebsocketType(t, ws, "setMatch") mainArena.AllianceStations["R1"].Bypass = true mainArena.AllianceStations["R2"].Bypass = true mainArena.AllianceStations["R3"].Bypass = true @@ -136,9 +138,15 @@ func TestAnnouncerDisplayWebsocket(t *testing.T) { mainArena.AllianceStations["B3"].Bypass = true mainArena.StartMatch() mainArena.Update() - readWebsocketType(t, ws, "matchTime") + messages := readWebsocketMultiple(t, ws, 2) + _, ok := messages["setAudienceDisplay"] + assert.True(t, ok) + _, ok = messages["matchTime"] + assert.True(t, ok) + mainArena.realtimeScoreNotifier.Notify(nil) + readWebsocketType(t, ws, "realtimeScore") mainArena.scorePostedNotifier.Notify(nil) - readWebsocketType(t, ws, "reload") + readWebsocketType(t, ws, "setFinalScore") // Test triggering the final score screen. ws.Write("setAudienceDisplay", "score") diff --git a/static/css/cheesy-arena.css b/static/css/cheesy-arena.css index 0421906..19865ad 100644 --- a/static/css/cheesy-arena.css +++ b/static/css/cheesy-arena.css @@ -38,6 +38,9 @@ padding-left: 0; padding-right: 0; } +.modal-large { + width: 60%; +} .ds-status, .robot-status, .battery-status, .bypass-status { background-color: #aaa; color: #000; diff --git a/static/js/announcer_display.js b/static/js/announcer_display.js index d32eb55..8a89683 100644 --- a/static/js/announcer_display.js +++ b/static/js/announcer_display.js @@ -4,20 +4,47 @@ // Client-side logic for the announcer display. var websocket; -var blinkTimeout; +var teamTemplate = Handlebars.compile($("#teamTemplate").html()); +var matchResultTemplate = Handlebars.compile($("#matchResultTemplate").html()); + +var handleSetAudienceDisplay = function(targetScreen) { + // Hide the final results so that they aren't blocking the current teams when the announcer needs them most. + if (targetScreen == "intro" || targetScreen == "match") { + $("#matchResult").modal("hide"); + } +}; + +var handleSetMatch = function(data) { + $("#matchName").text(data.MatchType + " Match " + data.MatchDisplayName); + $("#red1").html(teamTemplate(data.Red1)); + $("#red2").html(teamTemplate(data.Red2)); + $("#red3").html(teamTemplate(data.Red3)); + $("#blue1").html(teamTemplate(data.Blue1)); + $("#blue2").html(teamTemplate(data.Blue2)); + $("#blue3").html(teamTemplate(data.Blue3)); +}; var handleMatchTime = function(data) { translateMatchTime(data, function(matchState, matchStateText, countdownSec) { $("#matchState").text(matchStateText); $("#matchTime").text(getCountdown(data.MatchState, data.MatchTimeSec)); - if (matchState == "PRE_MATCH" || matchState == "POST_MATCH") { - $("#savedMatchResult").show(); - } }); }; +var handleRealtimeScore = function(data) { + $("#redScore").text(data.RedScore); + $("#blueScore").text(data.BlueScore); +}; + +var handleSetFinalScore = function(data) { +console.log(data); + $("#scoreMatchName").text(data.MatchType + " Match " + data.MatchDisplayName); + $("#redScoreDetails").html(matchResultTemplate({score: data.RedScoreSummary, fouls: data.RedFouls})); + $("#blueScoreDetails").html(matchResultTemplate({score: data.BlueScoreSummary, fouls: data.BlueFouls})); + $("#matchResult").modal("show"); +}; + var postMatchResult = function(data) { - clearTimeout(blinkTimeout); $("#savedMatchResult").attr("data-blink", false); websocket.send("setAudienceDisplay", "score"); } @@ -25,12 +52,16 @@ var postMatchResult = function(data) { $(function() { // Set up the websocket back to the server. websocket = new CheesyWebsocket("/displays/announcer/websocket", { + setMatch: function(event) { handleSetMatch(event.data); }, matchTiming: function(event) { handleMatchTiming(event.data); }, - matchTime: function(event) { handleMatchTime(event.data); } + matchTime: function(event) { handleMatchTime(event.data); }, + realtimeScore: function(event) { handleRealtimeScore(event.data); }, + setFinalScore: function(event) { handleSetFinalScore(event.data); }, + setAudienceDisplay: function(event) { handleSetAudienceDisplay(event.data); } }); // Make the score blink. - blinkTimeout = setInterval(function() { + setInterval(function() { var blinkOn = $("#savedMatchResult").attr("data-blink") == "true"; $("#savedMatchResult").attr("data-blink", !blinkOn); }, 500); diff --git a/templates/announcer_display.html b/templates/announcer_display.html index cbafa36..332d0ac 100644 --- a/templates/announcer_display.html +++ b/templates/announcer_display.html @@ -1,6 +1,6 @@ {{define "title"}}Announcer Display{{end}} {{define "body"}} -
| Rookie Year | Recent Accomplishments |
|---|---|