Updated realtime scoring entry for 2016.

This commit is contained in:
Patrick Fairbank
2016-08-07 14:15:57 -07:00
parent ebcd0db37e
commit 8af83ea7e7
6 changed files with 356 additions and 317 deletions

View File

@@ -50,7 +50,6 @@ type RealtimeScore struct {
AutoCommitted bool
TeleopCommitted bool
FoulsCommitted bool
undoScores []Score
}
type Arena struct {

View File

@@ -11,6 +11,7 @@ import (
"io"
"log"
"net/http"
"strconv"
"text/template"
)
@@ -123,7 +124,7 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
// Loop, waiting for commands and responding to them, until the client closes the connection.
for {
messageType, _, err := websocket.Read()
messageType, data, err := websocket.Read()
if err != nil {
if err == io.EOF {
// Client has closed the connection; nothing to do here.
@@ -134,7 +135,113 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
}
switch messageType {
// TODO(patrick): Add 2016 messages.
case "defenseCrossed":
position, ok := data.(string)
if !ok {
websocket.WriteError("Defense position is not a string.")
continue
}
intPosition, err := strconv.Atoi(position)
if err != nil {
websocket.WriteError(err.Error())
continue
}
if (*score).CurrentScore.AutoDefensesCrossed[intPosition-1]+
(*score).CurrentScore.DefensesCrossed[intPosition-1] < 2 {
if !(*score).AutoCommitted {
(*score).CurrentScore.AutoDefensesCrossed[intPosition-1]++
} else {
(*score).CurrentScore.DefensesCrossed[intPosition-1]++
}
}
case "undoDefenseCrossed":
position, ok := data.(string)
if !ok {
websocket.WriteError("Defense position is not a string.")
continue
}
intPosition, err := strconv.Atoi(position)
if err != nil {
websocket.WriteError(err.Error())
continue
}
if !(*score).AutoCommitted {
if (*score).CurrentScore.AutoDefensesCrossed[intPosition-1] > 0 {
(*score).CurrentScore.AutoDefensesCrossed[intPosition-1]--
}
} else {
if (*score).CurrentScore.DefensesCrossed[intPosition-1] > 0 {
(*score).CurrentScore.DefensesCrossed[intPosition-1]--
}
}
case "autoDefenseReached":
if !(*score).AutoCommitted {
if (*score).CurrentScore.AutoDefensesReached < 3 {
(*score).CurrentScore.AutoDefensesReached++
}
}
case "undoAutoDefenseReached":
if !(*score).AutoCommitted {
if (*score).CurrentScore.AutoDefensesReached > 0 {
(*score).CurrentScore.AutoDefensesReached--
}
}
case "highGoal":
if !(*score).AutoCommitted {
(*score).CurrentScore.AutoHighGoals++
} else {
(*score).CurrentScore.HighGoals++
}
case "undoHighGoal":
if !(*score).AutoCommitted {
if (*score).CurrentScore.AutoHighGoals > 0 {
(*score).CurrentScore.AutoHighGoals--
}
} else {
if (*score).CurrentScore.HighGoals > 0 {
(*score).CurrentScore.HighGoals--
}
}
case "lowGoal":
if !(*score).AutoCommitted {
(*score).CurrentScore.AutoLowGoals++
} else {
(*score).CurrentScore.LowGoals++
}
case "undoLowGoal":
if !(*score).AutoCommitted {
if (*score).CurrentScore.AutoLowGoals > 0 {
(*score).CurrentScore.AutoLowGoals--
}
} else {
if (*score).CurrentScore.LowGoals > 0 {
(*score).CurrentScore.LowGoals--
}
}
case "challenge":
if (*score).AutoCommitted {
if (*score).CurrentScore.Challenges < 3 {
(*score).CurrentScore.Challenges++
}
}
case "undoChallenge":
if (*score).AutoCommitted {
if (*score).CurrentScore.Challenges > 0 {
(*score).CurrentScore.Challenges--
}
}
case "scale":
if (*score).AutoCommitted {
if (*score).CurrentScore.Scales < 3 {
(*score).CurrentScore.Scales++
}
}
case "undoScale":
if (*score).AutoCommitted {
if (*score).CurrentScore.Scales > 0 {
(*score).CurrentScore.Scales--
}
}
case "commit":
(*score).AutoCommitted = true
case "uncommitAuto":
@@ -149,11 +256,6 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
(*score).AutoCommitted = true
(*score).TeleopCommitted = true
mainArena.scoringStatusNotifier.Notify(nil)
case "undo":
if len((*score).undoScores) > 0 {
(*score).CurrentScore = (*score).undoScores[len((*score).undoScores)-1]
(*score).undoScores = (*score).undoScores[0 : len((*score).undoScores)-1]
}
default:
websocket.WriteError(fmt.Sprintf("Invalid message type '%s'.", messageType))
continue

View File

@@ -4,6 +4,7 @@
package main
import (
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"testing"
)
@@ -29,101 +30,114 @@ func TestScoringDisplay(t *testing.T) {
}
func TestScoringDisplayWebsocket(t *testing.T) {
// TODO(patrick): Update for 2016.
/*
clearDb()
defer clearDb()
var err error
db, err = OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
eventSettings, _ = db.GetEventSettings()
mainArena.Setup()
clearDb()
defer clearDb()
var err error
db, err = OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
eventSettings, _ = db.GetEventSettings()
mainArena.Setup()
server, wsUrl := startTestServer()
defer server.Close()
_, _, err = websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/blorpy/websocket", nil)
assert.NotNil(t, err)
redConn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/red/websocket", nil)
assert.Nil(t, err)
defer redConn.Close()
redWs := &Websocket{redConn}
blueConn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/blue/websocket", nil)
assert.Nil(t, err)
defer blueConn.Close()
blueWs := &Websocket{blueConn}
server, wsUrl := startTestServer()
defer server.Close()
_, _, err = websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/blorpy/websocket", nil)
assert.NotNil(t, err)
redConn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/red/websocket", nil)
assert.Nil(t, err)
defer redConn.Close()
redWs := &Websocket{redConn}
blueConn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/blue/websocket", nil)
assert.Nil(t, err)
defer blueConn.Close()
blueWs := &Websocket{blueConn}
// Should receive a score update right after connection.
// Should receive a score update right after connection.
readWebsocketType(t, redWs, "score")
readWebsocketType(t, redWs, "matchTime")
readWebsocketType(t, blueWs, "score")
readWebsocketType(t, blueWs, "matchTime")
// Send a match worth of scoring commands in.
redWs.Write("defenseCrossed", "2")
blueWs.Write("autoDefenseReached", nil)
redWs.Write("highGoal", nil)
redWs.Write("highGoal", nil)
redWs.Write("lowGoal", nil)
redWs.Write("defenseCrossed", "5")
blueWs.Write("defenseCrossed", "1")
redWs.Write("undoHighGoal", nil)
redWs.Write("commit", nil)
blueWs.Write("autoDefenseReached", nil)
blueWs.Write("commit", nil)
redWs.Write("uncommitAuto", nil)
redWs.Write("autoDefenseReached", nil)
redWs.Write("defenseCrossed", "2")
redWs.Write("commit", nil)
for i := 0; i < 11; i++ {
readWebsocketType(t, redWs, "score")
readWebsocketType(t, redWs, "matchTime")
}
for i := 0; i < 4; i++ {
readWebsocketType(t, blueWs, "score")
readWebsocketType(t, blueWs, "matchTime")
}
// Send a match worth of scoring commands in.
redWs.Write("robotSet", nil)
blueWs.Write("containerSet", nil)
redWs.Write("stackedToteSet", nil)
redWs.Write("robotSet", nil)
redWs.Write("toteSet", nil)
blueWs.Write("stackedToteSet", nil)
redWs.Write("commit", nil)
blueWs.Write("commit", nil)
redWs.Write("uncommitAuto", nil)
redWs.Write("robotSet", nil)
redWs.Write("commit", nil)
for i := 0; i < 8; i++ {
readWebsocketType(t, redWs, "score")
}
for i := 0; i < 3; i++ {
readWebsocketType(t, blueWs, "score")
}
assert.Equal(t, [5]int{0, 2, 0, 0, 1}, mainArena.redRealtimeScore.CurrentScore.AutoDefensesCrossed)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoDefensesReached)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoHighGoals)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoLowGoals)
assert.Equal(t, [5]int{1, 0, 0, 0, 0}, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesCrossed)
assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesReached)
assert.True(t, mainArena.redRealtimeScore.CurrentScore.AutoRobotSet)
assert.False(t, mainArena.redRealtimeScore.CurrentScore.AutoContainerSet)
assert.True(t, mainArena.redRealtimeScore.CurrentScore.AutoToteSet)
assert.False(t, mainArena.redRealtimeScore.CurrentScore.AutoStackedToteSet)
assert.False(t, mainArena.blueRealtimeScore.CurrentScore.AutoRobotSet)
assert.True(t, mainArena.blueRealtimeScore.CurrentScore.AutoContainerSet)
assert.False(t, mainArena.blueRealtimeScore.CurrentScore.AutoToteSet)
assert.True(t, mainArena.blueRealtimeScore.CurrentScore.AutoStackedToteSet)
stacks := []Stack{Stack{6, true, true}, Stack{1, false, false}, Stack{2, true, false}, Stack{}}
blueWs.Write("commit", stacks)
redWs.Write("commit", stacks)
stacks[0].Litter = false
blueWs.Write("commit", stacks)
redWs.Write("toteSet", nil)
blueWs.Write("stackedToteSet", nil)
for i := 0; i < 2; i++ {
readWebsocketType(t, redWs, "score")
}
for i := 0; i < 3; i++ {
readWebsocketType(t, blueWs, "score")
}
assert.Equal(t, stacks, mainArena.blueRealtimeScore.CurrentScore.Stacks)
stacks[0].Litter = true
assert.Equal(t, stacks, mainArena.redRealtimeScore.CurrentScore.Stacks)
// Test committing logic.
redWs.Write("commitMatch", nil)
readWebsocketType(t, redWs, "error")
mainArena.MatchState = POST_MATCH
redWs.Write("commitMatch", nil)
blueWs.Write("commitMatch", nil)
readWebsocketType(t, redWs, "dialog") // Should be an error message about co-op not matching.
readWebsocketType(t, blueWs, "dialog")
redWs.Write("stackedToteSet", nil)
redWs.Write("commitMatch", nil)
blueWs.Write("commitMatch", nil)
redWs.Write("defenseCrossed", "2")
blueWs.Write("autoDefenseReached", nil)
redWs.Write("highGoal", nil)
redWs.Write("highGoal", nil)
redWs.Write("lowGoal", nil)
redWs.Write("defenseCrossed", "5")
blueWs.Write("defenseCrossed", "3")
blueWs.Write("challenge", nil)
blueWs.Write("scale", nil)
blueWs.Write("undoChallenge", nil)
redWs.Write("challenge", nil)
redWs.Write("defenseCrossed", "3")
redWs.Write("undoHighGoal", nil)
for i := 0; i < 8; i++ {
readWebsocketType(t, redWs, "score")
}
for i := 0; i < 5; i++ {
readWebsocketType(t, blueWs, "score")
}
// Load another match to reset the results.
mainArena.ResetMatch()
mainArena.LoadTestMatch()
readWebsocketType(t, redWs, "score")
readWebsocketType(t, blueWs, "score")
assert.Equal(t, *NewRealtimeScore(), *mainArena.redRealtimeScore)
assert.Equal(t, *NewRealtimeScore(), *mainArena.blueRealtimeScore)
*/
// Make sure auto scores haven't changed in teleop.
assert.Equal(t, [5]int{0, 2, 0, 0, 1}, mainArena.redRealtimeScore.CurrentScore.AutoDefensesCrossed)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoDefensesReached)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoHighGoals)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoLowGoals)
assert.Equal(t, [5]int{1, 0, 0, 0, 0}, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesCrossed)
assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesReached)
assert.Equal(t, [5]int{0, 0, 1, 0, 1}, mainArena.redRealtimeScore.CurrentScore.DefensesCrossed)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.HighGoals)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.LowGoals)
assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.Challenges)
assert.Equal(t, [5]int{0, 0, 1, 0, 0}, mainArena.blueRealtimeScore.CurrentScore.DefensesCrossed)
assert.Equal(t, 0, mainArena.blueRealtimeScore.CurrentScore.Challenges)
assert.Equal(t, 1, mainArena.blueRealtimeScore.CurrentScore.Scales)
// Test committing logic.
redWs.Write("commitMatch", nil)
readWebsocketType(t, redWs, "error")
mainArena.MatchState = POST_MATCH
redWs.Write("commitMatch", nil)
blueWs.Write("commitMatch", nil)
readWebsocketType(t, redWs, "score")
readWebsocketType(t, blueWs, "score")
// Load another match to reset the results.
mainArena.ResetMatch()
mainArena.LoadTestMatch()
readWebsocketType(t, redWs, "score")
readWebsocketType(t, blueWs, "score")
assert.Equal(t, *NewRealtimeScore(), *mainArena.redRealtimeScore)
assert.Equal(t, *NewRealtimeScore(), *mainArena.blueRealtimeScore)
}

View File

@@ -99,55 +99,9 @@
.scoring-comment {
font-size: 20px;
}
.scoring-comment[data-value=true] {
color: #0a0;
}
.scoring-comment[data-value=false] {
color: #f00;
}
.scoring-message {
color: #f00;
}
.stack-grid {
width: 90%;
margin: 5%;
border: 2px solid transparent;
}
.stack-grid[data-changed=true] {
border: 2px solid #fc0;
}
.stack-grid td {
width: 20%;
height: 120px;
}
.stack-grid td[data-selected=true] {
background-color: #ffc;
}
.stack-tote-count {
color: #999;
font-size: 40px;
padding: 20px;
}
.stack-container {
position: relative;
left: 47px;
bottom: 50px;
margin-bottom: -40px;
border-top: 40px solid #696;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
width: 35px;
height: 0;
}
.stack-litter {
position: relative;
left: 60px;
bottom: 80px;
margin-bottom: -30px;
width: 10px;
height: 30px;
background-color: #9f0;
}
.btn-lower-third {
width: 80px;
}

View File

@@ -4,55 +4,31 @@
// Client-side logic for the scoring interface.
var websocket;
var selectedStack = 0;
var numStacks = 10;
var stacks;
var stackScoreChanged = false;
var scoreCommitted = false;
function Stack() {
this.Totes = 0;
this.Container = false;
this.Litter = false;
}
// Handles a websocket message to update the realtime scoring fields.
var handleScore = function(data) {
// Update autonomous period values.
var score = data.CurrentScore;
$("#autoRobotSet").text(score.AutoRobotSet ? "Yes" : "No");
$("#autoRobotSet").attr("data-value", score.AutoRobotSet);
$("#autoContainerSet").text(score.AutoContainerSet ? "Yes" : "No");
$("#autoContainerSet").attr("data-value", score.AutoContainerSet);
$("#autoToteSet").text(score.AutoToteSet ? "Yes" : "No");
$("#autoToteSet").attr("data-value", score.AutoToteSet);
$("#autoStackedToteSet").text(score.AutoStackedToteSet ? "Yes" : "No");
$("#autoStackedToteSet").attr("data-value", score.AutoStackedToteSet);
$("#autoDefense1Crossings").text(score.AutoDefensesCrossed[0]);
$("#autoDefense2Crossings").text(score.AutoDefensesCrossed[1]);
$("#autoDefense3Crossings").text(score.AutoDefensesCrossed[2]);
$("#autoDefense4Crossings").text(score.AutoDefensesCrossed[3]);
$("#autoDefense5Crossings").text(score.AutoDefensesCrossed[4]);
$("#autoDefensesReached").text(score.AutoDefensesReached);
$("#autoHighGoals").text(score.AutoHighGoals);
$("#autoLowGoals").text(score.AutoLowGoals);
// Update teleoperated period values.
$("#coopertitionSet").text(score.CoopertitionSet ? "Yes" : "No");
$("#coopertitionSet").attr("data-value", score.CoopertitionSet);
$("#coopertitionStack").text(score.CoopertitionStack ? "Yes" : "No");
$("#coopertitionStack").attr("data-value", score.CoopertitionStack);
// Don't stomp on pending changes to the stack score.
if (stackScoreChanged == false) {
if (score.Stacks == null) {
stacks = new Array();
for (i = 0; i < numStacks; i++) {
stacks.push(new Stack());
}
} else {
stacks = score.Stacks;
}
for (i = 0; i < numStacks; i++) {
updateStackView(i);
}
// Reset indications that the stack score is uncommitted.
$("#teleopMessage").css("opacity", 0);
$(".stack-grid").attr("data-changed", false);
}
$("#defense1Crossings").text(score.DefensesCrossed[0] + " (" + score.AutoDefensesCrossed[0] + " in auto)");
$("#defense2Crossings").text(score.DefensesCrossed[1] + " (" + score.AutoDefensesCrossed[1] + " in auto)");
$("#defense3Crossings").text(score.DefensesCrossed[2] + " (" + score.AutoDefensesCrossed[2] + " in auto)");
$("#defense4Crossings").text(score.DefensesCrossed[3] + " (" + score.AutoDefensesCrossed[3] + " in auto)");
$("#defense5Crossings").text(score.DefensesCrossed[4] + " (" + score.AutoDefensesCrossed[4] + " in auto)");
$("#highGoals").text(score.HighGoals);
$("#lowGoals").text(score.LowGoals);
$("#challenges").text(score.Challenges);
$("#scales").text(score.Scales);
// Update component visibility.
if (!data.AutoCommitted) {
@@ -83,64 +59,61 @@ var handleScore = function(data) {
// Handles a keyboard event and sends the appropriate websocket message.
var handleKeyPress = function(event) {
var key = String.fromCharCode(event.keyCode);
switch(key) {
switch (key) {
case "1":
case "2":
case "3":
case "4":
case "5":
websocket.send("defenseCrossed", key);
break;
case "!":
websocket.send("undoDefenseCrossed", "1");
break;
case "@":
websocket.send("undoDefenseCrossed", "2");
break;
case "#":
websocket.send("undoDefenseCrossed", "3");
break;
case "$":
websocket.send("undoDefenseCrossed", "4");
break;
case "%":
websocket.send("undoDefenseCrossed", "5");
break;
case "r":
websocket.send("robotSet");
websocket.send("autoDefenseReached");
break;
case "c":
if ($("#autoCommands").is(":visible")) {
websocket.send("containerSet");
} else {
stacks[selectedStack].Container = !stacks[selectedStack].Container;
if (!stacks[selectedStack].Container) {
stacks[selectedStack].Litter = false;
}
updateStackView(selectedStack);
invalidateStackScore();
}
case "R":
websocket.send("undoAutoDefenseReached");
break;
case "t":
websocket.send("toteSet");
case "h":
websocket.send("highGoal");
break;
case "s":
websocket.send("stackedToteSet");
break;
case "j":
if (selectedStack > 0) {
selectedStack--;
updateSelectedStack();
}
case "H":
websocket.send("undoHighGoal");
break;
case "l":
if (selectedStack < numStacks - 1) {
selectedStack++;
updateSelectedStack();
}
websocket.send("lowGoal");
break;
case "i":
if (stacks[selectedStack].Totes < 6) {
stacks[selectedStack].Totes++;
updateStackView(selectedStack);
invalidateStackScore();
}
case "L":
websocket.send("undoLowGoal");
break;
case "k":
if (stacks[selectedStack].Totes > 0) {
stacks[selectedStack].Totes--;
updateStackView(selectedStack);
invalidateStackScore();
}
case "c":
websocket.send("challenge");
break;
case "n":
if (stacks[selectedStack].Container) {
stacks[selectedStack].Litter = !stacks[selectedStack].Litter;
updateStackView(selectedStack);
invalidateStackScore();
}
case "C":
websocket.send("undoChallenge");
break;
case "s":
websocket.send("scale");
break;
case "S":
websocket.send("undoScale");
break;
case "\r":
websocket.send("commit", stacks);
stackScoreChanged = false;
websocket.send("commit");
break;
case "a":
websocket.send("uncommitAuto");
@@ -157,31 +130,8 @@ var handleMatchTime = function(data) {
}
};
// Updates the stack grid to highlight only the active stack.
var updateSelectedStack = function() {
for (i = 0; i < numStacks; i++) {
$("#stack" + i).attr("data-selected", i == selectedStack);
}
};
// Updates the appearance of the given stack in the grid to match the scoring data.
var updateStackView = function(stackIndex) {
stack = stacks[stackIndex];
$("#stack" + stackIndex + " .stack-tote-count").text(stack.Totes);
$("#stack" + stackIndex + " .stack-container").toggle(stack.Container);
$("#stack" + stackIndex + " .stack-litter").toggle(stack.Litter);
};
// Shows message indicating that the stack score has been changed but not yet sent to the server.
var invalidateStackScore = function() {
$("#teleopMessage").css("opacity", 1);
$(".stack-grid").attr("data-changed", true);
stackScoreChanged = true;
};
// Sends a websocket message to indicate that the score for this alliance is ready.
var commitMatchScore = function() {
websocket.send("commit", stacks);
websocket.send("commitMatch");
};
@@ -192,7 +142,5 @@ $(function() {
matchTime: function(event) { handleMatchTime(event.data); }
});
updateSelectedStack();
$(document).keypress(handleKeyPress);
});

View File

@@ -16,20 +16,24 @@
<h2>Autonomous Period</h2>
<p>Use the following keyboard shortcuts:</p>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">r</div>
<div class="col-lg-8 scoring-comment">Toggle robot set</div>
<div class="col-lg-3 col-lg-offset-1 scoring">1-5</div>
<div class="col-lg-8 scoring-comment">Defense crossed +</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">c</div>
<div class="col-lg-8 scoring-comment">Toggle container set</div>
<div class="col-lg-3 col-lg-offset-1 scoring">Shift+1-5</div>
<div class="col-lg-8 scoring-comment">Defense crossed -</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">t</div>
<div class="col-lg-8 scoring-comment">Toggle tote set</div>
<div class="col-lg-3 col-lg-offset-1 scoring">r/R</div>
<div class="col-lg-8 scoring-comment">Defenses reached +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">s</div>
<div class="col-lg-8 scoring-comment">Toggle stacked tote set</div>
<div class="col-lg-3 col-lg-offset-1 scoring">h/H</div>
<div class="col-lg-8 scoring-comment">High goals +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">l/L</div>
<div class="col-lg-8 scoring-comment">Low goals +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">Enter</div>
@@ -40,32 +44,28 @@
<h2>Teleoperated Period</h2>
<p>Use the following keyboard shortcuts:</p>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">t</div>
<div class="col-lg-8 scoring-comment">Toggle co-op tote set</div>
<div class="col-lg-3 col-lg-offset-1 scoring">1-5</div>
<div class="col-lg-8 scoring-comment">Defense crossed +</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">s</div>
<div class="col-lg-8 scoring-comment">Toggle co-op stacked tote set</div>
<div class="col-lg-3 col-lg-offset-1 scoring">Shift+1-5</div>
<div class="col-lg-8 scoring-comment">Defense crossed -</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">j/l</div>
<div class="col-lg-8 scoring-comment">Change selected stack</div>
<div class="col-lg-3 col-lg-offset-1 scoring">h/H</div>
<div class="col-lg-8 scoring-comment">High goals +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">i/k</div>
<div class="col-lg-8 scoring-comment">Add/remove totes for selected stack</div>
<div class="col-lg-3 col-lg-offset-1 scoring">l/L</div>
<div class="col-lg-8 scoring-comment">Low goals +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">c</div>
<div class="col-lg-8 scoring-comment">Toggle container for selected stack</div>
<div class="col-lg-3 col-lg-offset-1 scoring">c/C</div>
<div class="col-lg-8 scoring-comment">Challenges +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">n</div>
<div class="col-lg-8 scoring-comment">Toggle noodle for selected stack</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">Enter</div>
<div class="col-lg-8 scoring-comment">Commit tote stack change</div>
<div class="col-lg-3 col-lg-offset-1 scoring">s/S</div>
<div class="col-lg-8 scoring-comment">Scales +/-</div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-1 scoring">a</div>
@@ -77,54 +77,83 @@
<div id="autoScore" style="display: none;">
<h2>Autonomous Score</h2>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Robot Set</div>
<div class="col-lg-2 scoring-comment" id="autoRobotSet" data-value="false">No</div>
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Defense crossings</div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Container Set</div>
<div class="col-lg-2 scoring-comment" id="autoContainerSet" data-value="false">No</div>
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 1</div>
<div class="col-lg-2 scoring-comment" id="autoDefense1Crossings"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Tote Set</div>
<div class="col-lg-2 scoring-comment" id="autoToteSet" data-value="false">No</div>
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 2</div>
<div class="col-lg-2 scoring-comment" id="autoDefense2Crossings"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Stacked Tote Set</div>
<div class="col-lg-2 scoring-comment" id="autoStackedToteSet" data-value="false">No</div>
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 3</div>
<div class="col-lg-2 scoring-comment" id="autoDefense3Crossings"></div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 4</div>
<div class="col-lg-2 scoring-comment" id="autoDefense4Crossings"></div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 5</div>
<div class="col-lg-2 scoring-comment" id="autoDefense5Crossings"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Defenses reached</div>
<div class="col-lg-2 scoring-comment" id="autoDefensesReached"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">High goals</div>
<div class="col-lg-2 scoring-comment" id="autoHighGoals"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Low goals</div>
<div class="col-lg-2 scoring-comment" id="autoLowGoals"></div>
</div>
<h3 class="text-center scoring-message">Press Enter to commit autonomous score</h3>
</div>
<div id="teleopScore" style="display: none;">
<h2>Teleoperated Score</h2>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Co-op set</div>
<div class="col-lg-2 scoring-comment" id="coopertitionSet">No</div>
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Defense crossings</div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Co-op stack</div>
<div class="col-lg-2 scoring-comment" id="coopertitionStack">No</div>
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 1</div>
<div class="col-lg-4 scoring-comment" id="defense1Crossings"></div>
</div>
<div class="row">
<table class="stack-grid" data-changed="false">
<tr>
{{template "stack" "0"}}
{{template "stack" "1"}}
{{template "stack" "2"}}
{{template "stack" "3"}}
{{template "stack" "4"}}
</tr>
<tr>
{{template "stack" "5"}}
{{template "stack" "6"}}
{{template "stack" "7"}}
{{template "stack" "8"}}
{{template "stack" "9"}}
</tr>
</table>
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 2</div>
<div class="col-lg-4 scoring-comment" id="defense2Crossings"></div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 3</div>
<div class="col-lg-4 scoring-comment" id="defense3Crossings"></div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 4</div>
<div class="col-lg-4 scoring-comment" id="defense4Crossings"></div>
</div>
<div class="row">
<div class="col-lg-3 col-lg-offset-2 scoring-comment">Position 5</div>
<div class="col-lg-4 scoring-comment" id="defense5Crossings"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">High goals</div>
<div class="col-lg-2 scoring-comment" id="highGoals"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Low goals</div>
<div class="col-lg-2 scoring-comment" id="lowGoals"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Challenges</div>
<div class="col-lg-2 scoring-comment" id="challenges"></div>
</div>
<div class="row">
<div class="col-lg-4 col-lg-offset-1 scoring-comment">Scales</div>
<div class="col-lg-2 scoring-comment" id="scales"></div>
</div>
<h3 class="text-center scoring-message" id="teleopMessage" style="opacity: 0">
Press Enter to confirm stack changes
</h3>
</div>
</div>
</div>
@@ -141,10 +170,3 @@
<script src="/static/js/match_timing.js"></script>
<script src="/static/js/scoring_display.js"></script>
{{end}}
{{define "stack"}}
<td id="stack{{.}}" data-selected="false">
<div class="stack-tote-count">0</div>
<div class="stack-container" style="display: none;"></div>
<div class="stack-litter" style="display: none;"></div>
</td>
{{end}}