Added tracking of score commit status in match play screen.

This commit is contained in:
Patrick Fairbank
2014-08-03 17:59:59 -07:00
parent 37a7c199a4
commit 1343e851e1
7 changed files with 102 additions and 34 deletions

View File

@@ -69,6 +69,7 @@ type Arena struct {
matchTimeNotifier *Notifier
robotStatusNotifier *Notifier
matchLoadTeamsNotifier *Notifier
scoringStatusNotifier *Notifier
realtimeScoreNotifier *Notifier
scorePostedNotifier *Notifier
audienceDisplayNotifier *Notifier
@@ -101,6 +102,7 @@ func (arena *Arena) Setup() {
arena.matchTimeNotifier = NewNotifier()
arena.robotStatusNotifier = NewNotifier()
arena.matchLoadTeamsNotifier = NewNotifier()
arena.scoringStatusNotifier = NewNotifier()
arena.realtimeScoreNotifier = NewNotifier()
arena.scorePostedNotifier = NewNotifier()
arena.audienceDisplayNotifier = NewNotifier()

View File

@@ -581,6 +581,7 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
// Commit last cycle.
(*score).CurrentScore.Cycles = append((*score).CurrentScore.Cycles, (*score).CurrentCycle)
}
mainArena.scoringStatusNotifier.Notify(nil)
case "undo":
if !(*score).AutoCommitted && len((*score).undoAutoScores) > 0 {
(*score).CurrentScore = (*score).undoAutoScores[len((*score).undoAutoScores)-1]
@@ -739,6 +740,7 @@ func RefereeDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
case "commitMatch":
mainArena.redRealtimeScore.FoulsCommitted = true
mainArena.blueRealtimeScore.FoulsCommitted = true
mainArena.scoringStatusNotifier.Notify(nil)
default:
websocket.WriteError(fmt.Sprintf("Invalid message type '%s'.", messageType))
continue

View File

@@ -159,8 +159,11 @@ func MatchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
defer close(robotStatusListener)
audienceDisplayListener := mainArena.audienceDisplayNotifier.Listen()
defer close(audienceDisplayListener)
scoringStatusListener := mainArena.scoringStatusNotifier.Listen()
defer close(scoringStatusListener)
// Send the various notifications immediately upon connection.
var data interface{}
err = websocket.Write("status", mainArena)
if err != nil {
log.Printf("Websocket error: %s", err)
@@ -171,7 +174,7 @@ func MatchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Websocket error: %s", err)
return
}
data := MatchTimeMessage{mainArena.MatchState, int(mainArena.lastMatchTimeSec)}
data = MatchTimeMessage{mainArena.MatchState, int(mainArena.lastMatchTimeSec)}
err = websocket.Write("matchTime", data)
if err != nil {
log.Printf("Websocket error: %s", err)
@@ -182,6 +185,17 @@ func MatchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Websocket error: %s", err)
return
}
data = struct {
RefereeScoreReady bool
RedScoreReady bool
BlueScoreReady bool
}{mainArena.redRealtimeScore.FoulsCommitted && mainArena.blueRealtimeScore.FoulsCommitted,
mainArena.redRealtimeScore.TeleopCommitted, mainArena.blueRealtimeScore.TeleopCommitted}
err = websocket.Write("scoringStatus", 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() {
@@ -207,6 +221,17 @@ func MatchPlayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
}
messageType = "setAudienceDisplay"
message = mainArena.audienceDisplayScreen
case _, ok := <-scoringStatusListener:
if !ok {
return
}
messageType = "scoringStatus"
message = struct {
RefereeScoreReady bool
RedScoreReady bool
BlueScoreReady bool
}{mainArena.redRealtimeScore.FoulsCommitted && mainArena.blueRealtimeScore.FoulsCommitted,
mainArena.redRealtimeScore.TeleopCommitted, mainArena.blueRealtimeScore.TeleopCommitted}
}
err = websocket.Write(messageType, message)
if err != nil {

View File

@@ -173,6 +173,7 @@ func TestMatchPlayWebsocketCommands(t *testing.T) {
readWebsocketType(t, ws, "matchTiming")
readWebsocketType(t, ws, "matchTime")
readWebsocketType(t, ws, "setAudienceDisplay")
readWebsocketType(t, ws, "scoringStatus")
// Test that a server-side error is communicated to the client.
ws.Write("nonexistenttype", nil)
@@ -257,6 +258,7 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) {
readWebsocketType(t, ws, "matchTiming")
readWebsocketType(t, ws, "matchTime")
readWebsocketType(t, ws, "setAudienceDisplay")
readWebsocketType(t, ws, "scoringStatus")
mainArena.AllianceStations["R1"].Bypass = true
mainArena.AllianceStations["R2"].Bypass = true
@@ -273,6 +275,8 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) {
assert.Equal(t, 0, matchTime.MatchTimeSec)
_, ok := messages["setAudienceDisplay"]
assert.True(t, ok)
mainArena.scoringStatusNotifier.Notify(nil)
readWebsocketType(t, ws, "scoringStatus")
// Should get a tick notification when an integer second threshold is crossed.
mainArena.matchStartTime = time.Now().Add(-time.Second + 10*time.Millisecond) // Not crossed yet

View File

@@ -68,6 +68,12 @@
.btn-match-play {
width: 165px;
}
.label-scoring {
background-color: #e66;
}
.label-scoring[data-ready=true] {
background-color: #0c6;
}
.nowrap {
white-space: nowrap;
}

View File

@@ -4,6 +4,7 @@
// Client-side logic for the match play page.
var websocket;
var scoreIsReady;
var substituteTeam = function(team, position) {
websocket.send("substituteTeam", { team: parseInt(team), position: position })
@@ -33,6 +34,17 @@ var setAudienceDisplay = function() {
websocket.send("setAudienceDisplay", $("input[name=audienceDisplay]:checked").val());
};
var confirmCommit = function(isReplay) {
if (isReplay || !scoreIsReady) {
// Show the appropriate message(s) in the confirmation dialog.
$("#confirmCommitReplay").css("display", isReplay ? "block" : "none");
$("#confirmCommitNotReady").css("display", scoreIsReady ? "none" : "block");
$("#confirmCommitResults").modal("show");
} else {
commitResults();
}
};
var handleStatus = function(data) {
// Update the team status view.
$.each(data.AllianceStations, function(station, stationStatus) {
@@ -99,6 +111,13 @@ var handleSetAudienceDisplay = function(data) {
$("input[name=audienceDisplay][value=" + data + "]").prop("checked", true);
};
var handleScoringStatus = function(data) {
scoreIsReady = data.RefereeScoreReady && data.RedScoreReady && data.BlueScoreReady;
$("#refereeScoreStatus").attr("data-ready", data.RefereeScoreReady);
$("#redScoreStatus").attr("data-ready", data.RedScoreReady);
$("#blueScoreStatus").attr("data-ready", data.BlueScoreReady);
};
$(function() {
// Activate tooltips above the status headers.
$("[data-toggle=tooltip]").tooltip({"placement": "top"});
@@ -108,6 +127,7 @@ $(function() {
status: function(event) { handleStatus(event.data); },
matchTiming: function(event) { handleMatchTiming(event.data); },
matchTime: function(event) { handleMatchTime(event.data); },
setAudienceDisplay: function(event) { handleSetAudienceDisplay(event.data); }
setAudienceDisplay: function(event) { handleSetAudienceDisplay(event.data); },
scoringStatus: function(event) { handleScoringStatus(event.data); }
});
});

View File

@@ -87,8 +87,7 @@
Abort Match
</button>
<button type="button" id="commitResults" class="btn btn-info btn-lg btn-match-play"
onclick="{{if .IsReplay}}$('#confirmCommitResults').modal('show');{{else}}commitResults();{{end}}"
disabled>
onclick="confirmCommit({{.IsReplay}});" disabled>
Commit Results
</button>
<button type="button" id="discardResults" class="btn btn-danger btn-lg btn-match-play"
@@ -98,35 +97,43 @@
</div>
<br />
<div class="row">
<div class="col-lg-3 well">
Audience Display
<div class="form-group">
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="blank" onclick="setAudienceDisplay();">Blank
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="intro" onclick="setAudienceDisplay();">Intro
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="match" onclick="setAudienceDisplay();">Match
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="score" onclick="setAudienceDisplay();">Final Score
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="logo" onclick="setAudienceDisplay();">Logo
</label>
<div class="col-lg-6 well">
<div class="col-lg-6">
Audience Display
<div class="form-group">
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="blank" onclick="setAudienceDisplay();">Blank
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="intro" onclick="setAudienceDisplay();">Intro
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="match" onclick="setAudienceDisplay();">Match
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="score" onclick="setAudienceDisplay();">Final Score
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="audienceDisplay" value="logo" onclick="setAudienceDisplay();">Logo
</label>
</div>
</div>
</div>
<div class="col-lg-6">
<p>Scoring Status</p>
<p><span class="label label-scoring" id="refereeScoreStatus">Referee</span><br />
<span class="label label-scoring" id="redScoreStatus">Red Scoring</span><br />
<span class="label label-scoring" id="blueScoreStatus">Blue Scoring</span></p>
</div>
</div>
</div>
</div>
@@ -139,13 +146,15 @@
<h4 class="modal-title">Confirm</h4>
</div>
<div class="modal-body">
<p>This is a replay of Match {{.Match.DisplayName}}. Are you sure you want to overwrite the previous
results?</p>
<p id="confirmCommitReplay">This is a replay of Match {{.Match.DisplayName}}. Are you sure you want to
overwrite the previous results?</p>
<p id="confirmCommitNotReady">Not all scoring sources are ready yet. Are you sure you want to
commit the results?</p>
</div>
<div class="modal-footer">
<form class="form-horizontal" action="/setup/teams/clear" method="POST">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="discardResults();">Overwrite Results</button>
<button type="button" class="btn btn-primary" onclick="commitResults();">Commit Results</button>
</form>
</div>
</div>