diff --git a/field/arena.go b/field/arena.go index b428e23c..a3c112ff 100644 --- a/field/arena.go +++ b/field/arena.go @@ -59,30 +59,32 @@ type Arena struct { ScoringPanelRegistry ArenaNotifiers MatchState - lastMatchState MatchState - CurrentMatch *model.Match - MatchStartTime time.Time - LastMatchTimeSec float64 - RedRealtimeScore *RealtimeScore - BlueRealtimeScore *RealtimeScore - lastDsPacketTime time.Time - lastPeriodicTaskTime time.Time - EventStatus EventStatus - FieldReset bool - AudienceDisplayMode string - SavedMatch *model.Match - SavedMatchResult *model.MatchResult - SavedRankings game.Rankings - AllianceStationDisplayMode string - AllianceSelectionAlliances []model.Alliance - PlayoffTournament *playoff.PlayoffTournament - LowerThird *model.LowerThird - ShowLowerThird bool - MuteMatchSounds bool - matchAborted bool - soundsPlayed map[*game.MatchSound]struct{} - breakDescription string - preloadedTeams *[6]*model.Team + lastMatchState MatchState + CurrentMatch *model.Match + MatchStartTime time.Time + LastMatchTimeSec float64 + RedRealtimeScore *RealtimeScore + BlueRealtimeScore *RealtimeScore + lastDsPacketTime time.Time + lastPeriodicTaskTime time.Time + EventStatus EventStatus + FieldReset bool + AudienceDisplayMode string + SavedMatch *model.Match + SavedMatchResult *model.MatchResult + SavedRankings game.Rankings + AllianceStationDisplayMode string + AllianceSelectionAlliances []model.Alliance + AllianceSelectionShowTimer bool + AllianceSelectionTimeRemainingSec int + PlayoffTournament *playoff.PlayoffTournament + LowerThird *model.LowerThird + ShowLowerThird bool + MuteMatchSounds bool + matchAborted bool + soundsPlayed map[*game.MatchSound]struct{} + breakDescription string + preloadedTeams *[6]*model.Team } type AllianceStation struct { diff --git a/field/arena_notifiers.go b/field/arena_notifiers.go index f917eddb..46fdffff 100644 --- a/field/arena_notifiers.go +++ b/field/arena_notifiers.go @@ -65,7 +65,15 @@ func (arena *Arena) configureNotifiers() { } func (arena *Arena) generateAllianceSelectionMessage() any { - return &arena.AllianceSelectionAlliances + return &struct { + Alliances []model.Alliance + ShowTimer bool + TimeRemainingSec int + }{ + arena.AllianceSelectionAlliances, + arena.AllianceSelectionShowTimer, + arena.AllianceSelectionTimeRemainingSec, + } } func (arena *Arena) generateAllianceStationDisplayModeMessage() any { diff --git a/static/js/alliance_selection.js b/static/js/alliance_selection.js new file mode 100644 index 00000000..75e40e96 --- /dev/null +++ b/static/js/alliance_selection.js @@ -0,0 +1,32 @@ +// Copyright 2024 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Client-side logic for the alliance selection page. + +var websocket; + +// Sends a websocket message to show the timer. +const showTimer = function() { + websocket.send("showTimer"); +}; + +// Sends a websocket message to hide the timer. +const hideTimer = function() { + websocket.send("hideTimer"); +} + +// Handles a websocket message to update the alliance selection status. +const handleAllianceSelection = function(data) { + $("#timer").text(getCountdownString(data.TimeRemainingSec)); +}; + +$(function() { + // Activate playoff tournament datetime picker. + const startTime = moment(new Date()).hour(13).minute(0).second(0); + newDateTimePicker("startTimePicker", startTime.toDate()); + + // Set up the websocket back to the server. + websocket = new CheesyWebsocket("/alliance_selection/websocket", { + allianceSelection: function(event) { handleAllianceSelection(event.data); }, + }); +}); diff --git a/static/js/audience_display.js b/static/js/audience_display.js index 667b98b7..710de9ca 100644 --- a/static/js/audience_display.js +++ b/static/js/audience_display.js @@ -139,12 +139,7 @@ const handleMatchLoad = function(data) { // Handles a websocket message to update the match time countdown. const handleMatchTime = function(data) { translateMatchTime(data, function(matchState, matchStateText, countdownSec) { - let countdownString = String(countdownSec % 60); - if (countdownString.length === 1) { - countdownString = "0" + countdownString; - } - countdownString = Math.floor(countdownSec / 60) + ":" + countdownString; - $("#matchTime").text(countdownString); + $("#matchTime").text(getCountdownString(countdownSec)); }); }; @@ -325,7 +320,8 @@ const handlePlaySound = function(sound) { }; // Handles a websocket message to update the alliance selection screen. -const handleAllianceSelection = function(alliances) { +const handleAllianceSelection = function(data) { + const alliances = data.Alliances; if (alliances && alliances.length > 0) { const numColumns = alliances[0].TeamIds.length + 1; $.each(alliances, function(k, v) { @@ -333,6 +329,12 @@ const handleAllianceSelection = function(alliances) { }); $("#allianceSelection").html(allianceSelectionTemplate({alliances: alliances, numColumns: numColumns})); } + + if (data.ShowTimer) { + $("#allianceSelectionTimer").text(getCountdownString(data.TimeRemainingSec)); + } else { + $("#allianceSelectionTimer").html(" "); + } }; // Handles a websocket message to populate and/or show/hide a lower third. diff --git a/static/js/match_timing.js b/static/js/match_timing.js index 9056abd3..486255ef 100644 --- a/static/js/match_timing.js +++ b/static/js/match_timing.js @@ -9,7 +9,7 @@ const matchTypePractice = 1; const matchTypeQualification = 2; const matchTypePlayoff = 3; -var matchStates = { +const matchStates = { 0: "PRE_MATCH", 1: "START_MATCH", 2: "WARMUP_PERIOD", @@ -20,16 +20,16 @@ var matchStates = { 7: "TIMEOUT_ACTIVE", 8: "POST_TIMEOUT" }; -var matchTiming; +let matchTiming; // Handles a websocket message containing the length of each period in the match. -var handleMatchTiming = function(data) { +const handleMatchTiming = function(data) { matchTiming = data; }; // Converts the raw match state and time into a human-readable state and per-period time. Calls the provided // callback with the result. -var translateMatchTime = function(data, callback) { +const translateMatchTime = function(data, callback) { var matchStateText; switch (matchStates[data.MatchState]) { case "PRE_MATCH": @@ -60,7 +60,7 @@ var translateMatchTime = function(data, callback) { }; // Returns the per-period countdown for the given match state and overall time into the match. -var getCountdown = function(matchState, matchTimeSec) { +const getCountdown = function(matchState, matchTimeSec) { switch (matchStates[matchState]) { case "PRE_MATCH": case "START_MATCH": @@ -77,3 +77,12 @@ var getCountdown = function(matchState, matchTimeSec) { return 0; } }; + +// Converts the given countdown in seconds to a string with a colon separator and leading zero padding. +const getCountdownString = function(countdownSec) { + let countdownString = String(countdownSec % 60); + if (countdownString.length === 1) { + countdownString = "0" + countdownString; + } + return Math.floor(countdownSec / 60) + ":" + countdownString; +}; diff --git a/static/js/wall_display.js b/static/js/wall_display.js index a25b4e77..3c6070b1 100644 --- a/static/js/wall_display.js +++ b/static/js/wall_display.js @@ -130,12 +130,7 @@ const handleMatchLoad = function(data) { // Handles a websocket message to update the match time countdown. const handleMatchTime = function(data) { translateMatchTime(data, function(matchState, matchStateText, countdownSec) { - let countdownString = String(countdownSec % 60); - if (countdownString.length === 1) { - countdownString = "0" + countdownString; - } - countdownString = Math.floor(countdownSec / 60) + ":" + countdownString; - $("#matchTime").text(countdownString); + $("#matchTime").text(getCountdownString(countdownSec)); }); }; diff --git a/templates/alliance_selection.html b/templates/alliance_selection.html index 4084370d..612a859e 100644 --- a/templates/alliance_selection.html +++ b/templates/alliance_selection.html @@ -76,6 +76,35 @@ Hint: Press 'Enter' after entering each team number for easiest use. +
+
+
+ Selection Timer +
+
+ +
+
+

+ Timer starts automatically after each non-captain assignment and is hidden on the audience overlay until + the button below is pressed. +

+
+ +
+ +
+
+ +
+
+
+
+ + +
+
+
@@ -149,6 +178,8 @@
{{end}} {{define "script"}} + +