mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
WIP: Austin's changes at beta testing day.
This commit is contained in:
10
arena.go
10
arena.go
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
const (
|
||||
arenaLoopPeriodMs = 10
|
||||
dsPacketPeriodMs = 250
|
||||
dsPacketPeriodMs = 20
|
||||
matchEndScoreDwellSec = 3
|
||||
)
|
||||
|
||||
@@ -335,6 +335,14 @@ func (arena *Arena) StartMatch() error {
|
||||
db.SaveMatch(arena.currentMatch)
|
||||
}
|
||||
|
||||
// At the beginning of the match, save the missed packet count.
|
||||
for _, allianceStation := range arena.AllianceStations {
|
||||
if allianceStation.DsConn != nil {
|
||||
allianceStation.DsConn.DriverStationStatus.MissedOffset =
|
||||
allianceStation.DsConn.DriverStationStatus.MissedPacketCount
|
||||
}
|
||||
}
|
||||
|
||||
arena.MatchState = START_MATCH
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -32,19 +32,21 @@ type DriverStationStatus struct {
|
||||
PacketCount int
|
||||
MissedPacketCount int
|
||||
DsRobotTripTimeMs int
|
||||
MissedOffset int
|
||||
}
|
||||
|
||||
type DriverStationConnection struct {
|
||||
TeamId int
|
||||
AllianceStation string
|
||||
Auto bool
|
||||
Enabled bool
|
||||
EmergencyStop bool
|
||||
DriverStationStatus *DriverStationStatus
|
||||
LastPacketTime time.Time
|
||||
LastRobotLinkedTime time.Time
|
||||
conn net.Conn
|
||||
packetCount int
|
||||
TeamId int
|
||||
AllianceStation string
|
||||
Auto bool
|
||||
Enabled bool
|
||||
EmergencyStop bool
|
||||
DriverStationStatus *DriverStationStatus
|
||||
LastPacketTime time.Time
|
||||
LastRobotLinkedTime time.Time
|
||||
SecondsSinceLastRobotConnection float64
|
||||
conn net.Conn
|
||||
packetCount int
|
||||
}
|
||||
|
||||
// Opens a UDP connection for communicating to the driver station.
|
||||
@@ -105,6 +107,7 @@ func ListenForDsPackets(listener *net.UDPConn) {
|
||||
if dsStatus.RobotLinked {
|
||||
dsConn.LastRobotLinkedTime = time.Now()
|
||||
}
|
||||
dsConn.SecondsSinceLastRobotConnection = time.Since(dsConn.LastRobotLinkedTime).Seconds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,58 @@ func MatchPlayHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Shows the match play control interface.
|
||||
func MatchFTAHandler(w http.ResponseWriter, r *http.Request) {
|
||||
practiceMatches, err := buildMatchPlayList("practice")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
qualificationMatches, err := buildMatchPlayList("qualification")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
eliminationMatches, err := buildMatchPlayList("elimination")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
template := template.New("").Funcs(templateHelpers)
|
||||
_, err = template.ParseFiles("templates/match_fta.html", "templates/base.html")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
matchesByType := map[string]MatchPlayList{"practice": practiceMatches,
|
||||
"qualification": qualificationMatches, "elimination": eliminationMatches}
|
||||
if currentMatchType == "" {
|
||||
currentMatchType = "practice"
|
||||
}
|
||||
allowSubstitution := mainArena.currentMatch.Type != "qualification"
|
||||
matchResult, err := db.GetMatchResultForMatch(mainArena.currentMatch.Id)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
isReplay := matchResult != nil
|
||||
data := struct {
|
||||
*EventSettings
|
||||
MatchesByType map[string]MatchPlayList
|
||||
CurrentMatchType string
|
||||
Match *Match
|
||||
AllowSubstitution bool
|
||||
IsReplay bool
|
||||
}{eventSettings, matchesByType, currentMatchType, mainArena.currentMatch, allowSubstitution, isReplay}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func MatchPlayLoadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
matchId, _ := strconv.Atoi(vars["matchId"])
|
||||
|
||||
BIN
static/audio/GetToTheChoppa.wav
Normal file
BIN
static/audio/GetToTheChoppa.wav
Normal file
Binary file not shown.
@@ -41,18 +41,21 @@
|
||||
.modal-large {
|
||||
width: 60%;
|
||||
}
|
||||
.ds-status, .robot-status, .battery-status, .bypass-status {
|
||||
.ds-status, .robot-status, .battery-status, .bypass-status, .trip-time, .packet-loss {
|
||||
background-color: #aaa;
|
||||
color: #000;
|
||||
border: 1px solid #999;
|
||||
border-radius: 4px;
|
||||
padding: 0px;
|
||||
width: 40px;
|
||||
width: 45px;
|
||||
height: 27px;
|
||||
line-height: 25px;
|
||||
font-size: 14px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.trip-time, .packet-loss {
|
||||
width:90px;
|
||||
}
|
||||
.bypass-status {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
152
static/js/match_fta.js
Normal file
152
static/js/match_fta.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// 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 })
|
||||
};
|
||||
|
||||
var toggleBypass = function(station) {
|
||||
websocket.send("toggleBypass", station);
|
||||
};
|
||||
|
||||
var startMatch = function() {
|
||||
websocket.send("startMatch");
|
||||
};
|
||||
|
||||
var abortMatch = function() {
|
||||
websocket.send("abortMatch");
|
||||
};
|
||||
|
||||
var setAudienceDisplay = function() {
|
||||
websocket.send("setAudienceDisplay", $("input[name=audienceDisplay]:checked").val());
|
||||
};
|
||||
|
||||
var setAllianceStationDisplay = function() {
|
||||
websocket.send("setAllianceStationDisplay", $("input[name=allianceStationDisplay]: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) {
|
||||
if (stationStatus.DsConn) {
|
||||
var dsStatus = stationStatus.DsConn.DriverStationStatus;
|
||||
$("#status" + station + " .ds-status").attr("data-status-ok", dsStatus.DsLinked);
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", dsStatus.RobotLinked);
|
||||
if (stationStatus.DsConn.SecondsSinceLastRobotConnection > 1 && stationStatus.DsConn.SecondsSinceLastRobotConnection < 1000) {
|
||||
$("#status" + station + " .robot-status").text(
|
||||
stationStatus.DsConn.SecondsSinceLastRobotConnection.toFixed());
|
||||
} else {
|
||||
$("#status" + station + " .robot-status").text("");
|
||||
}
|
||||
$("#status" + station + " .battery-status").attr("data-status-ok",
|
||||
dsStatus.BatteryVoltage > 6 && dsStatus.RobotLinked);
|
||||
$("#status" + station + " .battery-status").text(dsStatus.BatteryVoltage.toFixed(1) + "V");
|
||||
$("#status" + station + " .trip-time").attr("data-status-ok", true);
|
||||
$("#status" + station + " .trip-time").text(dsStatus.DsRobotTripTimeMs.toFixed(1) + "ms");
|
||||
$("#status" + station + " .packet-loss").attr("data-status-ok", true);
|
||||
$("#status" + station + " .packet-loss").text((dsStatus.MissedPacketCount - dsStatus.MissedOffset).toFixed() + "p");
|
||||
} else {
|
||||
$("#status" + station + " .ds-status").attr("data-status-ok", "");
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", "");
|
||||
$("#status" + station + " .robot-status").text("");
|
||||
$("#status" + station + " .battery-status").attr("data-status-ok", "");
|
||||
$("#status" + station + " .battery-status").text("");
|
||||
$("#status" + station + " .trip-time").attr("data-status-ok", "");
|
||||
$("#status" + station + " .trip-time").text("");
|
||||
$("#status" + station + " .packet-loss").attr("data-status-ok", "");
|
||||
$("#status" + station + " .packet-loss").text("");
|
||||
}
|
||||
|
||||
if (stationStatus.EmergencyStop) {
|
||||
$("#status" + station + " .bypass-status").attr("data-status-ok", false);
|
||||
$("#status" + station + " .bypass-status").text("ES");
|
||||
} else if (stationStatus.Bypass) {
|
||||
$("#status" + station + " .bypass-status").attr("data-status-ok", false);
|
||||
$("#status" + station + " .bypass-status").text("B");
|
||||
} else {
|
||||
$("#status" + station + " .bypass-status").attr("data-status-ok", true);
|
||||
$("#status" + station + " .bypass-status").text("");
|
||||
}
|
||||
});
|
||||
|
||||
// Enable/disable the buttons based on the current match state.
|
||||
switch (matchStates[data.MatchState]) {
|
||||
case "PRE_MATCH":
|
||||
$("#startMatch").prop("disabled", !data.CanStartMatch);
|
||||
$("#abortMatch").prop("disabled", true);
|
||||
$("#commitResults").prop("disabled", true);
|
||||
$("#discardResults").prop("disabled", true);
|
||||
break;
|
||||
case "START_MATCH":
|
||||
case "AUTO_PERIOD":
|
||||
case "PAUSE_PERIOD":
|
||||
case "TELEOP_PERIOD":
|
||||
case "ENDGAME_PERIOD":
|
||||
$("#startMatch").prop("disabled", true);
|
||||
$("#abortMatch").prop("disabled", false);
|
||||
$("#commitResults").prop("disabled", true);
|
||||
$("#discardResults").prop("disabled", true);
|
||||
break;
|
||||
case "POST_MATCH":
|
||||
$("#startMatch").prop("disabled", true);
|
||||
$("#abortMatch").prop("disabled", true);
|
||||
$("#commitResults").prop("disabled", false);
|
||||
$("#discardResults").prop("disabled", false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var handleMatchTime = function(data) {
|
||||
translateMatchTime(data, function(matchState, matchStateText, countdownSec) {
|
||||
$("#matchState").text(matchStateText);
|
||||
$("#matchTime").text(countdownSec);
|
||||
});
|
||||
};
|
||||
|
||||
var handleSetAudienceDisplay = function(data) {
|
||||
$("input[name=audienceDisplay]:checked").prop("checked", false);
|
||||
$("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);
|
||||
};
|
||||
|
||||
var handleSetAllianceStationDisplay = function(data) {
|
||||
$("input[name=allianceStationDisplay]:checked").prop("checked", false);
|
||||
$("input[name=allianceStationDisplay][value=" + data + "]").prop("checked", true);
|
||||
};
|
||||
|
||||
$(function() {
|
||||
// Activate tooltips above the status headers.
|
||||
$("[data-toggle=tooltip]").tooltip({"placement": "top"});
|
||||
|
||||
// Set up the websocket back to the server.
|
||||
websocket = new CheesyWebsocket("/match_play/websocket", {
|
||||
status: function(event) { handleStatus(event.data); },
|
||||
matchTiming: function(event) { handleMatchTiming(event.data); },
|
||||
matchTime: function(event) { handleMatchTime(event.data); },
|
||||
setAudienceDisplay: function(event) { handleSetAudienceDisplay(event.data); },
|
||||
scoringStatus: function(event) { handleScoringStatus(event.data); },
|
||||
setAllianceStationDisplay: function(event) { handleSetAllianceStationDisplay(event.data); }
|
||||
});
|
||||
});
|
||||
@@ -123,7 +123,7 @@
|
||||
<audio id="match-end" src="/static/audio/match_end.wav" type="audio/wav" preload="auto" />
|
||||
<audio id="match-abort" src="/static/audio/match_abort.mp3" type="audio/mp3" preload="auto" />
|
||||
<audio id="match-resume" src="/static/audio/match_resume.wav" type="audio/wav" preload="auto" />
|
||||
<audio id="match-endgame" src="/static/audio/match_endgame.wav" type="audio/wav" preload="auto" />
|
||||
<audio id="match-endgame" src="/static/audio/GetToTheChoppa.wav" type="audio/wav" preload="auto" />
|
||||
<script src="/static/js/lib/jquery.min.js"></script>
|
||||
<script src="/static/js/lib/jquery.json-2.4.min.js"></script>
|
||||
<script src="/static/js/lib/jquery.websocket-0.0.1.js"></script>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Run</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/match_play">Match Play</a></li>
|
||||
<li><a href="/match_fta">Match FTA</a></li>
|
||||
<li><a href="/match_review">Match Review</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
62
templates/match_fta.html
Normal file
62
templates/match_fta.html
Normal file
@@ -0,0 +1,62 @@
|
||||
{{define "title"}}Match FTA{{end}}
|
||||
{{define "body"}}
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="row text-center">
|
||||
<div id="matchState" class="col-lg-2 col-lg-offset-4 well well-sm"> </div>
|
||||
<div id="matchTime" class="col-lg-2 well well-sm"> </div>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<div class="col-lg-6 well well-darkblue">
|
||||
<div class="row form-group">
|
||||
<div class="col-lg-4">Blue Teams</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Driver Station">DS</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Robot Status and time since last seen">R</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Battery">Bat</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Trip Time">Trip Time</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Packet Loss">Lost Pack</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Bypass/Disable">Byp</div>
|
||||
</div>
|
||||
{{template "matchPlayTeam" dict "team" .Match.Blue1 "color" "B" "position" 1 "data" .}}
|
||||
{{template "matchPlayTeam" dict "team" .Match.Blue2 "color" "B" "position" 2 "data" .}}
|
||||
{{template "matchPlayTeam" dict "team" .Match.Blue3 "color" "B" "position" 3 "data" .}}
|
||||
</div>
|
||||
<div class="col-lg-6 well well-darkred">
|
||||
<div class="row form-group">
|
||||
<div class="col-lg-4">Red Teams</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Driver Station">DS</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Robot Status and time since last seen">R</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Battery">Bat</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Trip Time">Trip Time</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Packet Loss">Lost Pack</div>
|
||||
<div class="col-lg-1" data-toggle="tooltip" title="Bypass/Disable">Byp</div>
|
||||
</div>
|
||||
{{template "matchPlayTeam" dict "team" .Match.Red3 "color" "R" "position" 3 "data" .}}
|
||||
{{template "matchPlayTeam" dict "team" .Match.Red2 "color" "R" "position" 2 "data" .}}
|
||||
{{template "matchPlayTeam" dict "team" .Match.Red1 "color" "R" "position" 1 "data" .}}
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{define "script"}}
|
||||
<script src="/static/js/match_timing.js"></script>
|
||||
<script src="/static/js/match_fta.js"></script>
|
||||
{{end}}
|
||||
{{define "matchPlayTeam"}}
|
||||
<div class="row form-group" id="status{{.color}}{{.position}}">
|
||||
<div class="col-lg-1">{{.position}} </div>
|
||||
<div class="col-lg-3">
|
||||
<input type="text" disabled class="form-control input-sm" value="{{if ne 0 .team}}{{.team}}{{end}}"
|
||||
{{if not .data.AllowSubstitution}}disabled{{end}}>
|
||||
</div>
|
||||
<div class="col-lg-1 col-no-padding"><div class="ds-status"></div></div>
|
||||
<div class="col-lg-1 col-no-padding"><div class="robot-status"></div></div>
|
||||
<div class="col-lg-1 col-no-padding"><div class="battery-status"></div></div>
|
||||
<div class="col-lg-2 col-no-padding"><div class="trip-time" ></div></div>
|
||||
<div class="col-lg-2 col-no-padding"><div class="packet-loss" ></div></div>
|
||||
<div class="col-lg-1 col-no-padding"><div class="bypass-status"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
2
web.go
2
web.go
@@ -132,6 +132,8 @@ func newHandler() http.Handler {
|
||||
router.HandleFunc("/match_review", MatchReviewHandler).Methods("GET")
|
||||
router.HandleFunc("/match_review/{matchId}/edit", MatchReviewEditGetHandler).Methods("GET")
|
||||
router.HandleFunc("/match_review/{matchId}/edit", MatchReviewEditPostHandler).Methods("POST")
|
||||
router.HandleFunc("/match_fta", MatchFTAHandler).Methods("GET")
|
||||
router.HandleFunc("/match_fta/websocket", MatchPlayWebsocketHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/rankings", RankingsCsvReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/pdf/rankings", RankingsPdfReportHandler).Methods("GET")
|
||||
router.HandleFunc("/reports/csv/schedule/{type}", ScheduleCsvReportHandler).Methods("GET")
|
||||
|
||||
Reference in New Issue
Block a user